@nestjs-mod/graphql
v1.4.3
Published
GraphQL packages, providing an easy way to use GraphQL with the NestJS-mod, integrated: dataloader, included support for pipes, filters and interceptors in resolver fields, works with Fastify (Wrapper for https://docs.nestjs.com/graphql/quick-start)
Downloads
67
Maintainers
Readme
@nestjs-mod/graphql
GraphQL packages, providing an easy way to use GraphQL with the NestJS-mod, integrated: dataloader, included support for pipes, filters and interceptors in resolver fields, works with Fastify (Wrapper for https://docs.nestjs.com/graphql/quick-start)
Installation
npm i --save @nestjs/apollo @apollo/server @nestjs/graphql @as-integrations/fastify @nestjs-mod/graphql
Modules
| Link | Category | Description | | ---- | -------- | ----------- | | GraphqlModule | system | GraphQL packages, providing an easy way to use GraphQL with the NestJS-mod, integrated: dataloader, included support for pipes, filters and interceptors in resolver fields, works with Fastify (Wrapper for https://docs.nestjs.com/graphql/quick-start) |
Modules descriptions
GraphqlModule
GraphQL packages, providing an easy way to use GraphQL with the NestJS-mod, integrated: dataloader, included support for pipes, filters and interceptors in resolver fields, works with Fastify (Wrapper for https://docs.nestjs.com/graphql/quick-start)
Use in NestJS
Example of main.ts
import { GRAPHQL_SCHEMA_FILE, GraphqlModule } from '@nestjs-mod/graphql';
import { Module } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { join } from 'path';
import { AppModule } from './app/app.module';
const rootFolder = join(__dirname, '..', '..', '..');
const appFolder = join(rootFolder, 'apps', 'example-graphql');
@Module({
imports: [
GraphqlModule.forRoot({
configuration: {
autoSchemaFile: join(appFolder, GRAPHQL_SCHEMA_FILE),
},
}),
AppModule.forRoot(),
],
})
export class RootModule {}
async function bootstrap() {
const app = await NestFactory.create(RootModule);
await app.listen(3005);
}
bootstrap();
Example of dataloader
import { GraphqlDataLoader } from '@nestjs-mod/graphql';
import { Injectable } from '@nestjs/common';
import DataLoader from 'dataloader';
import { UserBalanceDto } from '../resolvers/dto/user-balance.dto';
import { BalanceOfUserService } from './balance-of-user.service';
@Injectable()
export class BalanceOfUserDataloader implements GraphqlDataLoader<number, UserBalanceDto> {
constructor(private readonly balanceOfUserService: BalanceOfUserService) {}
generateDataLoader(): DataLoader<number, UserBalanceDto> {
return new DataLoader<number, UserBalanceDto>(async (userIds) =>
this.balanceOfUserService.getUserBalanceByUserIds(userIds)
);
}
}
Examples of resolver fields with dataloader and without
/* eslint-disable @typescript-eslint/no-unused-vars */
import { getRequestFromExecutionContext } from '@nestjs-mod/common';
import { Loader } from '@nestjs-mod/graphql';
import { NestjsPinoAsyncLocalStorage, X_REQUEST_ID } from '@nestjs-mod/pino';
import { Logger } from '@nestjs/common';
import { Args, Parent, ResolveField, Resolver, Subscription } from '@nestjs/graphql';
import DataLoader from 'dataloader';
import { BalanceOfUserDataloader } from '../services/balance-of-user.data-loader';
import { BalanceOfUserService } from '../services/balance-of-user.service';
import { CHANGE_USER_BALANCE_EVENT, UserBalanceDto } from './dto/user-balance.dto';
import { UserDto } from './dto/user.dto';
@Resolver(UserDto)
export class BalanceOfUserResolver {
constructor(private readonly balanceOfUserService: BalanceOfUserService) {}
@ResolveField('balance', () => UserBalanceDto)
async balance(
@Parent()
userDto: UserDto
): Promise<UserBalanceDto> {
return (await this.balanceOfUserService.getUserBalanceByUserIds([userDto.id]))[0];
}
@ResolveField('balanceOverDataLoader', () => UserBalanceDto)
async balanceOverDataLoader(
@Parent()
userDto: UserDto,
@Loader(BalanceOfUserDataloader)
balanceOfUserDataloader: DataLoader<number, UserBalanceDto>
) {
return await balanceOfUserDataloader.load(userDto.id);
}
}
Example of work with headers
import { Request } from '@nestjs-mod/common';
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { StatusDto } from './dto/status.dto';
import { CreateUserDto, UserDto } from './dto/user.dto';
@Resolver()
export class UsersResolvers {
static logger = new Logger(UsersResolvers.name);
private readonly usersStorage: UserDto[] = [
{ id: 1, username: 'admin', custom: { one: 1 } },
{ id: 2, username: 'user', custom: { two: 2 } },
];
constructor(private readonly balanceOfUserService: BalanceOfUserService) {}
@Query(() => [UserDto])
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async users(@Request() req: any): Promise<UserDto[]> {
if (req.headers['x-throw-error']) {
throw new Error('Error from query!');
}
return this.usersStorage;
}
@Mutation(() => StatusDto)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async createUser(@Request() req: any, @Args() userDto: CreateUserDto) {
if (req.headers['x-throw-error']) {
throw new Error('Error from mutation!');
}
this.usersStorage.push({ ...userDto, id: +userDto.id });
return { status: 'OK' };
}
@Subscription(() => UserBalanceDto, {
name: CHANGE_USER_BALANCE_EVENT,
filter: (payload: UserBalanceDto, variables: { userId: string }) => {
return payload.userId === +variables.userId;
},
resolve: (payload: UserBalanceDto, _args, ctx) => {
const req = getRequestFromExecutionContext(ctx);
BalanceOfUserResolver.logger.log({
requestId: req.headers?.[X_REQUEST_ID],
});
// todo: requestId from request not apply in logger
BalanceOfUserResolver.logger.log({ [CHANGE_USER_BALANCE_EVENT]: payload });
if (req.headers['x-throw-error']) {
throw new Error('Error from subscription!');
}
return payload;
},
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onChangeUserBalance(@Args('userId') userId: string) {
return this.balanceOfUserService.asyncIterator<UserBalanceDto>(CHANGE_USER_BALANCE_EVENT);
}
}
Use in NestJS-mod
An example of using forRoot with parameters and use dataloader, resolver fields and subscriptions, you can see the full example here https://github.com/nestjs-mod/nestjs-mod-contrib/tree/master/apps/example-graphql.
import {
DefaultNestApplicationInitializer,
DefaultNestApplicationListener,
InfrastructureMarkdownReportGenerator,
PACKAGE_JSON_FILE,
ProjectUtils,
bootstrapNestApplication,
isInfrastructureMode,
} from '@nestjs-mod/common';
import { FastifyNestApplicationInitializer, FastifyNestApplicationListener } from '@nestjs-mod/fastify';
import { GRAPHQL_SCHEMA_FILE, GraphqlModule } from '@nestjs-mod/graphql';
import { NestjsPinoLoggerModule } from '@nestjs-mod/pino';
import { ECOSYSTEM_CONFIG_FILE, Pm2 } from '@nestjs-mod/pm2';
import { TerminusHealthCheckModule } from '@nestjs-mod/terminus';
import { MemoryHealthIndicator } from '@nestjs/terminus';
import { join } from 'path';
import { AppModule } from './app/app.module';
const platform = process.env.PLATFORM === 'fastify' ? 'fastify' : 'express';
const rootFolder = join(__dirname, '..', '..', '..');
const appFolder = join(rootFolder, 'apps', 'example-graphql');
bootstrapNestApplication({
modules: {
system: [
ProjectUtils.forRoot({
staticConfiguration: {
applicationPackageJsonFile: join(appFolder, PACKAGE_JSON_FILE),
packageJsonFile: join(rootFolder, PACKAGE_JSON_FILE),
envFile: join(rootFolder, '.env'),
},
}),
platform === 'express'
? DefaultNestApplicationInitializer.forRoot({ staticConfiguration: { bufferLogs: true } })
: FastifyNestApplicationInitializer.forRoot({ staticConfiguration: { bufferLogs: true } }),
NestjsPinoLoggerModule.forRoot(),
TerminusHealthCheckModule.forRootAsync({
configurationFactory: (memoryHealthIndicator: MemoryHealthIndicator) => ({
standardHealthIndicators: [
{ name: 'memory_heap', check: () => memoryHealthIndicator.checkHeap('memory_heap', 150 * 1024 * 1024) },
],
}),
inject: [MemoryHealthIndicator],
}),
platform === 'express'
? DefaultNestApplicationListener.forRoot({
staticConfiguration: {
// When running in infrastructure mode, the backend server does not start.
mode: isInfrastructureMode() ? 'silent' : 'listen',
},
})
: FastifyNestApplicationListener.forRoot({
staticConfiguration: {
// When running in infrastructure mode, the backend server does not start.
mode: isInfrastructureMode() ? 'silent' : 'listen',
},
}),
GraphqlModule.forRoot({
configuration: {
autoSchemaFile: join(appFolder, GRAPHQL_SCHEMA_FILE),
},
}),
],
feature: [AppModule.forRoot()],
infrastructure: [
InfrastructureMarkdownReportGenerator.forRoot({
staticConfiguration: {
markdownFile: join(appFolder, 'INFRASTRUCTURE.MD'),
skipEmptySettings: true,
},
}),
Pm2.forRoot({
configuration: {
ecosystemConfigFile: join(rootFolder, ECOSYSTEM_CONFIG_FILE),
applicationScriptFile: join('dist/apps/example-graphql/main.js'),
},
}),
],
},
});
Configuration
| Key | Description | Constraints | Default | Value |
| ------ | ----------- | ----------- | ------- | ----- |
|path
|Path to mount GraphQL API|optional|/graphql
|-|
|typeDefs
|Type definitions|optional|-|-|
|typePaths
|Paths to files that contain GraphQL definitions|optional|-|-|
|driver
|GraphQL server adapter|optional|ApolloDriver extends apollo_base_driver_1.ApolloBaseDriver|-|
|include
|An array of modules to scan when searching for resolvers|optional|-|-|
|directiveResolvers
|Directive resolvers|optional|-|-|
|schema
|Optional GraphQL schema (to be used or to be merged)|optional|-|-|
|resolvers
|Extra resolvers to be registered.|optional|-|-|
|definitions
|TypeScript definitions generator options|optional|-|-|
|autoSchemaFile
|If enabled, GraphQL schema will be generated automatically|optional|schema.gql
|-|
|sortSchema
|Sort the schema lexicographically|optional|-|-|
|buildSchemaOptions
|Options to be passed to the schema generator, only applicable if "autoSchemaFile" = true|optional|-|-|
|useGlobalPrefix
|Prepends the global prefix to the url @see [faq/global-prefix](Global Prefix)|optional|-|-|
|fieldResolverEnhancers
|Enable/disable enhancers for @ResolveField()|optional|[ interceptors
, guards
, filters
]|-|
|resolverValidationOptions
|Resolver validation options.|optional|-|-|
|inheritResolversFromInterfaces
|Inherit missing resolvers from their interface types defined in the resolvers object.|optional|-|-|
|transformSchema
|Function to be applied to the schema letting you register custom transformations.|optional|-|-|
|transformAutoSchemaFile
|Apply transformSchema
to the autoSchemaFile
|optional|-|-|
|context
|Context function|optional|defaultContextFunction
|-|
|metadata
|Extra static metadata to be loaded into the specification|optional|-|-|
|installSubscriptionHandlers
|If enabled, "subscriptions-transport-ws" will be automatically registered.|optional|true
|-|
|subscriptions
|Subscriptions configuration.|optional|{"graphql-ws":{"path":"/graphql"}}
|-|
|playground
|GraphQL playground options.|optional|{"settings":{"request.credentials":"include"}}
|-|
|autoTransformHttpErrors
|If enabled, will register a global interceptor that automatically maps "HttpException" class instances to corresponding Apollo errors.|optional|-|-|
Links
- https://github.com/nestjs-mod/nestjs-mod - A collection of utilities for unifying NestJS applications and modules
- https://github.com/nestjs-mod/nestjs-mod-contrib - Contrib repository for the NestJS-mod
- https://github.com/nestjs-mod/nestjs-mod-example - Example application built with @nestjs-mod/schematics
- https://github.com/nestjs-mod/nestjs-mod/blob/master/apps/example-basic/INFRASTRUCTURE.MD - A simple example of infrastructure documentation.
- https://github.com/nestjs-mod/nestjs-mod-contrib/blob/master/apps/example-prisma/INFRASTRUCTURE.MD - An extended example of infrastructure documentation with a docker-compose file and a data base.
- https://dev.to/endykaufman/collection-of-nestjs-mod-utilities-for-unifying-applications-and-modules-on-nestjs-5256 - Article about the project NestJS-mod
- https://habr.com/ru/articles/788916 - Коллекция утилит NestJS-mod для унификации приложений и модулей на NestJS
License
MIT