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:
You can find the code above in my github repo:
https://github.com/kingmaker9841/apollo-multiple-upload
Discussion
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/...