Setup JWT using NestJS , Prisma & MySQL Database
JWT atau Json Web Token adalah slah satu metode authentikasi yang saat ini banyak digunakan untuk bermacam skema seperti Mobile, Website, Desktop karena kemudahan dan keamananya.
pada tutorial kali ini, saya akan membagikan cara mudah membuat JWT menggunakan Framework NestJS dan Prisma sebagai databasenya. kita juga menggunakan OpenAPI untuk dokumentasi API nya.
Install NestJS
Pertama kita Install NestJS terlebih dahulu menggunakan Nest CLI untuk menginstall Nest CLI bisa menggunakan NPM dengan perintah
npm i -g @nestjs/cli
setelah Nest CLI terinstall, selanjutnya kita buat project NestJS dengan perintah
nest new example_nest_jwt
example_nest_jwt adalah nama project yang kita buat, untuk penamaan bisa dengan nama apa saja atau bebas.
pilih npm atau yarn jika kalian biasa menggunakan yarn lalu enter dan tunggu sampai proses instalasi selesai
kemudian masuk ke dalam project
cd example_nest_jwt/
Buka project menggunakan Text Editor seperti Visual Studio Code
Siapkan Database
buat database dengan nama sama persis dengan project contohnya “example_nest_jwt”
Setup Prisma
Install prisma dengan perintah
yarn add prisma -D
yarn add @prisma/client
atau
npm install prisma -D
npm install @prisma/client
kemudian jalankan perintah
npx prisma
npx prisma init
setelah melakukan perintah diatas maka akan terbentuk folder prisma dan file .env
edit file .env dan sesuaikan dengan Database MySQL yang sudah kita persiapkan sebelumnya
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="mysql://root:mysql@localhost:3307/example_nest_jwt"
edit koneksi file “prisma/schema.prisma” ubah file provider menjadi “mysql”
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
kemudian buat skema table User di dalam file “prisma/shcema.prisma”
model users {
id Int @id @default(autoincrement())
name String @db.VarChar(45)
email String @unique(map: "email_UNIQUE") @db.VarChar(45)
password String @db.VarChar(200)
created_at DateTime? @db.DateTime(0)
updated_at DateTime? @db.DateTime(0)
}
lebih lengkapnya seperti
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model users {
id Int @id @default(autoincrement())
name String @db.VarChar(45)
email String @unique(map: "email_UNIQUE") @db.VarChar(45)
password String @db.VarChar(200)
created_at DateTime? @db.DateTime(0)
updated_at DateTime? @db.DateTime(0)
}
kemudian jalankan perintah
yarn prisma db push
atau
npx prisma db push
perintah di atas adalah untuk menjalankan migration dari prisma membuat table dengan nama “users”. kalian bisa cek di dalam database “example_nest_jwt” yang sudah kita buat sebelumnya
kemudian jalankan perintah
yarn prisma generate
atau
npx prisma generate
kemudian test jalankan NestJS dengan perintah
yarn start:dev
atau
npm run start:dev
jika tidak ada error berarti kita sudah berhasil menginstall NestJS dan Prisma dengan benar.
kemudian kita kan membuat module dan service Prisma
masukan perintah
yarn nest g mo prisma
yarn nest g s prisma
atau
nest g mo prisma
nest g s prisma
setelah perintah di atas, akan terbentuk module baru dengan nama prisma
kemudian edit file “src/prisma/prisma.module.ts” mewnjadi seperti berikut
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports:[PrismaService]
})
export class PrismaModule {}
kemudian edit file “src/prisma/prisma.service.ts” dengan code seperti berikut
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient
implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
Setup JWT
selanjutnya adalah setup JWT
Install JWT dengan perintah
yarn add @nestjs/passport passport passport-jwt
yarn add @types/passport-jwt -D
yarn add @nestjs/jwt
atau
npm install @nestjs/passport passport passport-local
npm install @types/passport-jwt -D
npm install @nestjs/jwt
buat file “src/jwt.config.ts” yang akan menyimpan informasi JWT seperti Token dan Expired time
export const JwtConfig = {
user_secret: "7Myh9rN0y9PCrFYMVeuZCiGDLsISWkezBMI7adli877=",
user_expired: '1000s',
};
Buat Module Authentication
setelah menginstall JWT selanjutanya kita akan buat module authentikasi dengan struktur “src/auth”
masukan perintah
yarn nest g mo auth
atau
nest g mo auth
buat file di dalam “src/auth/dto/login.dto”
export class LoginDto {
email: string;
password: string;
}
buat file di dalam “src/auth/dto/register.dto”
import { Prisma } from '@prisma/client';
export class RegisterDto implements Prisma.usersCreateInput {
name: string;
email: string;
password: string;
}
buat auth service, controller dengan perintah
yarn nest g s auth
yarn nest g co auth
atau
nest g s auth
nest g co auth
buat JWT Strategy “src/auth/jwt.strategy.ts”
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { JwtConfig } from 'src/jwt.config';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy,'jwt-user') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: JwtConfig.user_secret
});
}
async validate(payload: any) {
return {
user_id: payload.sub,
email: payload.email,
expired: payload.exp
};
}
}
kemudian buat guard JWT “src/auth/jwt-auth.guard.ts” atau dengan perintah
yarn nest g gu auth/jwt-auth
atau
nest g gu auth/jwt-auth
edit file “src/auth/jwt-auth.guard.ts” menjadi seperti berikut
import {
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
export class JwtAuthGuard extends AuthGuard('jwt-user') {
canActivate(context: ExecutionContext) {
// Add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
return super.canActivate(context);
}
handleRequest(err, user, info) {
// You can throw an exception based on either "info" or "err" arguments
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}
kemudian edit file “src/auth/auth.module.ts” menjadi
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';
import { JwtConfig } from 'src/jwt.config';
@Module({
imports: [
PassportModule.register({
defaultStrategy: 'jwt',
property: 'user',
session: false,
}),
JwtModule.register({
secret: JwtConfig.user_secret,
signOptions: {
expiresIn: JwtConfig.user_expired,
},
}),
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController]
})
export class AuthModule { }
Setup Auth Service, Controller
install bycript untuk mengenkripsi password kita
yarn add bcrypt
yarn add @types/bcrypt -D
atau
npm install bcrypt
npm install @types/bcrypt -D
buat Pipe untuk merubah password menjadi hash dengan bycript
buat file “src/auth/tranform-password.pipe.ts”
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
import { hash } from 'bcrypt';
@Injectable()
export class TransformPasswordPipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
value.password = await hash(value.password, 12);
return value;
}
}
edit file “src/auth/auth.service.ts” menjadi seperti berikut
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from 'src/prisma/prisma.service';
import { LoginDto } from './dto/login.dto';
import { omit } from 'lodash';
import { compare } from 'bcrypt';
import { JwtConfig } from 'src/jwt.config';
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService, private dbService: PrismaService) { }
/**
* Register Service
* @param dto
* @returns
*/
async register(dto: any) {
let user = await this.dbService.users.findFirst({
where: {
email: dto.email
}
});
if (user) {
throw new HttpException('User Exists', HttpStatus.BAD_REQUEST);
}
let createUser = await this.dbService.users.create({
data: dto
})
if (createUser) {
return {
statusCode: 200,
message: 'Register success',
};
}
throw new HttpException('Bad request', HttpStatus.BAD_REQUEST);
}
/**
* Login Service
* @param dto
* @returns
*/
async login(dto: LoginDto) {
let user = await this.dbService.users.findFirst({
where: { email: dto.email }
});
if (!user) {
throw new HttpException('User not found', HttpStatus.NOT_FOUND);
}
let checkPassword = await compare(dto.password, user.password);
if (!checkPassword) {
throw new HttpException('Credential Incorrect', HttpStatus.UNAUTHORIZED);
}
return await this.generateJwt(user.id, user.email, user, JwtConfig.user_secret, JwtConfig.user_expired);
}
/**
* Generate JWT
* @param userId
* @param email
* @param user
* @param secret
* @param expired
* @returns
*/
async generateJwt(userId: any, email: string, user: any, secret: any, expired = JwtConfig.user_expired) {
let accessToken = await this.jwtService.sign({
sub: userId,
email,
name: user.first_name + ' ' + user.last_name
}, {
expiresIn: expired,
secret
});
return {
statusCode: 200,
accessToken: accessToken,
user: omit(user, ['password','created_at','updated_at'])
};
}
}
install class validator
yarn add class-validator
yarn add class-transformer
atau
npm install class-validator
npm install class-transformer
kemudian edit file controller “src/auth/auth.controller.ts”
import { Body, Controller, Get, HttpCode, Post, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import { JwtAuthGuard } from './jwt-auth.guard';
import { TransformPasswordPipe } from './transform-password.pipe';
@Controller('auth')
export class AuthController {
/**
* Constructor
* @param authService
*/
constructor(private authService: AuthService) {
}
/**
* Register controller
* @param dto
* @returns
*/
@UsePipes(ValidationPipe, TransformPasswordPipe)
@HttpCode(200)
@Post('register')
async register(@Body() dto: RegisterDto) {
return await this.authService.register(dto);
}
/**
* Login Controller
* @param dto
* @returns
*/
@HttpCode(200)
@Post('login')
async login(@Body() dto: LoginDto) {
return await this.authService.login(dto);
}
/**
* Get detail User
*/
@UseGuards(JwtAuthGuard)
@Get('profile')
async profile() {
return {
message: "Profile"
}
}
}
Selamat kita sudah bisa melakukan register, login dengan Respon JWT
Setup OpenAPI
Untuk mengetes API kita akan menggunakan OpenAPI
pertama adalah install OpenAPI
yarn add @nestjs/swagger swagger-ui-express
atau
npm install --save @nestjs/swagger swagger-ui-express
kemudian edit file “src/main.ts”
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('Open API Test Dansmultipro')
.setDescription('API Test Dansmultipro')
.setVersion('1.0')
.addTag('dansmultipro')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('openapi', app, document);
const cors = {
origin: [
'http://localhost:3000',
'http://localhost',
'*',
],
methods: 'GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS',
preflightContinue: false,
optionsSuccessStatus: 204,
credentials: true,
allowedHeaders: ['*'],
};
app.enableCors(cors);
await app.listen(3000);
}
bootstrap();
edit file “src/auth/dto/login.dto.ts”
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty } from 'class-validator';
export class LoginDto {
@ApiProperty()
@IsNotEmpty()
email: string;
@ApiProperty()
@IsNotEmpty()
password: string;
}
edit file “src/auth/register.dto.ts”
import { ApiProperty } from '@nestjs/swagger';
import { Prisma } from '@prisma/client';
import { IsNotEmpty } from 'class-validator';
export class RegisterDto implements Prisma.usersCreateInput {
@ApiProperty()
@IsNotEmpty()
name: string;
@ApiProperty()
@IsNotEmpty()
email: string;
@ApiProperty()
@IsNotEmpty()
password: string;
}
lalu edit file “src/auth/auth.controller.ts”
import { Body, Controller, Get, HttpCode, Post, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import { JwtAuthGuard } from './jwt-auth.guard';
import { TransformPasswordPipe } from './transform-password.pipe';
@ApiTags('Auth')
@Controller('auth')
export class AuthController {
/**
* Constructor
* @param authService
*/
constructor(private authService: AuthService) {
}
/**
* Register controller
* @param dto
* @returns
*/
@UsePipes(ValidationPipe, TransformPasswordPipe)
@HttpCode(200)
@Post('register')
async register(@Body() dto: RegisterDto) {
return await this.authService.register(dto);
}
/**
* Login Controller
* @param dto
* @returns
*/
@HttpCode(200)
@Post('login')
async login(@Body() dto: LoginDto) {
return await this.authService.login(dto);
}
/**
* Get detail User
*/
@UseGuards(JwtAuthGuard)
@Get('profile')
async profile() {
return {
message: "Profile"
}
}
}
Open API berhasil di install dan di tambahkan ke auth.controller, selanjutanya kita akan test OpenAPI dengan membuka browser http://localhost:3000/openapi
selanjutnya kita bisa mencoba register menggunakan try out yang ada di openapi
Response sukses jika berhasil
Selajutnya kita akan coba login
Selamat!! kita sudah berhasil membuat API Register,Login dengan JWT.\
Untuk menggunakan Token JWT kalian bisa menambahkan di dalam Header request
contoh:
Authtehntication: “Bearer TOKEN_JWT”
Repository: