Search code examples
javascriptnode.jsgraphqlapollonestjs

Nestjs Apollo graphql upload scalar


I'm using nestjs graphql framework and I want to use apollo scalar upload

I have been able to use the scalar in another project that did not include nestjs.

schema.graphql App.module.ts register graphql

    GraphQLModule.forRoot({
      typePaths: ['./**/*.graphql'],
      resolvers: { Upload: GraphQLUpload },
      installSubscriptionHandlers: true,
      context: ({ req }) => ({ req }),
      playground: true,
      definitions: {
        path: join(process.cwd(), './src/graphql.classes.ts'),
        outputAs: 'class',
      },
      uploads: {
        maxFileSize: 10000000, // 10 MB
        maxFiles: 5
      }
    }),

pets.resolver.ts mutation createPet

@Mutation('uploadFile')
    async uploadFile(@Args('fileUploadInput') fileUploadInput: FileUploadInput) {
        console.log("TCL: PetsResolver -> uploadFile -> file", fileUploadInput);
        return {
            id: '123454',
            path: 'www.wtf.com',
            filename: fileUploadInput.file.filename,
            mimetype: fileUploadInput.file.mimetype
        }
    }

pets.type.graphql

type Mutation {
        uploadFile(fileUploadInput: FileUploadInput!): File!
}
input FileUploadInput{
    file: Upload!
}

type File {
        id: String!
        path: String!
        filename: String!
        mimetype: String!
}

I expect that scalar works with nestjs but my actual result is

{"errors":[{"message":"Promise resolver undefined is not a function","locations":[{"line":2,"column":3}],"path":["createPet"],"extensions":{"code":"INTERNAL_SERVER_ERROR","exception":{"stacktrace":["TypeError: Promise resolver undefined is not a function","    at new Promise (<anonymous>)","    at TransformOperationExecutor.transform (E:\\projectos\\Gitlab\\latineo\\latineo-api\\src\\TransformOperationExecutor.ts:119:32)","    at E:\\projectos\\Gitlab\\latineo\\latineo-api\\src\\TransformOperationExecutor.ts:62:40","    at Array.forEach (<anonymous>)","    at TransformOperationExecutor.transform (E:\\projectos\\Gitlab\\latineo\\latineo-api\\src\\TransformOperationExecutor.ts:41:30)","    at _loop_1 (E:\\projectos\\Gitlab\\latineo\\latineo-api\\src\\TransformOperationExecutor.ts:226:43)","    at TransformOperationExecutor.transform (E:\\projectos\\Gitlab\\latineo\\latineo-api\\node_modules\\class-transformer\\TransformOperationExecutor.js:240:17)","    at ClassTransformer.plainToClass (E:\\projectos\\Gitlab\\latineo\\latineo-api\\src\\ClassTransformer.ts:43:25)","    at Object.plainToClass (E:\\projectos\\Gitlab\\latineo\\latineo-api\\src\\index.ts:37:29)","    at ValidationPipe.transform (E:\\projectos\\Gitlab\\latineo\\latineo-api\\node_modules\\@nestjs\\common\\pipes\\validation.pipe.js:50:41)","    at transforms.reduce (E:\\projectos\\Gitlab\\latineo\\latineo-api\\node_modules\\@nestjs\\core\\pipes\\pipes-consumer.js:15:28)","    at process._tickCallback (internal/process/next_tick.js:68:7)"]}}}],"data":null}

Solution

  • I solved it by using graphql-upload library. First i created a class for my scalar using GraphQLUpload from graphql-upload

    import { Scalar } from '@nestjs/graphql';
    
    import { GraphQLUpload } from 'graphql-upload';
    
    @Scalar('Upload')
    export class Upload {
      description = 'Upload custom scalar type';
    
      parseValue(value) {
        return GraphQLUpload.parseValue(value);
      }
    
      serialize(value: any) {
        return GraphQLUpload.serialize(value);
      }
    
      parseLiteral(ast) {
        return GraphQLUpload.parseLiteral(ast);
      }
    }
    

    That i added in my Application module

    @Module({
      imports: [
      ...
        DateScalar,
        Upload,
        GraphQLModule.forRoot({
          typePaths: ['./**/*.graphql'],
         ...
          uploads: {
            maxFileSize: 10000000, // 10 MB
            maxFiles: 5,
          },
        }),
      ...
      ],
    ...
    })
    export class ApplicationModule {}
    

    i also added Upload scalar in my graphql

    scalar Upload
    ...
    type Mutation {
      uploadFile(file: Upload!): String
    }
    

    and is worked in my resolver i had acces to the uploaded file.

      @Mutation()
      async uploadFile(@Args('file') file,) {
        console.log('Hello file',file)
        return "Nice !";
      }
    

    (side note : I used https://github.com/jaydenseric/apollo-upload-client#function-createuploadlink to upload the file, in the resolver it is a Node Stream)