Transfer Data Antar Service Menggunakan gRPC Node.js

Source: https://unsplash.com/@blakeconnally

Sebelumnya kita telah membahas cara membuat microservices grpc dengan nodejs. Kita juga telah mengetahui cara integrasi service dengan framework express.js.

Pada artikel kali ini, kita akan membahas transfer data dari service satu ke service lain melalui protokol gRPC.

Preparation

Project sebelumnya, kita namakan sebagai service utility. Sedangkan project pada artikel ini kita namakan sebagai service user.

Service user membutuhkan data catatan yang diambil dari service utility. Untuk meminta datanya, service user melakukan panggilan (call) ke service utility dengan gRPC.

Service user sendiri akan berbentuk seperti ini:

.
├── app.js
├── controllers
│   └── user.controller.js
├── grpc
│   ├── index.js
│   └── notes
│       └── client
│           ├── get-notes.grpc.js
│           └── index.js
├── index.js
├── node_modules
├── package.json
├── package-lock.json
├── proto
│   └── notes.proto
├── routes
│   └── index.js
└── utils
    └── response.js

Sedangkan service utility seperti ini:

.
├── app.js
├── grpc
│   ├── index.js
│   └── notes
│       └── index.js
├── index.js
├── package.json
├── package-lock.json
├── proto
│   └── notes.proto
└── routes
    └── index.js

Keseluruhan struktur project seperti ini:

.
├── service-user
│   ├── app.js
│   ├── controllers
│   │   └── user.controller.js
│   ├── grpc
│   │   ├── index.js
│   │   └── notes
│   │       └── client
│   │           ├── get-notes.grpc.js
│   │           └── index.js
│   ├── index.js
│   ├── package.json
│   ├── package-lock.json
│   ├── proto
│   │   └── notes.proto
│   ├── routes
│   │   └── index.js
│   └── utils
│       └── response.js
└── service-utility
    ├── app.js
    ├── grpc
    │   ├── index.js
    │   └── notes
    │       └── index.js
    ├── index.js
    ├── package.json
    ├── package-lock.json
    ├── proto
    │   └── notes.proto
    └── routes
        └── index.js

Saya telah membuat repositorynya di sini:

icon-software
gRPC-Node-User
github-icon

User service of gRPC Microservices with Node.js

icon-software
gRPC-Node-Utility
github-icon

Utility service of gRPC Microservices with Node.js

icon-software
gRPC-Node-Microservices
github-icon

gRPC Microservices with Node.js

Init Project

Oke, pada artikel ini kita akan membuat service user, sehingga kita perlu setup beberapa dependensi yang diperlukan.

mkdir service-user && cd service-user
npm init -y
npm i grpc @grpc/proto-loader express

Setup Proto File

Pada dasarnya proto file baik untuk server maupun client adalah sama. Sehingga harus disamakan isi file dari service-utility/proto/notes.proto dengan service-user/proto/notes.proto:

proto/notes.proto
syntax = "proto3";
package notes;

service NoteService {
  rpc List (Empty) returns (NoteList) {}
}

message Empty {}

message Note {
  string id = 1;
  string title = 2;
  string content = 3;
}

message NoteList {
  repeated Note notes = 1;
}

Pada proto file di atas, kita memiliki sebuah method pada gRPC server bernama List. List ini memiliki parameter Empty yaitu kosong, dan return NoteList yaitu repeated Note atau array of Note.

Setup gRPC

Pada direktori grpc, buatlah file index.js. Fungsi dari file ini yaitu membaca semua proto file yang ada dan menjadikannya sebagai package definition sehingga RPC tinggal memakainya saja.

Dengan begitu ketika ada bug yang berhubungan dengan pembacaan proto file ataupun package definition, kita hanya akan fokus ke file ini, tidak satu per satu service yang di cek.

grpc/index.js
const path = require('path')
const grpc = require('grpc')
const protoLoader = require('@grpc/proto-loader')

const notesProtoPath = path.join(__dirname, '..', 'proto', 'notes.proto')
const notesProtoDefinition = protoLoader.loadSync(notesProtoPath)

const notesPackageDefinition = grpc.loadPackageDefinition(notesProtoDefinition).notes

module.exports = {
  notesPackageDefinition
}

Kemudian buatlah folder notes di dalam folder grpc. Folder grpc nantinya akan berisi beberapa sub folder sesuai dengan module yang akan di consume.

grpc/notes/client/index.js
const grpc = require('grpc')
const { notesPackageDefinition } = require('../..')

const client = new notesPackageDefinition.NoteService(
  '0.0.0.0:50051', grpc.credentials.createInsecure()
)

module.exports = client

Pada kode di atas, kita mengekspor client yang mana mengonsumsi server grpc dengan alamat 0.0.0.0:50051.

Selanjutnya buat method untuk mengambil notes:

grpc/notes/client/get-notes.grpc.js
const client = require('.')

const getNotes = async () => {
  try {
    const notes = await new Promise((resolve, reject) => {
      client.list({}, (error, notes) => {
        if (!error) resolve(notes)
        reject(error)
      })
    })

    return notes
  } catch (error) {
    throw error
  }
}

module.exports = getNotes

Method tersebut kita export agar bisa digunakan di banyak tempat.

Setup Express

Selanjutnya kita setup express server untuk service user ini. Seperti biasanya pada root project, buatlah file app.js. File ini akan berisi semua konfigurasi express app secara global.

app.js
const express = require('express')
const routes = require('./routes')

const app = express()

app.use('/', routes)

module.exports = app

Selanjutnya pada routes/index.js, masukan seluruh controller dari express app ini.

routes/index.js
const { Router } = require('express')
const userController = require('../controllers/user.controller')

const router = Router()

router.get('/user/notes', userController.userNotes)

module.exports = router

Pada kode diatas kita membuat sebuah endpoint dengan method GET /user/notes yang akan di handle oleh user controller

controllers/user.controller.js
const getNotes = require('../grpc/notes/client/get-notes.grpc')
const { resError, resSuccess } = require('../utils/response')

exports.userNotes = async (req, res, next) => {
  try {
    const { notes } = await getNotes()

    return resSuccess(res, notes, 'Success get all user notes')
  } catch (error) {
    return resError(res, 500, [{
      flag: 'INTERNAL_SERVER_ERROR',
      message: 'Failed get all note'
    }])
  }
}

Pada user controller, kita menggunakan method getNotes yang sebelumnya telah kita buat, untuk mengambil semua notes yang ada pada service utility.

Apabila tidak terdapat error, maka akan mereturn success namun ketika gagal akan mereturn error.

utils/response.js
const success = (res, result, message) => {
  res.status(200).json({
    status: 200,
    message,
    result
  })
}

const error = (res, code, errors) => {
  res.status(code).json({
    status: code,
    errors
  })
}

module.exports = {
  resSuccess: success,
  resError: error
}

Kemudian pada file index.js kita listen express app ke dalam port 5050.

index.js
const app = require('./app')

app.listen(5050, () => {
  console.log(`App running on port: 5050`)
})

Setting Package.json

Selanjutnya, kita buat script untuk menjalankan express app ini. Karena main projectnya adalah index.js, maka kita jalankan aplikasi ini dengan perintah node index.js

package.json
{
  ...
  "main": "index.js",
  "scripts": {
    "start": "node ."
    ...
  },
  ...
}

Untuk menjalankannya, di terminal masukan perintah npm run start

npm run start

> [email protected] start /service-user
> node .

App running on port: 5050

Testing

Jangan lupa jalankan kedua service, baik service user maupun service utility. Kemudian buka url localhost:5050/user/notes untuk melihat hasil dari microservices ini.

{
  "status": 200,
  "message": "Success get all user notes",
  "result": [
    {
      "id": "1",
      "title": "Note 1",
      "content": "Content 1"
    },
    {
      "id": "2",
      "title": "Note 2",
      "content": "Content 2"
    }
  ]
}

Pada hasil di atas, kita telah berhasil mengambil data dari service utility melalui service user menggunakan protokol gRPC.

Penutup

Demikian cara transfer data antar service menggunakan grpc pada node js. Jika membutuhkan referensi bacaan, silakan baca artikel sebelumnya mengenai cara membuat microservices gRPC dengan nodejs dan integrasi gRPC server dengan expressjs