This article covers the detailed method to use GraphQL to upload files and process them to store them in MongoDB in BinData format. Framework used is NestJS and technologies used are GraphQL, MongoDB and mongoose. Working with files have always been a tricky task and when it comes to uploading files using graphql, there is very less help available online. Graphql is relatively new and strong data query framework. In this article I have described the details of uploading files using graphql mongodb. Link to complete working code base is available at the end of the article. You will require following package and chrome extension to make this work.
- graphql-upload v12.0.0
- Altair GraphQL Client (chrome extension)
I tried working with version 14 of graphql-upload but it seems they are not stable or there are some issue in imports. Also some functions are either missing or they have been changed, however things work just fine with v12.0.0 .
Setting Up a Project to Upload files with GraphQL:
**Disclaimer** : skip this section if you are not using NestJS
I am working with Nest JS, but you can create your own NodeJS project and things should work just fine in that too. If you have been working with NestJS than you must familiar with @nestcli than it will not be an issue. I’ll right the commands that you need to run.
nest new project_name
____________________________________________________________________ nest g resource ./module/module_name
First command will create a simple nest project. Second command will create a simple boiler plate for a Nest module inside module directory. When you will run the second command it will ask you to choose layer like this:
You need to choose GraphQL(code-first). This will create a simple code base with all configurations intact. However it will not generate *.controller.ts file because for GraphQL API. You will have to create that file manually and write respective APIs.
Installing Dependencies for file upload GrpahQL:
Run following commands inside the directory of NestJS/NodeJS project.
- For nestjs:
npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express graphql-upload@12.0.0
- For nodejs:
npm install graphql graphql-upload@12.0.0 --save
Now you have to add a line i.e. app.use(graphqlUploadExpress()); in your bootstrap function to tell the NestJS application that incoming request should be executed as GraphQL request. Your main.ts file will look like this:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { graphqlUploadExpress } from 'graphql-upload';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
//app.use(graphqlUploadExpress());
await app.listen(3000);
}
bootstrap();
In Node.JS you can add this line in your app.js inside exported function.
I have commented the line here, the reason is mentioned below. We will uncomment it late at right time.
**Important Disclaimer:** Adding the mentioned line will make your application GraphQL centric and will not execute direct http requests. I have not found any solution yet, but if you want to run http requests you need to comment that line out.
If you find any solution to that, kindly help the community by commenting the solution.
If have done the previous step, congratulations you are now ready to work with GraphQL and Node/Nest JS.
**Disclaimer:** It does not seem right to talk about detailed configurations and imports to run the project. I assume that if you are working with Node/Nest you are already familiar with it. However, if you need help with Nest, I have attached github repo of my nest project at the end. You can clone it and play with it to get better understanding.
Now I will fast forward to our main problem, which is,
How to store files in MongoDB and How to upload file using GraphQL API?
Let me share the code first:
import { ArgsType, Field, Float, Int, ObjectType } from '@nestjs/graphql';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document} from 'mongoose';
import { Blob } from 'buffer';
import { Stream } from 'stream';
import { GraphQLUpload, FileUpload } from 'graphql-upload';
export type UserDocument = User & Document;
@ArgsType()@Schema({})
@ObjectType()
export class User {
@Prop()
@Field(() => String, {nullable: true})
email: string;
@Prop()
@Field( () => Int, {nullable: true})
age: number;
@Prop({type: Buffer})
@Field(() => GraphQLUpload,{nullable: true})
image: FileUpload
}
export const UserSchema = SchemaFactory.createForClass(User);
Here you have to take a closer look on the datatype of image for MongoDB and GraphQL. For M_ongoDB it is_ Buffer and for GraphQL it is GraphQLUpload.
In my repository, I have followed a certain coding practice of NestJS which is,
Controller / Resolver -> Service -> Repository
However, all the business logic lies in repository, so I will directly show you the repository’s code.
async create(email, age, file): Promise<User> {
console.log(file);
const u = await this.userModel.create({
email,
age,
image: Buffer.from(file).toString('base64')
});
u.save();
return u;
}
When you input a file and set its dataType to Express.Multer.File in controller.ts, you get the file in following format. The console.log will console output like this:
The important thing is that buffer variable. Now what we are gonna do is send this file.buffer in our business logic (repository.ts file) where we will convert it in BinData or base64 to save it in MongoDB as shown in recent mentioned code section. Now you can hit the API with postman by sending image in body in form-data like this,
and your data will be saved in MongoDB like this:
Till now you should be able to upload a file in BinData by sending Http request from postman to your rest API.
Now only thing left is to right a mutation and than send a file from GraphQL and upload it to MongoDB.
Writing Mutation and Sending File as a input using GraphQL Client:
NOTE: Add app.use(graphqlUploadExpress()); this line in main.ts in NestJS. For NodeJS, it will be added in app.js file. Otherwise, graphql will not work.
Now in Mutation, you only have to call the function that has business logic in it. But there is one issue, when you send file from Altair Graphql Client the file is received in readStream format_._ We will have to convert it into buffer first and than send it as a parameter to our create function.
For NestJS, the mutation will look like this.
@Mutation(() => User, {name: 'CreateUser'})
createUser(@Args('email') email: string,
@Args('age') age: number,
@Args({ name: 'file', type: () => GraphQLUpload }) file: FileUpload) {
console.log(email, age);
console.log(file)
let readStream = file.createReadStream()
let data = ''
// set stream encoding to binary so chunks are kept in binaryreadStream.setEncoding('binary')
readStream.once('error', err => {
return console.log(err)
})
readStream.on('data', chunk => (data += chunk))
readStream.on('end', () => {
// If you need the binary data as a Buffer
// create one from data chunks
console.log(Buffer.from(data, 'binary'))
this.userService.createUser(email, age, Buffer.from(data, 'binary'))
})
}
For NodeJS, after converting the file in correct format (buffer), you have to export the function or write the function in an exported class. Than import that class/function in your Mutation file and use it.
Now the final step is to send file from the GraphQL Client. For this, first you have to download Altair GraphQL Client. link is mentioned.
[https://chrome.google.com/webstore/detail/altair-graphql-client/flnheeellpciglgpaodhkhmapeljopja?hl=en-US](https://chrome.google.com/webstore/detail/altair-graphql-client/flnheeellpciglgpaodhkhmapeljopja?hl=en-US)
Once its downloaded, open it and,
- write your mutation query in the code editor.
- Fill in all the variable except the one that will contain file.
- At bottom-left corner, you will see a Green colored active link named “Add Files”
- clicking it will open file explorer from where you can upload file.
- Now click the send request button and it should work just fine.
The working code directory is available here:
**NOTE:** In my code, If you want to test by sending http request from postman, please comment out the line7 in **main.ts** file.
GitHub - haadbaig/NestJS-concepts at using-files-with-gql
I have tried my best to explain the code in detail, I hope it will help the community and every developer who will come here to find answers to his/her queries. If something is not clear you can always contact me on my linked-in profile. If this article helps you, do not forget to follow my account, give this article multiple claps and share it with your fellow developers.
Read about how to make compound keys in NestJS with mongoose MongoDB:
Top comments (0)