代码仓库
在开发完公司项目后,发现项目后端逻辑单一,对比前后端分离的开发流程,决定使用nestjs+graphql+mysql对前后端分离的项目进行优化,优化开发资源。
为什么使用nestjs
Nest 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。它使用渐进式 JavaScript,内置并完全支持 TypeScript(但仍然允许开发人员使用纯 JavaScript 编写代码)并结合了 OOP(面向对象编程),FP(函数式编程)和 FRP(函数式响应编程)的元素。
在底层,Nest使用强大的 HTTP Server 框架,如 Express(默认)和 Fastify。Nest 在这些框架之上提供了一定程度的抽象,同时也将其 API 直接暴露给开发人员。这样可以轻松使用每个平台的无数第三方模块。
为什么使用graphql
- 避免对数据的过度抓取
- 兼容不同环境对数据需求
- 产品的快速迭代
简介
1. 什么是nest.js
用于构建高效且可伸缩的服务端应用程序的渐进式 Node.js 框架。 完美支持 Typescript,面向 AOP 编程,支持 typeorm,Node.js 版的 spring,构建微服务应用。
2. 什么是graphql
GraphQL 是一种用于应用编程接口(API)的查询语言和服务器端运行时,可以使客户端准确地获得所需的数据,没有任何冗余。
nest.js项目搭建
1. 安装Nest CLI
npm i -g @nestjs/cli // 安装nest-cli
nest new nest-graphql // 新建工程
cd nest-grapql
yarn run start
2.添加@nestjs/graphql
npm i --save @nestjs/graphql graphql-tools graphql apollo-server-express ts-morph @apollo/gateway
3.引入GraphQLModule
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
@Module({
imports: [GraphQLModule.forRoot({})],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
此时运行npm start会抛出无法找到存在schema的异常,这里只需要配置对应的gql文件就可以解决。
4.添加@nestjs/config
在项目开发的过程中,我们经常会遇到不同项目,或者在不同环境下需要配置不同的参数。又或者,我们在开发的不同环境下都需要有修改不通的参数。在这种情况下,我们一般使用dotenv读取项目根目录下的.env文件,并通过process访问参数。而在nestjs中,我们使用@nestjs/config包中的ConfigModule来进行参数的处理。
npm i --save @nestjs/config
添加对应配置文件
// src/config/configuration.ts
export default () => ({
graphql: {
installSubscriptionHandlers: true, // 启用订阅
typePaths: ['./**/*.graphql'], // 指示 GraphQLModule 应该查找 GraphQL 文件的位置
},
});
在AppMoudle中注入模块,并导入到GraphQLModule
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import { ConfigModule, ConfigService } from '@nestjs/config';
import config from './config/configuration';
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: '.env',//
ignoreEnvFile: false,
isGlobal: true,
load: [config],
}),
GraphQLModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => {
return {
typePaths: configService.get('graphql.typePaths'),
};
},
inject: [ConfigService],
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
- 注入对应的业务模块
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { CatsModule } from './cats/cats.module';
import config from './config/configuration';
@Module({
imports: [
CatsModule,
ConfigModule.forRoot({
envFilePath: '.env',
ignoreEnvFile: false,
isGlobal: true,
load: [config],
}),
GraphQLModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => {
return {
typePaths: configService.get('graphql.typePaths'),
};
},
inject: [ConfigService],
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
为什么导入模块有的模块后面加forRoot
在nest.js中,我们会注意到在imports的数组中,对应的模块会调用forRoot()方法,并可以传入一个可选的参数家对象,那么这forRoot()是什么呢? 在nestjs中,注册模块不仅可以使用imports:[ConfigModule],也可以使用代码去动态注册,也就是调用模块类上的静态方法forRoot,返回一个对象,且这个对象需要是
DynamicModule实例。
// 这几个属性和我们在使用@module时使用的属性是一致的
{
module: ConfigModule,
providers: [ConfigService],
exports: [ConfigService],
}
// 当我们调用forRoot()返回动态模块,动态模块将会被注册
而传入的options对象可以作为内容提供者provider注册到动态模块中使用,进而起到了配置模块的作用
import { DynamicModule, Module } from '@nestjs/common';
import { ConfigService } from './config.service';
@Module({})
export class ConfigModule {
static forRoot(options): DynamicModule {
return {
module: ConfigModule,
providers: [
{
provide: 'CONFIG_OPTIONS', // 可以使用@Inject('CONFIG_OPTIONS') 在需要的模块中注入
useValue: options, // options使用了自定义提供者的方法去注入
},
ConfigService,
],
exports: [ConfigService],
};
}
}
import { Injectable, Inject } from '@nestjs/common';
import * as dotenv from 'dotenv';
import * as fs from 'fs';
import { EnvConfig } from './interfaces';
@Injectable()
export class ConfigService {
private readonly envConfig: EnvConfig;
constructor(@Inject('CONFIG_OPTIONS') private options) { // 这里使用了@Inject 标识了对象
const filePath = `${process.env.NODE_ENV || 'development'}.env`;
const envFile = path.resolve(__dirname, '../../', options.folder, filePath);
this.envConfig = dotenv.parse(fs.readFileSync(envFile));
}
get(key: string): string {
return this.envConfig[key];
}
}
至此,Nest.js的结合graphql整体框架。都已完成。后续将会第二篇讲详细说明客户端的搭建。