Nestjs Apollo graphql 上传标量

Nestjs Apollo graphql upload scalar

我正在使用 nestjs graphql 框架,我想使用 apollo 标量上传

我已经能够在另一个不包含 nestjs 的项目中使用标量。

schema.graphql App.module.ts 注册 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('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!
}

我希望标量与 nestjs 一起使用,但我的实际结果是

{"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}

我使用 graphql-upload 库解决了它。 首先,我使用 graphql-upload

中的 GraphQLUpload 为我的标量创建了一个 class
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);
  }
}

我在应用程序模块中添加的

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

我还在我的 graphql 中添加了上传标量

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

并且在我的解析器中工作,我可以访问上传的文件。

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

(旁注:我使用 https://github.com/jaydenseric/apollo-upload-client#function-createuploadlink 上传文件,在解析器中它是一个节点流)

使用 从“apollo-server-express”导入 {GraphQLUpload}
不是来自'graphql-upload'

import { Resolver, Mutation, Args } from '@nestjs/graphql';
import { createWriteStream } from 'fs';

import {GraphQLUpload} from "apollo-server-express"

@Resolver('Download')
export class DownloadResolver {
    @Mutation(() => Boolean)
    async uploadFile(@Args({name: 'file', type: () => GraphQLUpload})
    {
        createReadStream,
        filename
    }): Promise<boolean> {
        return new Promise(async (resolve, reject) => 
            createReadStream()
                .pipe(createWriteStream(`./uploads/${filename}`))
                .on('finish', () => resolve(true))
                .on('error', () => reject(false))
        );
    }
    
}

请注意,此旧版本与 Node 14 不完全兼容。

Note: Apollo Server's built-in file upload mechanism is not fully supported in Node 14 and later, and it will be removed in Apollo Server 3. For details, see below.

所有版本Apollo Server的正确答案(完全兼容Node 14)

  1. 禁用 Apollo Server 的内置上传处理(不需要 apollo 3+)并将 graphqlUploadExpress 中间件添加到您的应用程序。
import { graphqlUploadExpress } from "graphql-upload"
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common"

@Module({
  imports: [
    GraphQLModule.forRoot({
      uploads: false, // disable built-in upload handling (for apollo 3+ not needed)
    }),
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(graphqlUploadExpress()).forRoutes("graphql")
  }
}
  1. 移除 GraphQLUploadapollo-server-core 导入,改为从 graphql-upload 导入
// import { GraphQLUpload } from "apollo-server-core" <-- remove this
import { FileUpload, GraphQLUpload } from "graphql-upload"