Loading...
Skip to main content

Create API in cerev-server

Generate service and controller

cerev-server uses NestJS as the backend framework for the whole application. Lets go to our api directory now in cerev-server/src ;

.
├── \_gen
│ └── prisma-class
├── api
│ ├── account-type
│ ├── admin
│ ├── analytic
│ ├── asset
│ ├── asset-type
│ ├── attachment
│ ├── auth
│ ├── comment
│ ├── companies
... many more files down..

Inside these folders, contains the folders that has our controller and service files respectively.

Lets run the following command leveraging Nest CLI.

nest g controller api/legal-compliances && nest g service api/legal-compliances

You will see two new files being created with the following directory structure.

.legal-compliances
├── legal-compliances.controller.spec.ts
├── legal-compliances.controller.ts
├── legal-compliances.service.spec.ts
└── legal-compliances.service.ts

Those files that are annotated with .spec. are test files. For simplicity of this example, we shall delete them for now.

Writing Business Logic

First, we go open legal-compliances.service.ts file and write the following code;

// In real scenario, do keep dto in separate files. We will
// touch on auto generation of dto classes later.
class CreateLegalDto {
name: string;
expiryDate: Date;
}

@Injectable()
export class LegalCompliancesService {
constructor(private prisma: PrismaService) {}

async createOne({dto}: {dto: CreateLegalDto}) {
await this.prisma.legalCompliance.create({
data: {
name: dto.name,
expiryDate: dto.expiryDate,
},
});
}

async getAll() {
return await this.prisma.legalCompliance.findMany();
}
}

Basically in service, this is where all the meat of the interaction between data received and database shall happen. This is also where the business logic happens.

Writing Controller / Routing Logic

To expose this legal-compliance.service.ts function through the application endpoints, we need to amend the legal-compliance.controller.ts. Add in the following code;

@Controller("legal-compliance")
export class LegalCompliancesController {
constructor(private legalComplianceSrv: LegalCompliancesService) {}

@Post()
async createLegalCompliance(@Body() dto: CreateLegalCom) {
return await this.legalComplianceSrv.createOne({dto});
}

@Get()
async getLegalCompliance() {
return await this.legalComplianceSrv.getAll();
}
}
danger

Please do not write database or business logic inside the controller files. This will make the functions very hard to test in the future. Remember the following golden rule

  1. controller is for Guarding access, deciding endpoint address, getting data from requests, middleware configuration.
  2. service is for communicating with database, complex business logic, transforming data structure.

ApiResponse

To ensure all typings are all accounted for, which later on will have an effect when we use OpenApi. We need to add @ApiResponse decorator to indicate the type of our response. Lets create a class for LegalCompliance data model first;

export class LegalCompliance {
id: number;
name: string;
date: Date;
}

We then add the @ApiResponse decorator in our controller class under our GET and POST endpoint as follows:

@Controller("legal-compliance")
export class LegalCompliancesController {
constructor(private legalComplianceSrv: LegalCompliancesService) {}

@Post()
@ApiResponse({
status: 200,
description: `Get All Legal Compliance`,
type: () => LegalCompliance,
})
async createLegalCompliance(@Body() dto: CreateLegalCom) {
return await this.legalComplianceSrv.createOne({dto});
}

@Get()
@ApiResponse({
status: 200,
description: `Get All Legal Compliance`,
type: () => LegalCompliance,
isArray: true,
})
async getLegalCompliance() {
return await this.legalComplianceSrv.getAll();
}
}

This will tell NestJS that these are the expected typings when the status is 200. For more information on ApiResponse, please refer to ApiResponse - NestJS

API is ready

Congratulation, your API is ready to be consumed. Next, we going to explore Swagger , OpenApi