所有涉及service、model均要在model注冊(cè)
GraphQL是一種強(qiáng)大的 API 查詢語(yǔ)言妻枕,也是使用現(xiàn)有數(shù)據(jù)完成這些查詢的運(yùn)行時(shí)僻族。這是一種優(yōu)雅的方法,可以解決通常在 REST API 中發(fā)現(xiàn)的許多問(wèn)題屡谐。對(duì)于背景述么,建議閱讀GraphQL 和 REST 之間的比較。GraphQL 與TypeScript相結(jié)合愕掏,可幫助您使用 GraphQL 查詢開發(fā)更好的類型安全性度秘,為您提供端到端的輸入。
Mercurius(帶有@nestjs/mercurius
)饵撑。我們?yōu)檫@些經(jīng)過(guò)驗(yàn)證的 GraphQL 包提供官方集成剑梳,以提供一種將 GraphQL 與 Nest 結(jié)合使用的簡(jiǎn)單方法(請(qǐng)在此處查看更多集成)唆貌。
安裝
# graphql
$ yarn add @nestjs/graphql @nestjs/mercurius graphql mercurius graphql-scalars
# 切換fastify 內(nèi)核
$ 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內(nèi)核
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}]已經(jīng)啟動(dòng),接口請(qǐng)?jiān)L問(wèn):
http://${HOST}:${PORT}${PREFIX}
http://${HOST}:${PORT}${PREFIX}/graphiql
`,
);
});
}
bootstrap();
代碼生成
# 選擇 user 對(duì)象 生成
$ yarn code
image.png
基礎(chǔ)對(duì)象代碼
- /src/utils/common.input
import { Field, InputType, Int } from '@nestjs/graphql';
import { GraphQLJSONObject } from 'graphql-scalars';
/**
* 查詢用參數(shù)
*/
@InputType()
export class FindAllInput {
@Field(() => GraphQLJSONObject, {
nullable: true,
description: '過(guò)濾條件',
})
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;
}
/**
* 獲取行數(shù)
* @param param
* @returns
*/
public findCount(
param: typeof GraphQLJSONObject,
user?: JwtAuthEntity,
): Promise<number> {
return this.GetModel.count({
where: this.jsonToWhere(param),
});
}
/**
* 根據(jù)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;
}
/**
* 對(duì)象映射
* @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 查詢參數(shù) 轉(zhuǎn)換為 sequelize where條件
* @param param
* @returns
*/
public jsonToWhere(param: any): WhereOptions {
if (!param || !isObject(param)) {
return param;
}
return this.setOp(param);
}
/**
* 屬性迭代 自循環(huán)
* @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: '創(chuàng)建時(shí)間', nullable: true })
createdAt: Date;
@Field(() => GraphQLISODateTime, { description: '修改時(shí)間', nullable: true })
updatedAt: Date;
@Field(() => GraphQLISODateTime, { description: '刪除時(shí)間', nullable: true })
deletedAt: Date;
@Field(() => String, { description: '創(chuàng)建人id', nullable: true })
createdId: string;
@Field(() => String, { description: '修改人id', nullable: true })
updatedId: string;
@Field(() => String, { description: '刪除人id', nullable: true })
deletedId: string;
@Field(() => String, { description: '錯(cuò)誤信息', nullable: true })
errorMessage?: string;
}