我在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
image.png

如果显示如上则说明操作已确认完成。

引入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。

image.png

将 src/tasks/tasks.service.ts 文件中的 findAll 方法更改如下。

...
  findAll() {
    return [];
  }
...

可以发出以下查询以获取结果(返回空数组)。

{
  tasks{
    exampleField
  }
}
image.png

引入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
  }
}
image.png

要获取列表,需要执行以下查询。

{
  tasks{
    id
    title
    description
    status
    createdAt
    updatedAt
  }
}
image.png

数据库中的数据存储方式如下。

image.png

概括一下

实际上,迁移设置和命令花费了最多的时间。我参考了一篇文章并借用了其中的一些代码。非常感谢前辈们的帮助。

请提供更多的上下文。

    • 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/
广告
将在 10 秒后关闭
bannerAds