loading...

Writing Blazing Fast and Less Code CRUD Operation + OpenAPI in NestJS

efd1006 profile image Edmar Diaz ・4 min read

Introduction

Hello guys, today I'm gonna share to you how I write my CRUD Operations in a fastest way.

In this tutorial, we will create a simple ToDo List Backend with CRUD Operations, we will be using mysql as our database and typeorm for our ORM. We will be using nestjsx/crud package for our easy crud operation.

Before we start make sure you have the following installed and configured:

  • MySQL
  • NestJS

Let's Get Started

First let's create a new project using nest-cli:

nest new crud-operation

First lets install typeporm, mysql library and dotenv:

npm install --save dotenv
npm install --save @nestjs/typeorm typeorm mysql

Now lets create our .env file and database configuration service:

touch .env
mkdir src/shared && mkdir src/shared/services
touch src/shared//services/database-connection.service.ts

Let open our .env file and configure our database credential, make sure to put your own database credentials:

DATABASE_HOST = "localhost"
DATABASE_PORT = 3306
DATABASE_USER = "root"
DATABASE_PASSWORD = "root"
DATABASE_DB = "nestjs_crud"

Now let's open our database-connection.service.ts and lets add the follwing code:

import { Injectable } from '@nestjs/common'
import { TypeOrmOptionsFactory, TypeOrmModuleOptions } from '@nestjs/typeorm'
import 'dotenv/config'

@Injectable()
export class DatabaseConnectionService implements TypeOrmOptionsFactory {

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      name: 'default',
      type: 'mysql',
      host: process.env.DATABASE_HOST,
      port: Number(process.env.DATABASE_PORT),
      username: process.env.DATABASE_USER,
      password: process.env.DATABASE_PASSWORD,
      database: process.env.DATABASE_DB,
      synchronize: true,
      dropSchema: false,
      logging: true,
      entities: ['dist/**/*.entity.js'],
    }
  }
}

After we created our database connection service lets configure our TypeOrmModule and use our database connection service, lets open app.module.ts import our TypeOrmModule:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DatabaseConnectionService } from './shared/services/database-connection.service';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      useClass: DatabaseConnectionService
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Now that we have our database connection configured lets check if we are really connected lets run our application by using this command:

npm run start:dev

Now lets wait for the compilation and build and after it we should see the following:
Alt Text

Now let's make our todo module, service and controller:

nest g module todo
nest g service todo
nest g controller todo

After we created our todo module, service and controller lets install the nestjsx/crud:

npm i @nestjsx/crud class-transformer class-validator
npm i @nestjsx/crud-typeorm

Now lets create our todo entity (Database Entity) :

touch src/todo/todo.entity.ts

Let's open our todo.entity.ts and create our todo entity:

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm";

@Entity('todos')
export class TodoEntity {

  @PrimaryGeneratedColumn() 
  id: number;

  @Column() 
  title: string;

  @Column()
  description: string

  @Column({
    type: 'boolean',
    default: false
  })
  is_done: boolean

  @CreateDateColumn()
  create_at: Date

  @UpdateDateColumn()
  updated_at: Date
}

We successfully created our todos table in our database.

Now Let's start our CRUD Operation, lets open our todo.service.ts and write the following code:

import { Injectable } from '@nestjs/common';
import { TypeOrmCrudService } from "@nestjsx/crud-typeorm";
import { TodoEntity } from './todo.entity';
import { InjectRepository } from '@nestjs/typeorm';

@Injectable()
export class TodoService extends TypeOrmCrudService<TodoEntity>{
  constructor(@InjectRepository(TodoEntity) repo) {
    super(repo)
  }
}

Now lets open our todo.controller.ts and write the following:

import { Controller } from '@nestjs/common';
import { Crud, CrudController } from '@nestjsx/crud';
import { TodoEntity } from './todo.entity';
import { TodoService } from './todo.service';

@Crud({
  model: {
    type: TodoEntity
  }
})

@Controller('todo')
export class TodoController implements CrudController<TodoEntity> {
  constructor(public service: TodoService) {}
}

At this point we are almost done with our service and controller and ofcourse CRUD Operation, we just need to import our TypeOrmModule and use our TodoEntity on our todo.module.ts so lets open up our todo.module.ts:

import { Module } from '@nestjs/common';
import { TodoService } from './todo.service';
import { TodoController } from './todo.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TodoEntity } from './todo.entity';

@Module({
  imports: [
    TypeOrmModule.forFeature([TodoEntity])
  ],
  providers: [TodoService],
  controllers: [TodoController]
})
export class TodoModule {}

At this point our CRUD Operation is done, now maybe you are wondering WTF where is @post , @Get, @Put, @Patch, @Delete methods?? Nestjs/Crud package handles it for us automatically but we can override different method if we want our own custom logic just go to their wiki for more info about it, Now lets setup our OpenAPI, for this we will be using nestjs swagger:

npm install --save @nestjs/swagger swagger-ui-express

After installing swagger lets open our main.ts:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const options = new DocumentBuilder()
    .setTitle('Todo Crud')
    .setDescription('The todo API description')
    .setVersion('1.0')
    .addTag('todo')
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('api/docs', app, document);

  await app.listen(3000);
}
bootstrap();

Now our openAPI is configured we can now access it on http://localhost:3000/api/docs
Alt Text

Now we can see our different todo endpoints. But our openAPI is not properly documented at this moment we need to add api tags for us to show what are the fields that we need on creating a todo for example this screenshot.
Alt Text

It's empty right?, We dont know what are to fields on our post request

In order to fix this we need to add @ApiProperty() on our column in our entity file, lets open our todo.entity.ts and add the decorator:

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm";
import { ApiProperty } from "@nestjs/swagger";

@Entity('todos')
export class TodoEntity {

  @PrimaryGeneratedColumn() 
  id: number;

  @ApiProperty()
  @Column() 
  title: string;

  @ApiProperty()
  @Column()
  description: string

  @ApiProperty()
  @Column({
    type: 'boolean',
    default: false
  })
  is_done: boolean

  @CreateDateColumn()
  create_at: Date

  @UpdateDateColumn()
  updated_at: Date
}

Lets refresh the page and, now we can see the fields

Alt Text

We can now test our crud operation.
Alt Text

Aside from that nestjs/crud comes in with different options like pagination, filters, etc
Alt Text

You dont have to worry about pagination, writing your own filters, etc, its blazing fast right and less code.

If you want to explore more and go deep in you can check nestjsx/crud wiki and repo

Posted on by:

efd1006 profile

Edmar Diaz

@efd1006

Application Developer working with React, ReactNative, Ionic(3,4,5) Angular <3, NestJS <3, NodeJS, TS <3 and #geekineers

Discussion

pic
Editor guide