我在GraphQL+NestJS+TypeORM+MySQL上尝试了一下Helloworld
首先
我试着使用GraphQL+NestJS+TypeORM+MySQL来搭建应用程序开发的初始环境。除了MySQL以外,其他都是我不太熟悉的,所以在查阅资料的过程中设法让它们能够运转。希望对他人有所帮助,这会让我感到幸福。
这是我们的成果物。
事前准备
请安装Node.js、yarn和mysql(省略说明)
执行时的环境如下。
$ npm -v
8.8.0
$ yarn -v
1.22.4
$ mysql -V
mysql Ver 8.0.28 for macos12.2 on x86_64 (Homebrew)
安装Nest CLI
运行以下命令来安装 Nest CLI。
$ npm install -g @nestjs/cli
创建项目并进行初始化操作
使用CLI创建新的Nest项目,并切换到该文件夹。
选择yarn作为包管理器。
$ nest new nestjs-typeorm-ts-example
? Which package manager would you ❤️ to use?
npm
❯ yarn
pnpm
$ cd nestjs-typeorm-ts-example
首先,我们将执行必要的操作来确认功能可用性。请使用以下指令启动nest服务器。
$ yarn start:dev
如果显示如上则说明操作已确认完成。
引入GraphQL
使用 yarn 添加所需的 GraphQL packages。
$ yarn add @nestjs/graphql @nestjs/apollo graphql apollo-server-express
$ yarn add class-validator class-transformer #Validator使わなければ無くても良いけど
创建资源
我们将使用生成器来创建任务资源。
我们将使用GraphQL(code first)来创建传输层。
我们还将创建CRUD入口点。
$ nest g resource tasks
? What transport layer do you use?
REST API
❯ GraphQL (code first)
GraphQL (schema first)
Microservice (non-HTTP)
WebSockets
? Would you like to generate CRUD entry points? (Y/n) Y
CREATE src/tasks/tasks.module.ts (224 bytes)
CREATE src/tasks/tasks.resolver.spec.ts (525 bytes)
CREATE src/tasks/tasks.resolver.ts (1109 bytes)
CREATE src/tasks/tasks.service.spec.ts (453 bytes)
CREATE src/tasks/tasks.service.ts (625 bytes)
CREATE src/tasks/dto/create-task.input.ts (196 bytes)
CREATE src/tasks/dto/update-task.input.ts (243 bytes)
CREATE src/tasks/entities/task.entity.ts (187 bytes)
UPDATE src/app.module.ts (1163 bytes)
设置用于GraphQL
将nest-cli.json进行如下修改。
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": [
{
"name": "@nestjs/graphql",
"options": {
"introspectComments": true
}
}
]
}
}
另外,修改 src/app.module.ts 如下。这样就可以自动生成 src/schema.gql。
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TasksModule } from './tasks/tasks.module';
@Module({
imports: [
TasksModule,
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
debug: true,
playground: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
GraphQL 的作業檢查
当在本地主机的3000端口上访问时,设置到这一步时,将启动GraphQL Playground。
将 src/tasks/tasks.service.ts 文件中的 findAll 方法更改如下。
...
findAll() {
return [];
}
...
可以发出以下查询以获取结果(返回空数组)。
{
tasks{
exampleField
}
}
引入TypeORM
我会添加必要的软件包。
$ yarn add @nestjs/typeorm typeorm mysql2 reflect-metadata
在中国,只需要一种选项:引入typeorm-extension。
由于我想执行数据库的创建和删除操作,因此我还会安装typeorm-extension。
链接:https://github.com/tada5hi/typeorm-extension。
$ yarn add -D typeorm-extension
创建设置文件
我会创建两个设置文件。
import { DataSourceOptions } from 'typeorm';
const ormconfig: DataSourceOptions = {
name: 'default',
type: 'mysql',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
synchronize: false,
logging: false,
connectTimeout: 30 * 1000,
entities: [process.cwd() + '/dist/**/entities/**/*.entity.js'],
migrations: [process.cwd() + '/dist/database/migrations/**/*.js'],
charset: 'utf8mb4_general_ci',
};
export default ormconfig;
import { DataSource } from 'typeorm';
import ormconfig from './ormconfig';
export const AppDataSource = new DataSource(ormconfig);
创建.env文件
# DB
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=password
DB_DATABASE=nestjs-typeorm-ts-example
请根据需要在Shell中执行以下命令来设置环境变量。建议先设置好环境变量,然后启动nest服务器。
$ export $(cat .env | grep -v ^# | xargs)
添加命令
在package.json中添加命令。
...
"scripts": {
...
"db:create": "ts-node ./node_modules/typeorm-extension/dist/cli/index.js -f ./src/config/ormconfig.ts db:create",
"db:drop": "ts-node ./node_modules/typeorm-extension/dist/cli/index.js -f ./src/config/ormconfig.ts db:drop",
},
...
创建数据库
请使用以下命令创建数据库。在出现错误时,请确认是否从.env文件中读取了环境变量,并检查配置是否正确。
$ yarn db:create
yarn run v1.22.4
$ ts-node ./node_modules/typeorm-extension/dist/cli/index.js -f ./src/config/ormconfig.ts db:create
query: SELECT VERSION() AS `version`
query: START TRANSACTION
query: SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'nestjs-typeorm-ts-example' AND `TABLE_NAME` = 'typeorm_metadata'
query: COMMIT
✨ Done in 5.61s.
此外,如果要删除数据库,请按照以下步骤进行操作。
$ yarn db:drop
把实体连接到ORM。
将task实体连接到ORM。
省略了详细的代码说明(换言之,没有足够的了解来进行解释)。
import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql';
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
} from 'typeorm';
export enum TaskStatus {
NEW,
IN_PROGRESS,
COMPLETE,
}
registerEnumType(TaskStatus, {
name: 'TaskStatus',
});
@Entity()
@ObjectType()
export class Task {
@PrimaryGeneratedColumn()
@Field(() => ID)
id: string;
@Column({ length: '255' })
@Field()
title: string;
@Column('text')
@Field({ nullable: true })
description: string;
@Column({
type: 'enum',
enum: TaskStatus,
default: TaskStatus.NEW,
})
@Field(() => TaskStatus)
status: TaskStatus;
@CreateDateColumn()
@Field()
createdAt: Date;
@CreateDateColumn()
@Field()
updatedAt: Date;
}
import { InputType, Field } from '@nestjs/graphql';
import { MaxLength } from 'class-validator';
import { TaskStatus } from '../entities/task.entity';
@InputType()
export class CreateTaskInput {
@MaxLength(255)
@Field()
title: string;
@Field()
description: string;
@Field(() => TaskStatus)
status: TaskStatus;
}
import { CreateTaskInput } from './create-task.input';
import { InputType, Field, Int, PartialType } from '@nestjs/graphql';
@InputType()
export class UpdateTaskInput extends PartialType(CreateTaskInput) {
@Field(() => Int)
id: number;
}
我会创建迁移。
$ npx ts-node ./node_modules/.bin/typeorm migration:generate src/database/migrations/create-task -d src/config/ormdatasource
执行迁移。
$ npx ts-node ./node_modules/.bin/typeorm migration:run -d src/config/ormdatasource
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateTaskInput } from './dto/create-task.input';
import { UpdateTaskInput } from './dto/update-task.input';
import { Task } from './entities/task.entity';
@Injectable()
export class TasksService {
constructor(
@InjectRepository(Task)
private taskRepostiory: Repository<Task>,
) {}
async create(createTaskInput: CreateTaskInput) {
const task = this.taskRepostiory.create(createTaskInput);
await this.taskRepostiory.save(task);
return task;
}
findAll() {
return this.taskRepostiory.find();
}
async findOne(id: number) {
return await this.taskRepostiory.findOne({
where: {
id,
},
});
}
async update(id: number, updateTaskInput: UpdateTaskInput) {
const task = this.findOne(id);
if (task) {
await this.taskRepostiory.save(updateTaskInput);
}
}
async remove(id: number) {
const result = await this.taskRepostiory.delete(id);
return result.affected > 0;
}
}
import { Module } from '@nestjs/common';
import { TasksService } from './tasks.service';
import { TasksResolver } from './tasks.resolver';
import { Task } from './entities/task.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [TypeOrmModule.forFeature([Task])],
exports: [TypeOrmModule],
providers: [TasksResolver, TasksService],
})
export class TasksModule {}
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import ormconfig from './config/ormconfig';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TasksModule } from './tasks/tasks.module';
@Module({
imports: [
TypeOrmModule.forRoot(ormconfig),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
debug: true,
playground: true,
}),
TasksModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
到目前为止,我们已经能够使用GraphQL来操作数据,并将其存储或提取到MySQL中。
请使用GraphQL来验证数据操作的运行情况。
要新增一个,可以执行以下查询:
mutation {
createTask(createTaskInput:{title:"Hello", description:"Hello World!", status: NEW}) {
id
title
description
status
createdAt
updatedAt
}
}
要获取列表,需要执行以下查询。
{
tasks{
id
title
description
status
createdAt
updatedAt
}
}
数据库中的数据存储方式如下。
概括一下
实际上,迁移设置和命令花费了最多的时间。我参考了一篇文章并借用了其中的一些代码。非常感谢前辈们的帮助。
请提供更多的上下文。
-
- https://zenn.dev/naonao70/articles/a91d8835f1832b
-
- https://zenn.dev/msksgm/articles/20211107-typeorm-ormconfig
-
- https://qiita.com/potato4d/items/64a1f518abdfe281ce01
-
- https://zenn.dev/hakushun/articles/7daac74ae9af25
-
- https://zenn.dev/azukiazusa/articles/e84be9735d357e
-
- https://www.kindacode.com/snippet/using-enum-type-in-typeorm/
- https://www.wakuwakubank.com/posts/729-typeorm-migration/