I. Environment configuration

  • 1. Official website address

    The official website provides two ways to manipulate GraphQL

    • The traditional way is to define firstschema
    • Direct use oftypescript-graphqlObject to create
  • 2. Install dependency packages

    npm install @nestjs/graphql graphql-tools graphql apollo-server-express
    Copy the code
  • 3. Add it in 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({
          // Automatically generate schema.graphQL files
          autoSchemaFile: 'schema.graph'})],controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}Copy the code
  • 4, delete scaffolding create AppController to resolver

    In graphQL, resolver is called a parser, including query, mutation, and subscription. In the graphQL project, we replaced the previous controller with resolver

    nest g r app
    Copy the code
    ➜ SRC git:(master) go onto university and go onto university. ├── app.class.solverCopy the code

Second, the firstgraphqlThe program

  • 1. Define resolver

    import { Resolver, Query } from '@nestjs/graphql';
    
    @Resolver(a)export class AppResolver {
      @Query(() = > String) // Define a query and return a character type
      hello() {
        return 'hello world'; }}Copy the code
  • 2. An error occurs when the program is directly started

  • 3. Fix this error by adding a line to the tsconfig.json file

    "compilerOptions": {
      "esModuleInterop": true,}Copy the code
  • 4. Check whether a schema.graph file is automatically generated under the project

  • 5. Launch the program and access it in your browserhttp://localhost:3000/graphql

Second, innestjsIn the integrationtypeorm

  • 1. At present, the common ORM for connecting database with GraphQL API in NestJS are as follows:

    • typeorm
    • prisma

    I’ve been using TypeOrM instead of messing with PRISma to connect to the database

  • 2. Install the TypeOrm package that interconnects with mysql

    npm install --save @nestjs/typeorm typeorm mysql
    Copy the code
  • 3. Configure database connection information in app.module.ts

    import { Module } from '@nestjs/common';
    import { AppService } from './app.service';
    import { GraphQLModule } from '@nestjs/graphql';
    import { AppResolver } from './app.resolver';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { UserModule } from './user/user.module';
    
    @Module({
      imports: [
        TypeOrmModule.forRoot({
          type: 'mysql'.host: 'localhost'.port: 3306.username: 'root'.password: '123456'.database: 'typeorm_mysql'.entities: [__dirname + '/**/*.entity{.ts,.js}'].logging: true.synchronize: true.extra: {
            poolMax: 32.poolMin: 16.queueTimeout: 60000.pollPingInterval: 60.// Connect every 60 seconds
            pollTimeout: 60.// The connection is valid for 60 seconds
          }
        }),
        GraphQLModule.forRoot({
          // Automatically generate schema.graphQL files
          autoSchemaFile: 'schema.graph',
        }),
        UserModule, // Create a user module].controllers: [].providers: [AppService, AppResolver],
    })
    export class AppModule {}Copy the code

Third, transform the user module intographqlA query

  • Transform the entity class into a graphQL that can be recognized

    import { Entity, BaseEntity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
    import { ObjectType, Field } from '@nestjs/graphql';
    
    @ObjectType(a)// Add this decorator to the class
    @Entity('user')
    export class UserEntity extends BaseEntity {
      @Field(a)// The field that needs to be returned plus this
      @PrimaryGeneratedColumn({
        type: 'int'.name: 'id'.comment: 'primary key id'
      })
      id: number;
    
      @Field(a)@Column({
        type: 'varchar'.unique: true.length: 50.nullable: false.name: 'username',})username: string;
    
      // This field will not be added unless it is returned
      @Column({
        type: 'varchar'.length: 100.nullable: false.name: 'password',})password: string;
    
      @Field(a)@CreateDateColumn({
        type: 'timestamp'.nullable: false.name: 'created_at'.comment: 'Creation time'
      })
      createdAt: Date;
    
      @Field(a)@UpdateDateColumn({
        type: 'timestamp'.nullable: false.name: 'updated_at'.comment: 'Update Time',})updatedAt: Date;
    }
    Copy the code
  • 2. Define front-end receiving parameters in user.resolver.ts to send data to the service layer for storage in the database

    import { Resolver, Mutation, Args } from '@nestjs/graphql';
    import { UserService } from './user.service';
    import { UserEntity } from './user.entity';
    
    @Resolver(a)export class UserResolver {
      constructor (
        private readonly userService: UserService,
      ) {}// The return value of the first argument,name is the definition alias, otherwise the default is square name
      @Mutation(() = > UserEntity, { nullable: true.name: 'register' })
      async register(
        @Args('username') username: string.// Use @args to receive client arguments
        @Args('password') password: string,
      ): Promise<UserEntity> {
        return await this.userService.register(username, password); }}Copy the code
  • 3. The service layer uses TypeOrM to store data in the database

    import { Injectable } from '@nestjs/common';
    import { UserEntity } from './user.entity';
    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    
    @Injectable(a)export class UserService {
      constructor (
        @InjectRepository(UserEntity)
        private readonly userRepository: Repository<UserEntity>,
      ) {}/** * User registration *@param username 
       * @param password 
       */
      async register(username: string.password: string) :Promise<UserEntity> {
        const user = this.userRepository.create({ username, password });
        return await this.userRepository.save(user); }}Copy the code
  • 4. The client stores the data in the database

  • 5. Define a query for all data

    import { Resolver, Mutation, Args, Query } from '@nestjs/graphql';
    import { UserService } from './user.service';
    import { UserEntity } from './user.entity';
    
    @Resolver(a)export class UserResolver {
      constructor (
        private readonly userService: UserService,
      ) {}@Query(() = >[UserEntity!] ! , {nullable: true })
      async userList(): Promise<UserEntity[]> {
        return await this.userService.userList(); }}Copy the code

  • 6, Query a data

    import { Resolver, Mutation, Args, Query } from '@nestjs/graphql';
    import { UserService } from './user.service';
    import { UserEntity } from './user.entity';
    
    @Resolver(a)export class UserResolver {
      constructor (
        private readonly userService: UserService,
      ) {}@Query(() = > UserEntity, { nullable: true.name: 'user' })
      async userById(
        @Args('id') id: number) :Promise<UserEntity> {
        return await this.userService.userById(id); }}Copy the code

  • 7. Delete data

    import { Resolver, Mutation, Args, Query } from '@nestjs/graphql';
    import { UserService } from './user.service';
    import { UserEntity } from './user.entity';
    
    @Resolver(a)export class UserResolver {
      constructor (
        private readonly userService: UserService,
      {}...@Mutation(() = > String, { nullable: true })
      async deleteById(
        @Args('id') id: number) :Promise<string> {
        return await this.userService.deleteById(id); }}Copy the code

  • 8. Modify data

    import { Resolver, Mutation, Args, Query } from '@nestjs/graphql';
    import { UserService } from './user.service';
    import { UserEntity } from './user.entity';
    
    @Resolver(a)export class UserResolver {
      constructor (
        private readonly userService: UserService,
      {}...@Mutation(() = > String, { nullable: true.name: 'modifyById' })
      async modifyById(
        @Args('id') id: number.@Args('username') username: string.@Args('password') password: string,
      ): Promise<string> {
        return await this.userService.modifyById(id, username, password); }}Copy the code

Four, the use ofdtoVerify incoming parameter data

  • 1. Installation package

    npm install --save class-validator class-transformer
    Copy the code
  • 2. Use global pipes in main.ts

    app.useGlobalPipes(new ValidationPipe());
    Copy the code
  • 3. Define the DTOS for user registration

    import { InputType, Field } from '@nestjs/graphql';
    import { IsNotEmpty, MinLength } from 'class-validator';
    
    @InputType(a)export class RegisterDto {
      @Field(a)@MinLength(3, { message: 'Minimum length is 3' })
      @IsNotEmpty({ message: 'User name cannot be empty' })
      username: string;
    
      @Field(a)@IsNotEmpty({ message: 'Password cannot be empty' })
      password: string;
    }
    Copy the code
  • 4. Use in resolver

    @Mutation(() = > UserEntity, { nullable: true.name: 'register' })
    async register(
      @Args('data') registerDto: RegisterDto, // Data is arbitrarily defined) :Promise<UserEntity> {
      return await this.userService.register(registerDto);
    }
    Copy the code
  • 5. Call method

Fifth, usejwtDo login returntoken

  • 1, install jsonWebToken package

    npm install jsonwebtoken
    Copy the code
  • Define a token-generating field in the user’s entity class

    @Field(() = > String)
    get token() {
      // The first parameter is to encrypt what to add, the second parameter is to add salt, here is a demo just write a random one, the third parameter is another parameter, this set expiration time to 3D
      return jwt.sign({ id: this.id, username: this.username }, 'abc', {
        expiresIn: '3d'})}Copy the code
  • 3. Login interface

    @Mutation(() = > UserEntity, { nullable: true })
    async login(
      @Args('data') loginDto: LoginDto,
    ): Promise<UserEntity> {
      return await this.userService.login(loginDto);
    }
    Copy the code
  • 4. Test interface

Use guards to guard other interfaces

  • 1. Install the dependency package reference document

    npm install passport @nestjs/passport @nestjs/jwt passport-jwt
    npm install @types/passport-jwt -D
    Copy the code
  • 2. Create a guard module

    ➜ auth git: (master) ✗ tree.. ├ ─ ─ auth. The module. The ts ├ ─ ─ GQL - auth. Guard. The ts └ ─ ─ JWT. Strategy. Ts 0 directories, 3 filesCopy the code
  • 3, auth.module.ts module code

    import { Module } from '@nestjs/common';
    import { JwtModule } from '@nestjs/jwt';
    import { JwtStrategy } from './jwt.strategy';
    
    @Module({
      imports: [
        JwtModule.register({
          secret: 'abc'.// This is the same as the encrypted one, where variables are defined separately in the actual project})].providers: [JwtStrategy]
    })
    export class AuthModule {}Copy the code
  • 4. Jwt.strategy.ts code

    You can refer to this source code for the implementation principle. If you are not satisfied with this way of implementation, you can define your own to achieve token verification

    import { Injectable, UnauthorizedException } from '@nestjs/common';
    import { PassportStrategy } from '@nestjs/passport';
    import { Strategy, ExtractJwt } from 'passport-jwt';
    import { UserEntity } from 'src/user/user.entity';
    
    @Injectable(a)export class JwtStrategy extends PassportStrategy(Strategy) {
      constructor () {
        super({
          jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
          ignoreExpiration: false.secretOrKey: 'abc'}); }async validate(payload: { id: number, username: string }) {
        // The id is parsed from the token to find the current user, and then attached to the REq
        const user = await UserEntity.findOne(payload.id);
        if(! user) {throw new UnauthorizedException('No such user');
        }
        returnuser; }}Copy the code
  • 5. Gql-auth.guard.ts

    import { Injectable, ExecutionContext } from '@nestjs/common';
    import { AuthGuard } from '@nestjs/passport';
    import { GqlExecutionContext } from '@nestjs/graphql';
    
    @Injectable(a)export class GqlAuthGuard extends AuthGuard('jwt') {
      getRequest(context: ExecutionContext) {
        const ctx = GqlExecutionContext.create(context);
        returnctx.getContext().req; }}Copy the code
  • 6, modify app.module.ts file

    import { Module } from '@nestjs/common';
    import { AppService } from './app.service';
    import { GraphQLModule } from '@nestjs/graphql';
    import { AppResolver } from './app.resolver';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { UserModule } from './user/user.module';
    import { AuthModule } from './auth/auth.module';
    
    @Module({
      imports: [
        ...
        GraphQLModule.forRoot({
          // Automatically generate schema.graphQL files
          autoSchemaFile: 'schema.graph'.context: ({ req }) = > ({ req }), // Add this line to mount data to context
        }),
        UserModule,
        AuthModule
      ],
      controllers: [].providers: [AppService, AppResolver],
    })
    export class AppModule {}Copy the code
  • Use guards in resolver

    @UseGuards(GqlAuthGuard)
    @Query(() = > UserEntity, { nullable: true.name: 'user' })
    async userById(
      @Args('id') id: number.@Context() context: any,
      ): Promise<UserEntity> {
        console.log(context.req ? .user,'Request Header data')
        return await this.userService.userById(id);
    }
    Copy the code
  • 8. Test data

    { "Authorization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbjEiLCJpYXQiOjE2MDk2NjQ1NzcsImV4cCI6MTYwOTkyMzc3N30 .H64VYR6plKDhki4wGBSSSsrRDaL51tQY55lZuhQ743U" }Copy the code