Membangun REST API dengan NestJS dan Prisma ORM

Membangun REST API dengan NestJS dan Prisma ORM

NestJS adalah salah satu framework yang dibangun diatas platform NodeJS yang akhir-akhir ini banyak dipakai oleh programmer NodeJS. Dalam tutorial kali ini, saya akan membagikan cara begitu mudahnya membangun REST API dengan NestJS ini.

Introduction

image.png Dalam materi ini, kamua akan dipandu step by step untuk membangun sebuah sistem yang dinamakan Median. Kita akan mengawali membuat project ini dengan membuat projek NestJS kemudian dilanjut dengan step postgresql db yang nantinya akan diintergrasikan dengan Prisma ORM. Dan harapan artikel ini kamu dapat mengetahui cara membuat REST API dengan NestJS sehingga kedepannya kamu mempunyai fondasi yang cukup untuk belajar NestJS ke level yang lebih advance lagi.

Teknologi yang digunakan

Kamu akan dipandu untuk dapat menggunakan beberapa tools di bawah ini :

  • NestJS sebagai backend framework

  • Prisma sebagai Object Relational Mapping (ORM)

  • Postgresql sebagai Database

  • Swagger sebagai API dokumentasi

  • dan tentunya, Typescript sebagai bahasa yang digunakan

Prerequisites

Ini adalah tutorial untuk pemula. Namun dalam tutorial ini, saya mengasumsikan bahwa kalian sudah :

  • Mengetahui basic dari Javascript or Typescript (lebih diutamakan)

  • Mengetahui basic dari NestJS

Jika kamu belum mengetahui gambaran umum dari NestJS, saya sarankan untuk membaca NestJS Overview dulu sebelum memulai mencoba artikel ini.

Development Environtment

namun sebelum itu, pastikan bahwa di laptop local kalian sudah diinstall tools seperti dibawah ini :

Membuat Project NestJS

Pertama sebelum membuat projek NestJS adalah kamu harus menginstall Nest CLI terlebih dahulu. dengan Nest CLI kamu bisa membuat project, membuat resource, controller, service dan lainnya. Untuk memulainya, ketikan command sebagai berikut

npx i -g @nestjs/cli new median

CLI akan meminta kamu untuk memilih sebuah package manager untuk project ini -- pilihlah npm. Kemudian kamu akan mendapatkan sebuah NestJS projek dengan nama folder median. Buka visual studio code dan buka project tersebut. Kamu akan mendapatkan struktur project kurang lebih seperti ini.

median
  ├── node_modules
  ├── src
  │   ├── app.controller.spec.ts
  │   ├── app.controller.ts
  │   ├── app.module.ts
  │   ├── app.service.ts
  │   └── main.ts
  ├── test
  │   ├── app.e2e-spec.ts
  │   └── jest-e2e.json
  ├── README.md
  ├── nest-cli.json
  ├── package-lock.json
  ├── package.json
  ├── tsconfig.build.json
  └── tsconfig.json

Namun kita akan fokus ke folder src dimana folder itu adalah tempat kita menuliskan kode. NestCLI sudah membuatkan projek untukmu, ada beberapa hal yang perlu digaris bawahi adalah :

  • src/app.module.ts adalah root module dari aplikasi.

  • src/app.controller.ts adalah basic dari Controller yang output nya akan menghasilkan text "Hello World".

  • src/main.ts entri poin dari aplikasi. Ini class acuan dalam menjalankan aplikasi NestJS.

Kamu dapat menjalankan projek menggunakan command berikut :

npm run start:dev

command ini akan menggunakan mode watch dimana setiap perubahan kode akan langsung mentrigger restart aplikasi. Untuk mengecek apakah aplikasinya sudah berjalan, buka browser dan masukan alamat localhost:3000 , kamu akan melihat halaman kosong dengan tulisan Hello World.

Note: kamu harus tetap menjaga agar aplikasi / server nya tetap berjalan selama mengikuti tutorial ini.

Membuat Database di Postgresql

Langkah selanjut nya adalah buat sebuah database di Postgresql misalkan dengan nama median-db, kalian bisa menggunakan DBeaver ato DB Tools lainnya

Setup Prisma ORM

Database selesai dibuat, kemudian step selanjutnya adalah mensetup Prisma ORM.

Initialize Prisma

Untuk dapat menggunakan Prisma, hal yang harus kalian lakukan adalah menginstall Prisma CLI. Dengan menggunakan Prisma CLI, kalian bisa memberikan perintah-perintah terkait database pada aplikasi kamu.

npm install -D prisma

Setelah selesai mendownload library prisma, kamu dapat menginisiasi prima ke dalam projek dengan menggunakan perintah.

npx prisma init

perintah ini akan menggenerate sebuah folder dengan nama prisma sebuah file schema.prisma. Disamping itu, Prisma juga akan membuat sebuah file bernama .env di root folder project.

Setup Environtment Variable

Di dalam file .env, kamu akan melihat satu properti dengan nama DATABASE_URL dengan sebuah nilai dummy. Gantikan dengan username, password dan nama database yang ada di localhost postgresql kalian.

// .env
DATABASE_URL="postgres://username:password@localhost:5432/median-db"

Note: Tolong lihat kembali username dan password postgresqnya dan masukan ke file .env diatas.

Memahami Schema Prisma

Jika kamu membuka file prisma/schema.prisma kamu akan melihat default schema sebagai berikut :

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

File ini ditulis menggunakan Prisma Schema Language dimana dengan bahasa tersebut, Prisma dapat mudah mendefinisikan schema dengan cepat. schema.prisma dibagi menjadi tiga komponen utama yaitu :

  • Datasource : Mendefinisikan koneksi ke database. Konfigurasi .env diatas menyatakan bahwa provider database yang kamu gunakan adalah PostgreSQL dan alamat yang menghubungkan aplikasi dengan database itu ada di variable DATABASE_URL.

  • Generator : Mengindikasikan bahwa kamu menyetujui untuk menggenerate prisma client yang bisa digunakan untuk mengirim query ke database.

  • Data Model : Mendefinisikan model database kamu. Setiap model dipetakan ke dalam tabel yang berada pada database yang udh didefinisikan sebelumnya. Betul, untuk sekarang kamu belum melihat model apapun dalam schema prisma, kamu akan mengeksplor itu di part selanjutnya.

    Note: Jika kamu ingin mengeksplor Prisma lebih dalam lagi, kalian bisa kunjungi link prisma docs nya

Membuat Artikel Model

Sekarang waktunya untuk mendefinisikan model dari aplikasimu. Dalam tutorial ini, kamu hanya membutuhkan article model untuk merepresentasikan setiap artikel dalam blog. Didalam file prisma/prisma.schema, buatlah model baru dengan nama Article.

// prisma/schema.prisma

model Article {
  id                    Int      @id @default(autoincrement())
  title                 String   @unique
  description     String?
  body                String
  published       Boolean  @default(false)
  createdAt       DateTime @default(now())
  updatedAt     DateTime @updatedAt
}

Disini kamu sudah membuat Article model dengan beberapa field. Sebuah field mempunyai nama (id, title, description) dan mempunyai tipe (Int, String dst). Dan beberapa atribute opsional seperti @id, @unique dst. Sebuah field bisa bersifat optional dengan menambahkan tanda ? disamping tipe data.

Field id mempunyai spesial atribut @id. Atribut ini mengindikasikan bahwa field ini adalah primary key dari model dan atribut @default(autoincrement) juga mengindikasikan bahwa field ini nantinya akan diisi dengan nilai otomatis increment setiap kamu menambahkan data baru.

Field published adalah sebuah flag yang menentukan apakah artikel ini dipublikasikan atau masih dalam bentuk draft. Atribut @default(false) mengindikasikan bahwa field ini akan diisi dengan nilai false secara default.

Dua field DateTime yaitu createdAt dan updatedAt akan memantau kapan artikel itu dibuat dan diupdate. Field @updatedAt akan secara otomatis mengupdate nilai nya dengan nilai current timestampt (saat ini) dimanapun artikel itu diupdate.

Migrate Database

Setelah mendefinisikan prisma schema dan model langkah selanjutnya adalah menjalankan command migrate untuk mengkonversikan model ke dalam bentuk tabel di database postgresql.

npx prisma migrate dev --name "init_article_table"

Command diatas akan melakukan tiga hal berikut :

  • Save the migration: Prisma akan mengambil sebuah snapshot dari schema dan mencari tahu command SQL yang cocok untuk proses migrasi ini. Disini prisma juga akan menyimpan file migrations yang berisi perintah SQL didalam folder prisma/migrations.

  • Execute the migration: Prisma akan melakukan proses migrasi dengan dari file migrations ke dalam bentuk tabel didalam database.

  • Generate Prisma Client: Prisma akan menggenerate Prisma Client berdasarkan dari schema yang terakhir. Kamu bisa melihatnya @prisma/client library dalam package.json. Prisma Client adalah sebuah library yang menggunakan typescript yang bertugas untuk menggenerate otomatis dari prisma schema disamping itu aplikasi bisa berkomunikasi dengan database menggunakan ini seperti operasi penambahan, update, delete dan proses pencarian.

    Note: Untuk lebih jelasnya, kalian bisa mengunjungi dokumentasi prisma client disini.

Jika proses migrasi berhasil, output nya kurang lebih seperti ini

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20220528101323_init_article_table/
    └─ migration.sql

Your database is now in sync with your schema.
...
✔ Generated Prisma Client (3.14.0 | library) to ./node_modules/@prisma/client in 31ms

Kemudian kalian juga bisa cek, prisma sudah membuat sebuah script SQL yaitu membuat tabel article.

-- prisma/migrations/20220528101323_init/migration.sql

-- CreateTable
CREATE TABLE "Article" (
    "id" SERIAL NOT NULL,
    "title" TEXT NOT NULL,
    "description" TEXT,
    "body" TEXT NOT NULL,
    "published" BOOLEAN NOT NULL DEFAULT false,
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" TIMESTAMP(3) NOT NULL,

    CONSTRAINT "Article_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Article_title_key" ON "Article"("title");

Membuat Prisma Service

Didalam aplikasi NestJS, adalah sebuah best practice yang bagus untuk mengabstraksi prisma client API. Untuk melakukan hal tersebut, kamu bisa membuat sebuah service dengan nama Prisma Service yang bertugas untuk menginisiasi Prisma Client dan menjembatani antara aplikasi dan database. NestJS memberikan langkah yang mudah untuk membuat prisma service, ketikan command berikut

npx nest generate module prisma
npx nest generate service prisma

Command diatas akan membuat subdirektori baru ./src/prisma berisi file prisma.module.ts dan prisma.service.ts .

Update file prisma.service.ts menjadi seperti ini

// src/prisma/prisma.service.ts

import { INestApplication, Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient {
  async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close();
    });
  }
}

function enableShutdownHooks memastikan aplikasi dapat menjalankan gracefuly shutdown.

Note: Gracefuly shutdown adalah sebuah mekanisme yang memperbolehkan kita untuk menjalankan beberapa prosedur sebelum prosedur shutdown dijalankan,agar aplikasi atau sistem dapat meminimalisir hilangnya proses ditengah jalan

Prisma Module bertanggung jawab untuk membuat singleton dari prisma service dan mengijinkan untuk mensharing prisma service ke setiap modul didalam aplikasi. Untuk melakukannya, kamu tinggal menambahan PrismaService ke dalam exports array didalam file prisma.module.ts .

// src/prisma/prisma.module.ts

import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}

Sekarang, setiap modul apapun dalam aplikasi, dapat menggunakan PrismaService dan dapat diinject ke dalam setiap component / services. Ini adalah common patter dalam framework NestJS. Setup Prisma sudah beres, hal selanjut nya adalah setup swagger untuk API Dokumentasi.

Setup Swagger API Doc

Swagger adalah sebuah tool API Documentation yang menggunakan OpenAPI Spesification. Beruntungnya, NestJS sudah membungkus itu dalam package miliknya. Untuk dapat menginstallnya gunakan perintah ini

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

Sekarang buka file main.ts dan inisiasi Swagger menggunakan SwaggerModule

// src/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 config = new DocumentBuilder()
    .setTitle('Median')
    .setDescription('The Median API description')
    .setVersion('0.1')
    .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

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

Selama aplikasi berjalan, kemudian buka alamat ini localhost:3000/api. kamu akan melihat tampilan swagger sudah bisa digunakan.

image.png

CRUD Article

Dalam sesi ini, kita akan membuat fitur create, read, update, delete untuk resource Article.

Generate REST Resource

NestJS sangat memudahkan developer untuk membuat resource menggunakan REST salah satunya. Menggunakan Nest CLI berikut maka Anda akan diminta untuk mengisi beberapa data.

npx nest generate resource

CLI Prompt akan muncul dan Anda akan diminta untuk memilih.

  1. What name would you like to use for this resource (plural, e.g., "users")? articles
  2. What transport layer do you use? REST API
  3. Would you like to generate CRUD entry points? Yes

Kamu juga akan menemukan sebuah direktori baru src/articles dengan semua file yang kamu butuhkan untuk membangun REST endpoint. Didalam file src/articles/articles.controller.ts berisi route handler yang menampung request dan meneruskan response dari beberapa endpoint yang berbeda. Sedangkan file src/articles/articles.service.ts yang nantinya akan kita isi dengan logic dari endpoint articles ini.

Jika kamu membuka halaman Swagger API kembali, maka resource articles akan muncul.

image.png

SwaggerModule membaca semua decorator @Body(), @Query() dan @Param() didalam controller untuk menggenerate halaman API.

Menambahkan PrismaModule ke dalam Articles module

Untuk bisa menggunakan PrismaClient didalam module Article, maka kamu harus menambahkan PrismaModule dibagian imports pada file ArticleModule.

// src/articles/articles.module.ts

import { Module } from '@nestjs/common';
import { ArticlesService } from './articles.service';
import { ArticlesController } from './articles.controller';
import { PrismaModule } from 'src/prisma/prisma.module';

@Module({
  controllers: [ArticlesController],
  providers: [ArticlesService],
  imports: [PrismaModule],
})
export class ArticlesModule {}

Setelahnya Anda baru bisa menginject PrismaService didalam ArticlesService.

// src/articles/articles.service.ts

import { Injectable } from '@nestjs/common';
import { CreateArticleDto } from './dto/create-article.dto';
import { UpdateArticleDto } from './dto/update-article.dto';
import { PrismaService } from 'src/prisma/prisma.service';

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

  // CRUD operations
}

Membuat endpoint GET /articles

Masih dalam file src/articles/articles.controller.ts dalam method findAll() akan mengembalikan semua articles yang ada didatabase.

// src/articles/articles.controller.ts

@Get()
findAll() {
  return this.articlesService.findAll();
}

Kamu juga harus mengupdate file ArticlesService.findAll() supaya bisa mengambil data dari database dan diteruskan ke controller.

// src/articles/articles.service.ts

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

  create(createArticleDto: CreateArticleDto) {
    return 'This action adds a new article';
  }

  findAll() {
    return this.prisma.article.findMany({ where: { published: true } });
  }

findMany query akan mengembalikan semua record yang ada didatabase yang sesuai dengan kondisi where yang diberikan.

Sekarang kamu bisa mengetest endpoint tersebut, akses localhost:3000/api dan klik pada bagian menu dropdown GET/articles. Tekan try it out kemudian execute

image.png

Note: kamu juga bisa mengetest nya menggunakan postman atau insomnia tools.

Membuat endpoint GET /articles/drafts

Kamu akan membuat sebuah endpoint yang mengembalikan semua article yang belum dipublish (draft). Untuk part ini, NestJS tidak akan membuatkannya secara otomatis, dan harus harus menulis nya manual.'

// src/articles/articles.controller.ts

@Controller('articles')
export class ArticlesController {
  constructor(private readonly articlesService: ArticlesService) {}

    ...

    @Get('drafts')
    findDrafts() {
      return this.articlesService.findDrafts();
    }

}

Editormu akan menampilkan pesan error karena function this.articlesService.findDrafts() tidak ada. Untuk itu buka file article service dan tambahkan function findDrafts().

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

   ...

  findDrafts() {

    return this.prisma.article.findMany({ where: { published: false } });

  }

}

Selanjutnya kamu bisa mencoba sendiri di Swagger API Doc.

Membuat endpoint GET /articles/:id

Controller yang digunakan untuk menghandle ini adalah

// src/articles/articles.controller.ts

@Get(':id')
findOne(@Param('id') id: string) {
  return this.articlesService.findOne(+id);
}

route ini menerima sebuah id dinamis sebagai parameter, yang nantinya akan di passing ke function findOne(). Namun dalam function findOne(), id nya harus menggunakan tipe number, untuk itu sebelum di passing ke function nya, id ditambahkan tanda plus (+ ) yang berfungsi merubah (casting) dari string ke number.

kemudian tambahkan function findOne() didalam article service

findOne(id: number) {
    return this.prisma.article.findUnique({ where: { id } });
  }

Kalian bisa mengetest menggunakan swagger doc dengan mengakses endpoint GET /articles/{id}

image.png

Membuat endpoint POST /articles

Sesuai judulnya, endpoint ini digunakan untuk membuat article baru.

// src/articles/articles.controller.ts

@Post()
create(@Body() createArticleDto: CreateArticleDto) {
  return this.articlesService.create(createArticleDto);
}

perlu diperhatikan bahwa endpoint ini memerlukan class CreateArticleDto sebagai payloadnya. Untuk itu buatlah sebuah class kosong dengan nama CreateArticleDto.

// src/articles/dto/create-article.dto.ts

import { ApiProperty } from '@nestjs/swagger';

export class CreateArticleDto {
  @ApiProperty()
  title: string;

  @ApiProperty({ required: false })
  description?: string;

  @ApiProperty()
  body: string;

  @ApiProperty({ required: false, default: false })
  published?: boolean = false;
}

Decorator @ApiProperty berfungsi untuk membuat variable-variable yang ada dalam sebuah class bisa bersifat visible dan dibaca oleh SwaggerModule. Untuk lebih jelasnya kalian bisa baca lansung didokumentasinya.

image.png

Selanjutnya update article service dan tambahkan update function baru menjadi seperti ini

// src/articles/articles.service.ts

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

  ...

  create(createArticleDto: CreateArticleDto) {
    return this.prisma.article.create({ data: createArticleDto });
  }

  // ...
}

Membuat endpoint PATC /articles/:id

Endpoint ini digunakan untuk mengupdate data article yang sudah tersimpan didatabase. Endpoint ini seperti dibawah ini

// src/articles/articles.controller.ts

@Patch(':id')
update(@Param('id') id: string, @Body() updateArticleDto: UpdateArticleDto) {
  return this.articlesService.update(+id, updateArticleDto);
}

endpoint ini menggunakan updateArticleDto sebagai payloadnya dan updateArticleDto itu adalah PartialType dari CreateArticleDto sehingga updateArticleDto dapat mengakses semua properti yang ada di CreateArticleDto;

// src/articles/dto/update-article.dto.ts

import { PartialType } from '@nestjs/swagger';
import { CreateArticleDto } from './create-article.dto';

export class UpdateArticleDto extends PartialType(CreateArticleDto) {}

Seperti sebelumnya, kamu harus mengupdate article service untuk operasi update ini

// src/articles/articles.service.ts

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

  // ...

  update(id: number, updateArticleDto: UpdateArticleDto) {

    return this.prisma.article.update({

      where: { id },

      data: updateArticleDto,

    });

  }

  // ...
}

Operasi article.update akan memulai mencari article dengan id yang diberikan dan data akan diupdate berdasarkan data yang diberikan dari updateArticleDto.

Jika tidak ada article yang dicari berdasarkan id, maka Prisma akan memunculkan error.

Membuat endpoint DELETE /articles/:id

Seperti langkah sebelumnya, kamu akan edit article service di function delete seperti ini

// src/articles/articles.service.ts

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

  // ...

  remove(id: number) {
    return this.prisma.article.delete({ where: { id } });
  }
}

Selamat ini adalah langkah terakhir, jika kalian mengikuti article ini berarti kalian sudah mendapatkan pengetahuan basic mengenai NestJS. Step berikut nya kita akan coba membuat fungsi authentication dan authorizationnya. Tetap standby di blog ini yaa sahabat coding .... 🎉🎉🎉

Sumber

prisma.io/blog/nestjs-prisma-rest-api-7D056.. docs.nestjs.com

Did you find this article valuable?

Support Teten Nugraha by becoming a sponsor. Any amount is appreciated!