Apollo-server-express File Upload on Local Storage and on AWS S3
Tags: apolloserverexpress, aws, express, fileupload
Published: 2020-09-29 09:52:29

**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](https://dev-to-uploads.s3.amazonaws.com/i/6h7wr69em9lmeax1dhoz.png)

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

# Have a great day!