Writing Blazing Fast and Less Code CRUD Operation + OpenAPI in NestJS
Tags: nestjs, crud, swagger, nestjsxcrud
Published: 2020-04-09 00:33:58

### 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: 
```typescript
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:
```typescript
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](https://dev-to-uploads.s3.amazonaws.com/i/38to9ifrg14baevscwt9.png)

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:
```typescript
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:
```typescript
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:
```typescript
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`:
```typescript
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`: 
```typescript
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](https://dev-to-uploads.s3.amazonaws.com/i/h7ue1buqw11y0iei5066.png)

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](https://dev-to-uploads.s3.amazonaws.com/i/lrh179595a9bfxjak98i.png)

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: 
```typescript
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](https://dev-to-uploads.s3.amazonaws.com/i/mste6xx60ojsr8irqft1.png)

We can now test our crud operation.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2v0pc3y0vm52dzqch7jf.png)

Aside from that nestjs/crud comes in with different options like pagination, filters, etc
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9q1ciqe8soqxulqdkden.png)

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 
 - Wiki: https://github.com/nestjsx/crud/wiki
 - Repo: https://github.com/nestjsx/crud