I have a problem with @nestjs/graphql with serverless.
When i start the app normaly with the $ nest start
command, it's working well without any error.
But with the $ sls offline
command, it's not running and i have this error when i go to the /graphql (playground) endpoint :
offline: ANY /graphql (λ: graphql)
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [NestFactory] Starting Nest application...
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] ConfigHostModule dependencies initialized +35ms
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] ConfigModule dependencies initialized +1ms
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] JwtModule dependencies initialized +1ms
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] GraphQLModule dependencies initialized +0ms
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] GraphQLSchemaBuilderModule dependencies initialized +0ms
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] GraphQLModule dependencies initialized +1ms
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] DatabaseModule dependencies initialized +166ms
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] AuthModule dependencies initialized +2ms
[Nest] 49532 - 04/02/2022, 22:56:30 LOG [InstanceLoader] UsersModule dependencies initialized +1ms
offline: Failure: Cannot determine a GraphQL output type for the "roles". Make sure your class is decorated with an appropriate decorator.
I think the problem is located on a specific objectType who expose the User
(an other objectType+Entity).
// auth.models.ts
import { User } from '@features/graphql/users/entities';
import { Field, ObjectType } from '@nestjs/graphql';
@ObjectType()
export class SignInUserModel {
@Field(() => User, { nullable: true })
user?: User;
@Field(() => String, { nullable: true })
accessToken?: string;
@Field(() => String, { nullable: true })
refreshToken?: string;
}
Because if i remove the User
field from the SignInUserModel
, the problem disappear ... someone have an idea of what i need to do ?
A specific decorator or something is missing for roles in my User model ?
I am totally lost haha
About my code:
// lambda.handler
import { defaultConfig } from '@config';
import { GraphQLModule } from '@features/graphql/graphql.module';
import { Logger, ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import serverlessExpress from '@vendia/serverless-express';
import { Callback, Context, Handler } from 'aws-lambda';
let server: Handler;
async function bootstrap() {
const config = defaultConfig();
const app = await NestFactory.create(GraphQLModule);
// Logger
if (config.isLoggerEnabled) app.useLogger(app.get(Logger));
// Validation
app.useGlobalPipes(new ValidationPipe());
await app.init();
const expressApp = app.getHttpAdapter().getInstance();
return serverlessExpress({ app: expressApp });
}
export const handler: Handler = async (
event: any,
context: Context,
callback: Callback,
) => {
server = server ? server : await bootstrap();
return server(event, context, callback);
};
// graphql.module.ts
import { ConfigModule, graphqlConfig } from '@config';
import { AppService } from '@features/app.service';
import { UsersModule } from '@features/graphql/users/users.module';
import { AuthModule } from '@features/_auth/auth.module';
import { Module } from '@nestjs/common';
import { GraphQLModule as NESTJSGraphQLModule } from '@nestjs/graphql';
@Module({
imports: [
ConfigModule,
AuthModule,
NESTJSGraphQLModule.forRoot({
installSubscriptionHandlers: true,
autoSchemaFile: graphqlConfig().generatedSchemaFileLocation,
sortSchema: true,
debug: graphqlConfig().isDebugEnabled,
introspection: true,
context: ({ req }) => ({ headers: req.headers }),
playground: graphqlConfig().isPlaygroundEnabled
? {
settings: { 'schema.polling.enable': false },
}
: false,
}),
UsersModule,
],
providers: [AppService],
})
export class GraphQLModule {
constructor(private readonly appService: AppService) {
if (!this.appService.checkEnv()) process.exit();
}
}
// user.entity.ts
import { UserRole } from '@features/graphql/users/users.enums';
import { Field, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@ObjectType()
@Entity({ name: 'users' })
export class User {
@PrimaryGeneratedColumn('uuid')
@Field(() => String)
id: string;
@Column('text')
@Field(() => String)
firstName: string;
@Column('text')
@Field(() => String)
lastName: string;
@Column('text')
@Field(() => String)
email: string;
@Column({ type: 'enum', enum: UserRole, array: true, default: ['USER'] })
@Field(() => [UserRole])
roles: UserRole[];
...
}
// user.enums.ts
export enum UserRole {
ADMIN = 'ADMIN',
MODERATOR = 'MODERATOR',
USER = 'USER',
}
// serverless.ts
import type { AWS } from '@serverless/typescript';
import { config as dotEnvConfig } from 'dotenv';
import * as envVar from 'env-var';
import packageConfig from './package.json';
dotEnvConfig();
const serverlessConfiguration: AWS = {
service: packageConfig.name,
useDotenv: true,
frameworkVersion: '*',
plugins: {
modules: [
'serverless-plugin-optimize',
'serverless-offline',
'serverless-plugin-warmup',
],
},
custom: {
warmup: {
default: {
enabled: true,
},
},
'serverless-offline': {
noPrependStageInUrl: true,
},
},
provider: {
name: 'aws',
lambdaHashingVersion: '20201221',
runtime: 'nodejs12.x',
apiGateway: {
shouldStartNameWithService: true,
},
environment: {
/* ... my envs ... */
},
},
functions: {
graphql: {
handler: 'dist/src/features/graphql/_lambda.handler',
events: [
{
http: {
method: 'ANY',
path: '/graphql',
},
},
],
},
},
};
module.exports = serverlessConfiguration;
I am using packages :
"dependencies": {
"@nestjs/common": "^8.0.0",
"@nestjs/config": "^1.1.6",
"@nestjs/core": "^8.0.0",
"@nestjs/graphql": "^9.1.2",
"@nestjs/jwt": "^8.0.0",
"@nestjs/passport": "^8.1.0",
"@nestjs/platform-express": "^8.0.0",
"@nestjs/typeorm": "^8.0.3",
"@vendia/serverless-express": "^4.5.3",
"apollo-server-express": "^3.6.2",
"aws-lambda": "^1.0.7",
"bcryptjs": "^2.4.3",
"class-validator": "^0.13.2",
"env-var": "^7.1.1",
"graphql": "^15",
"nestjs-pino": "^2.5.0",
"passport": "^0.5.2",
"passport-local": "^1.0.0",
"pg": "^8.7.1",
"pino-http": "^6.6.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"typeorm": "^0.2.41"
},
"devDependencies": {
"@faker-js/faker": "^6.0.0-alpha.5",
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
"@serverless/typescript": "^3.0.0",
"@types/aws-lambda": "^8.10.92",
"@types/bcryptjs": "^2.4.2",
"@types/express": "^4.17.13",
"@types/jest": "27.0.2",
"@types/node": "^16.0.0",
"@types/passport-local": "^1.0.34",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.2.5",
"npm-run-all": "^4.1.5",
"prettier": "^2.3.2",
"serverless-offline": "^8.4.0",
"serverless-plugin-optimize": "^4.2.1-rc.1",
"serverless-plugin-warmup": "^6.2.0",
"source-map-support": "^0.5.20",
"supertest": "^6.2.2",
"ts-jest": "^27.1.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsc-alias": "^1.5.0",
"tsconfig-paths": "^3.12.0",
"typescript": "^4.3.5"
}
Thanks for your help and I hope you have a solution :)
According to this page https://docs.nestjs.com/graphql/unions-and-enums
enums need to be declared with the function registerEnumType
registerEnumType(UserRole, {
name: 'UserRole',
});