You can use graphql-upload with Apollo Server and NestJS's @nestjs/platform-fastify. The idea is to use the graphql-upload as a middleware to handle file uploads from the client side.
First, you need to update the NestJS's main.ts file to integrate the graphql-upload with Apollo and Fastify.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { graphqlUploadFastify } from 'graphql-upload';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
// Apply the graphql upload middleware
app.register(graphqlUploadFastify, {
maxFileSize: 10000000, // 10 MB
maxFiles: 5,
});
await app.listen(3000);
}
bootstrap();
Then, you need to define the Upload scalar in your GraphQL Module and update your resolvers and schema to use the Upload scalar. Here is an example of how you could do that.
In your app.module.ts:
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
@Module({
imports: [
GraphQLModule.forRoot({
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
uploads: false, // Disable the built-in upload functionality of Apollo Server
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
Next, define your schema (e.g., upload.schema.ts):
import { ObjectType, Field, InputType, Directive } from '@nestjs/graphql';
@ObjectType()
export class File {
@Field()
filename: string;
@Field()
mimetype: string;
@Field()
encoding: string;
}
@InputType()
@Directive('@extends')
@Directive('@key(fields: "id")')
export class Upload {
@Field()
id: string;
}
And the corresponding resolvers (e.g., upload.resolver.ts):
import { Resolver, Mutation, Args, Query } from '@nestjs/graphql';
import { FileUpload, GraphQLUpload } from 'graphql-upload';
import { createWriteStream } from 'fs';
import { File } from './upload.schema';
@Resolver()
export class UploadResolver {
@Query(() => [File])
async uploads(): Promise<File[]> {
// Implement the logic to return the files
return [];
}
@Mutation(() => Boolean)
async uploadObjects(@Args({ name: 'files', type: () => [GraphQLUpload] }) files: FileUpload[]): Promise<boolean> {
await Promise.all(
files.map(async file => {
const { createReadStream, filename } = await file;
// Create a stream to your file destination.
// Here is an example of saving the file locally
return new Promise((resolve, reject) =>
createReadStream()
.pipe(createWriteStream(`./uploads/${filename}`))
.on('finish', () => resolve(true))
.on('error', (error) => reject(error)),
);
}),
);
return true;
}
}
In this implementation, you are using Fastify as a HTTP server instead of the default one (Express) and graphql-upload middleware to handle the file upload. The middleware parses the incoming requests with file upload and passes the streams into your resolvers. Note that in the @Mutation decorator, we are using GraphQLUpload provided by graphql-upload, which is the required format for file uploads.
Please ensure that all the necessary packages are installed. If not, install them using npm or yarn:
npm install @nestjs/graphql graphql-tools graphql apollo-server-fastify fastify multipart graphql-upload