DEV Community

loading...
Cover image for Apollo-server-express File Upload on Local Storage and on AWS S3

Apollo-server-express File Upload on Local Storage and on AWS S3

kingmaker9841 profile image kingmaker9841 ・3 min read

Summary : We will be uploading files from one storage to another. I will use express, apollo-server-express and aws-sdk.

File Upload is fundamental to web-apps. And there is not much information regarding details of file uploads using apollo-server-express for beginner and intermediate. So, anyone reading this post, if you do not understand some code, please feel free to comment and I will update you asap. Now, let us write some code...

First initialize package.json in your working directory using:

npm init -y

Now let us install some module.

npm install express apollo-server-express dotenv nodemon uuid aws-sdk

If you are not familiar with some of the modules, you can google the module name and learn from its documentation.

Open package.json and make some changes as:

"main": "app.js",
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js"
  }

As you can see, app.js is my server file but you can rename it to anything you want.
If your main: property is app.js then go ahead and create a file named app.js in your working directory.Also create a file .env in order to store some environment variables. Now inside app.js, paste the following code:

const express = require('express');
const app = express();
const {ApolloServer} = require('apollo-server-express');
require('dotenv').config();

let server = new ApolloServer({
    typeDefs, resolvers
});
server.applyMiddleware({app});

let PORT = process.env.PORT || 5000;
app.listen(PORT, ()=>{
    console.log(`Server started on ${PORT}...`);
})

This will get our server up and running on port 5000.
Now configure your AWS S3 bucket to get accesskeyid, secretaccesskey, region and bucket-name. Then create a directory named config and inside it create a file s3.js and copy the code below:
s3.js

const aws = require('aws-sdk');

let s3 = new aws.S3({
    credentials: {
        accessKeyId: process.env.ACCESS_KEY_ID,
        secretAccessKey: process.env.SECRET_ACCESS_KEY
    },
    region: process.env.REGION,
    params : {
        ACL : 'public-read',
        Bucket : process.env.AWS_BUCKET
    }
});

module.exports = s3

Here, all the environment variables are kept inside .env file.

Now create a directory named schema and create two files namely typedefs.js and resolvers.js and paste the following code:
typedefs.js

const {gql} = require('apollo-server-express')

const typedefs = gql`
    type Query {
        uploadedFiles : [File]
    }
    type Mutation {
        singleUploadLocal (file: Upload!) : File
        multipleUploadLocal (files: [Upload]!) : [File]
        singleUploadS3 (file : Upload!) : File
        multipleUploadS3 (files : [Upload]!) : [File]
    }
    type File {
        success : String!
        message : String!
        mimetype : String
        encoding : String
        filename : String
        location : String
    }
`;
module.exports = typedefs;

resolvers.js

const fs = require('fs');
const {v4: uuid} = require('uuid');
const s3 = require('../config/s3');

const processUpload = async (file)=>{
    const {createReadStream, mimetype, encoding, filename} = await file;
    let path = "uploads/" + uuid() + filename;
    let stream = createReadStream();
    return new Promise((resolve,reject)=>{
        stream
        .pipe(fs.createWriteStream(path))
        .on("finish", ()=>{

            resolve({
                success: true,
                message: "Successfully Uploaded",
                mimetype, filename, encoding, location: path
            })
        })
        .on("error", (err)=>{
            console.log("Error Event Emitted")
            reject({
                success: false,
                message: "Failed"
            })
        })
    })
}

let processUploadS3 = async (file)=>{
    const {createReadStream, mimetype, encoding, filename} = await file;
    let stream = createReadStream();
    const {Location} = await s3.upload({
        Body: stream,
        Key: `${uuid()}${filename}`,
        ContentType: mimetype
    }).promise();
    return new Promise((resolve,reject)=>{
        if (Location){
            resolve({
                success: true, message: "Uploaded", mimetype,filename,
                location: Location, encoding
            })
        }else {
            reject({
                success: false, message: "Failed"
            })
        }
    })
}
const resolvers = {
    Mutation: {
        singleUploadLocal : async (_, args)=>{
            return processUpload(args.file);
        },
        multipleUploadLocal : async (_, args) =>{
            let obj =  (await Promise.all(args.files)).map(processUpload);
            console.log(obj);
            return obj;
        },
        singleUploadS3 : async (_, args)=>{
            return processUploadS3(args.file);
        },
        multipleUploadS3 : async (_, args)=>{
            let obj = (await Promise.all(args.files)).map(processUploadS3);
            return obj;
        }
    }
}

module.exports = resolvers;

Now to test it out, I am using a chrome extension called Altair that allows me to easily upload file on my graphql query which looks like this:

Alt Text

You can find the code above in my github repo:
https://github.com/kingmaker9841/apollo-multiple-upload

Have a great day!

Discussion

pic
Editor guide
Collapse
eriickson profile image
Erickson Manuel Holguín

Great, it works almost perfectly for me, I have been researching the subject for a long time and I had only achieved this asaña with the "apollo-server" package, it had a large part advanced, but with your example I was able to complete the puzzle, let's say I mixed part of your code with mine both in app.js, resolvers.js and in typedefs.js aah and I also had to install the "graphql-upload" module, I frame in red boxes the things that I changed and that I had to take in bill
dev-to-uploads.s3.amazonaws.com/i/...
dev-to-uploads.s3.amazonaws.com/i/...
dev-to-uploads.s3.amazonaws.com/i/...
dev-to-uploads.s3.amazonaws.com/i/...