所有涉及service、model均要在model注册
GraphQL是一种强大的 API 查询语言,也是使用现有数据完成这些查询的运行时。这是一种优雅的方法,可以解决通常在 REST API 中发现的许多问题。对于背景,建议阅读GraphQL 和 REST 之间的比较。GraphQL 与TypeScript相结合,可帮助您使用 GraphQL 查询开发更好的类型安全性,为您提供端到端的输入。
Mercurius(带有@nestjs/mercurius
)。我们为这些经过验证的 GraphQL 包提供官方集成,以提供一种将 GraphQL 与 Nest 结合使用的简单方法(请在此处查看更多集成)。
安装
# graphql
$ yarn add @nestjs/graphql @nestjs/mercurius graphql mercurius graphql-scalars
# 切换fastify 内核
$ yarn add @nestjs/platform-fastify
声明
- /src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import configuration from 'config/configuration';
import { SequelizeModule, SequelizeModuleOptions } from '@nestjs/sequelize';
import { DataLogHisModel } from './model/customer/data-log-his.model';
import { DataopOperationModel } from './model/customer/dataop-operation.model';
import { DataopItemOperationModel } from './model/customer/dataop-item-operation.model';
import { OrgRoleModel } from './model/customer/org-role.model';
import { OrganizationModel } from './model/customer/organization.model';
import { OrgroleUserModel } from './model/customer/orgrole-user.model';
import { RoleModel } from './model/customer/role.model';
import { UserModel } from './model/customer/user.model';
import { WebopOrgroleModel } from './model/customer/webop-orgrole.model';
import { WebOperationModel } from './model/customer/web-operation.model';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
import { GraphQLJSONObject } from 'graphql-scalars';
import { MercuriusDriver, MercuriusDriverConfig } from '@nestjs/mercurius';
const envFilePath = ['env/.env'];
if (process.env.NODE_ENV) {
envFilePath.unshift(`env/.env.${process.env.NODE_ENV}`);
}
@Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
envFilePath,
}),
SequelizeModule.forRoot({
...dbCustomerConfig(),
models: [
DataLogHisModel,
DataopOperationModel,
DataopItemOperationModel,
DataopOperationModel,
OrgRoleModel,
OrganizationModel,
OrgroleUserModel,
RoleModel,
UserModel,
WebOperationModel,
WebopOrgroleModel,
],
logging: (...msg) => console.log(msg),
} as SequelizeModuleOptions),
// Mercurius 不支持异步
GraphQLModule.forRoot<MercuriusDriverConfig>({
driver: MercuriusDriver,
graphiql: true,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
sortSchema: true,
resolvers: { JSONObject: GraphQLJSONObject },
path: `/gql`, // graphql 路径
prefix: process.env.PREFIX, // graphiql 前缀
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
function dbCustomerConfig(): SequelizeModuleOptions {
throw new Error('Function not implemented.');
}
- /src/main.ts
Mercurius 依赖于 Fastify 切换app内核
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from '@nestjs/config';
import { Logger } from '@nestjs/common';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
const configService = app.get(ConfigService);
const PORT = configService.get('PORT');
const HOST = configService.get('HOST');
const PREFIX = `/${configService.get('PREFIX')}`;
const PROJECTNAME = configService.get('PROJECTNAME');
const logger: Logger = new Logger('main.ts');
await app.listen(PORT, HOST, () => {
logger.log(
`[${PROJECTNAME}]已经启动,接口请访问:
http://${HOST}:${PORT}${PREFIX}
http://${HOST}:${PORT}${PREFIX}/graphiql
`,
);
});
}
bootstrap();
代码生成
# 选择 user 对象 生成
$ yarn code
基础对象代码
- /src/utils/common.input
import { Field, InputType, Int } from '@nestjs/graphql';
import { GraphQLJSONObject } from 'graphql-scalars';
/**
* 查询用参数
*/
@InputType()
export class FindAllInput {
@Field(() => GraphQLJSONObject, {
nullable: true,
description: '过滤条件',
})
where?: any;
@Field(() => [[String]], { nullable: true, description: '排序' })
orderBy?: Array<Array<string>>;
@Field(() => Int, { nullable: true })
limit?: number;
@Field(() => Int, { nullable: true })
skip?: number;
}
- /src/utils/base-service.ts
import { GraphQLJSONObject } from 'graphql-scalars';
import {
get,
isArray,
isFunction,
isObject,
mapKeys,
set,
startsWith,
toInteger,
} from 'lodash';
import { Op, WhereOptions } from 'sequelize';
import { JwtAuthEntity } from 'src/auth/jwt-auth-entity';
import { BaseModel } from 'src/model/base.model';
import { FindAllInput } from './common.input';
export type ModelStatic = typeof BaseModel & {
new (): BaseModel;
};
export type DefaultT = any;
export abstract class IBaseService<T extends BaseModel = DefaultT> {
abstract get GetModel(): typeof BaseModel;
/**
* 获取列表
* @param param
* @returns
*/
public findAll<X = T>(
param: FindAllInput,
user?: JwtAuthEntity,
): Promise<Array<X>> {
const sqOptions = {
where: this.jsonToWhere(param.where),
limit: param?.limit || toInteger(process.env.SQ_LIMIT || 1000),
offset: param?.skip,
order: param?.orderBy || [['id', 'DESC']],
};
return this.GetModel.findAll(sqOptions as any) as any;
}
/**
* 获取行数
* @param param
* @returns
*/
public findCount(
param: typeof GraphQLJSONObject,
user?: JwtAuthEntity,
): Promise<number> {
return this.GetModel.count({
where: this.jsonToWhere(param),
});
}
/**
* 根据id获取
* @param param
* @returns
*/
public findByPk<X = T>(param: string, user?: JwtAuthEntity): Promise<X> {
return this.GetModel.findByPk(param) as any;
}
public findOne<X = T>(param: FindAllInput, user?: JwtAuthEntity): Promise<X> {
const sqOptions = {
where: this.jsonToWhere(param.where),
limit: param?.limit || toInteger(process.env.SQ_LIMIT || 1000),
offset: param?.skip,
order: param?.orderBy || [['id', 'DESC']],
};
return this.GetModel.findOne(sqOptions as any) as any;
}
public create<X = T>(
createInput: X | any,
user?: JwtAuthEntity,
): Promise<X | any> {
createInput.createdId = user?.userId;
return this.GetModel.create(createInput as any) as any;
}
/**
*
* @param id
* @param updateInput
* @param user
* @returns
*/
public async update<X = T>(
id: string,
updateInput: X,
user?: JwtAuthEntity,
): Promise<X> {
return this.GetModel.findByPk(id).then((res) => {
mapKeys(updateInput as any, (value, key) => res.set(key, value));
res.updatedId = user?.userId;
return res.save();
}) as any;
}
/**
* 对象映射
* @param model
* @param input
* @returns
*/
public mapperModel<X = T>(model: X, input: any) {
mapKeys(input, (value, key) => {
const setFun = get(model, 'set');
if (setFun && isFunction(setFun)) {
(model as BaseModel).set(key, value);
} else {
set(model as any, key, value);
}
});
return model;
}
/**
* 逻辑删除
* @param id
* @returns
*/
public async remove(id: string, user?: JwtAuthEntity): Promise<string> {
return this.GetModel.findByPk(id)
.then((res) => {
res.deletedId = user?.userId;
return res.destroy();
})
.then(() => {
return id;
})
.catch((error) => {
throw error;
});
}
/**
* 删除
* @param id
* @param user
* @returns
*/
public async distory(id: string, user?: JwtAuthEntity): Promise<string> {
return this.GetModel.findByPk(id)
.then((res) => {
res.deletedId = user?.userId;
return res.destroy();
})
.then(() => {
return id;
})
.catch((error) => {
throw error;
});
}
/**
* json 查询参数 转换为 sequelize where条件
* @param param
* @returns
*/
public jsonToWhere(param: any): WhereOptions {
if (!param || !isObject(param)) {
return param;
}
return this.setOp(param);
}
/**
* 属性迭代 自循环
* @param param
* @returns
*/
private setOp(param: any) {
const res = isArray(param) ? [] : {};
for (const k of Reflect.ownKeys(param)) {
const v = param[k];
if (typeof k === 'string') {
res[startsWith(k, '_') ? Op[k.substring(1, k.length)] : k] =
isObject(v) && !(v instanceof Date) ? this.setOp(v) : v;
} else {
res[k] = isObject(v) && !(v instanceof Date) ? this.setOp(v) : v;
}
}
return res;
}
}
utils辅助
- /src/utils/base-entity.ts
import { Field, GraphQLISODateTime, ObjectType } from '@nestjs/graphql';
@ObjectType()
export class BaseEntity {
@Field(() => String, { description: 'id', nullable: true })
id: string;
@Field(() => GraphQLISODateTime, { description: '创建时间', nullable: true })
createdAt: Date;
@Field(() => GraphQLISODateTime, { description: '修改时间', nullable: true })
updatedAt: Date;
@Field(() => GraphQLISODateTime, { description: '删除时间', nullable: true })
deletedAt: Date;
@Field(() => String, { description: '创建人id', nullable: true })
createdId: string;
@Field(() => String, { description: '修改人id', nullable: true })
updatedId: string;
@Field(() => String, { description: '删除人id', nullable: true })
deletedId: string;
@Field(() => String, { description: '错误信息', nullable: true })
errorMessage?: string;
}