วันนี้เราจะมาลองใช้ gRPC ทำระบบ CRUD ง่าย ๆ บน Node.js กันครับ เพื่อให้เข้าใจว่า gRPC ทำงานอย่างไร ให้เพื่อน ๆ สามารถนำไปต่อยอดกับงานของตัวเองได้ง่าย ๆ กัน

gRPC คืออะไร?
gRPC คือเฟรมเวิร์กสำหรับสร้าง API ที่พัฒนาโดย Google โดยหลักการทำงานของ gRPC จะไม่ใช้การส่งข้อมูลแบบ JSON เหมือน REST API แต่จะใช้ Protocol Buffers (Protobuf) ซึ่งมีขนาดข้อมูลเล็กกว่า และยังใช้ HTTP/2 ที่เร็วกว่า HTTP/1.1 ของ REST API อีกด้วย จึงทำให้การส่ง Request – Response แต่ละครั้งมี Latency ต่ำ และทำงานได้รวดเร็วขึ้น
จุดเด่นของ HTTP/2 ที่ gRPC ใช้
- HTTP/2 รองรับ multiplexing → สามาถส่งไปหลาย Requests พร้อมกันช่วยลดเวลาในการรอแต่ละ requests
- ต่างจาก HTTP/1.1 ที่ใช้ใน REST API ทั่วไป → ต้องส่งข้อมูลทีละไฟล์ถ้าส่งไฟล์เยอะ ๆ ก็จะเสียเวลาในการส่งเยอะมาก ๆ
- เหมาะกับระบบใหญ่ ๆ ที่มี Service หลายตัว → ด้วย HTTP/2 จะทำให้เราส่งข้อมูลระหว่าง Service แต่ละตัวไปมาได้เร็วขึ้น

Protobuf (Protocol Buffers) คืออะไร?
Protobuf เป็น IDL (Interface Definition Language) ที่ gRPC ใช้กำหนดรูปแบบข้อมูลของ API ว่าแต่ละ endpoint ต้องรับ-ส่งข้อมูลชนิดใด ข้อดีของ Protobuf คือสามารถ serialize ข้อมูลให้อยู่ในรูป binary ทำให้ขนาดเล็กกว่า JSON ส่งข้อมูลได้เร็วและประหยัด bandwidth มากขึ้น

ที่มาของภาพ : https://medium.com/@business.agency.koloc/demystifying-protobuf-a-detailed-guide-wallarm-42ef59d15a08
ตัวอย่างการใช้งาน gRPC กับ Microservices
ตัวอย่างที่ gRPC ทำได้ดี เช่น การสื่อสารระหว่าง Backend Service หรือการใช้กับ API Gateway ที่มีข้อมูลวิ่งไปมาปริมาณมาก และต้องการความเร็วสูง

เรามาเริ่มติดตั้ง Project กันเลย
npm init -y
npm install @grpc/grpc-js @grpc/proto-loader
JavaScriptสร้างไฟล์ proto กัน
สร้างโฟลเดอร์ proto/
และไฟล์ book.proto
เพื่อเขียนสเปค API
syntax = "proto3";
package book;
message bookInfo {
int32 bookId = 1;
string bookName = 2;
string author = 3;
}
message buyingDone {
string message = 1;
}
service testBook {
// เปลี่ยน method เป็น TestBook
rpc TestBook (bookInfo) returns (buyingDone);
}
JavaScriptต่อมาเราจะมาสร้างไฟล์ service.js ที่จะเป็นตัวจัดการฟังก์ชัน API ของเราก่อนส่งไปให้ index.js เรียกใช้
function TestBook(call, callback) {
const { bookId, bookName, author } = call.request;
const message = `📚 Book ID ${bookId}: "${bookName}" by ${author} is now purchased!`;
callback(null, { message });
}
module.exports = {
TestBook,
};
JavaScriptจากนั้นเราจะมาสร้าง Server ของในไฟล์ index.js
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');
const bookService = require('./service');
const PROTO_PATH = path.join(__dirname, 'proto/book.proto');
// โหลด proto แบบ dynamic
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const proto = grpc.loadPackageDefinition(packageDefinition).user;
const server = new grpc.Server();
// map method SayHello ไปที่ฟังก์ชัน bookService.TestBook
server.addService(proto.testBook.service, {
TestBook: bookService.TestBook,
});
const PORT = '0.0.0.0:50051';
server.bindAsync(PORT, grpc.ServerCredentials.createInsecure(), (err, port) => {
if (err) {
console.error(err);
return;
}
console.log(`📘 gRPC Book Server running at ${PORT}`);
});
JavaScriptโครงสร้าง Project
project/
├── node_modules/
├── proto/
│ └── book.proto ← ไฟล์ proto
├── service.js ← จัดการ logic CRUD แยกไว้
├── index.js ← ไฟล์เริ่ม gRPC server
├── package.json
├── package-lock.json
JavaScriptจากนั้นเราจะลองมาทดสอบยิง API ผ่าน Postman กันครับว่า API ของเราทำงานได้หรือเปล่า
node index.js
JavaScriptทดสอบ API ของเราด้วย Postman
เปิด Postman → กด New → เลือก gRPC

จากนั้นใส่ port เป็น localhost:50051

อัปโหลดไฟล์ book.proto

เลือก Method: TestBook ใส่ข้อมูล แล้วกด Invoke ได้เลย

Implement CRUD
ทำการเขียน API เส้นอื่น ๆ เข้าไปในไฟล์ .Proto
syntax = "proto3";
package book;
// Message ข้อมูลหนังสือ
message Book {
int32 book_id = 1;
string book_name = 2;
string author = 3;
}
// Request/Response: Create
message CreateBookRequest {
Book book = 1;
}
message CreateBookResponse {
Book book = 1;
}
// Request/Response: Get
message GetBookRequest {
int32 book_id = 1;
}
message GetBookResponse {
Book book = 1;
}
// Request/Response: Update
message UpdateBookRequest {
Book book = 1;
}
message UpdateBookResponse {
Book book = 1;
}
// Request/Response: Delete
message DeleteBookRequest {
int32 book_id = 1;
}
message DeleteBookResponse {
bool success = 1;
}
// Response: List
message ListBooksRequest {}
message ListBooksResponse {
repeated Book books = 1;
}
// Service
service BookService {
rpc CreateBook (CreateBookRequest) returns (CreateBookResponse);
rpc GetBook (GetBookRequest) returns (GetBookResponse);
rpc UpdateBook (UpdateBookRequest) returns (UpdateBookResponse);
rpc DeleteBook (DeleteBookRequest) returns (DeleteBookResponse);
rpc ListBooks (ListBooksRequest) returns (ListBooksResponse);
}
JavaScriptเพิ่มฟังก์ชันใน Service.js
let books = [];
let idCounter = 1;
function CreateBook(call, callback) {
const book = call.request.book;
book.book_id = idCounter++;
books.push(book);
callback(null, { book });
}
function GetBook(call, callback) {
const book = books.find(b => b.book_id === call.request.book_id);
book
? callback(null, { book })
: callback({ code: 5, message: "Book not found" });
}
function UpdateBook(call, callback) {
const updatedBook = call.request.book;
const index = books.findIndex(b => b.book_id === updatedBook.book_id);
if (index !== -1) {
books[index] = updatedBook;
callback(null, { book: updatedBook });
} else {
callback({ code: 5, message: "Book not found" });
}
}
function DeleteBook(call, callback) {
const index = books.findIndex(b => b.book_id === call.request.book_id);
if (index !== -1) {
books.splice(index, 1);
callback(null, { success: true });
} else {
callback({ code: 5, message: "Book not found" });
}
}
function ListBooks(call, callback) {
callback(null, { books });
}
module.exports = {
CreateBook,
GetBook,
UpdateBook,
DeleteBook,
ListBooks,
};
JavaScriptเพิ่ม Endpoint index.js
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');
const bookService = require('./service');
const PROTO_PATH = path.join(__dirname, 'proto/book.proto');
const packageDef = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const proto = grpc.loadPackageDefinition(packageDef).book;
const server = new grpc.Server();
server.addService(proto.BookService.service, {
CreateBook: bookService.CreateBook,
GetBook: bookService.GetBook,
UpdateBook: bookService.UpdateBook,
DeleteBook: bookService.DeleteBook,
ListBooks: bookService.ListBooks,
});
const PORT = '0.0.0.0:50051';
server.bindAsync(PORT, grpc.ServerCredentials.createInsecure(), (err, port) => {
if (err) return console.error(err);
console.log(`📚 gRPC Book CRUD Server running at ${PORT}`);
});
JavaScriptทดสอบ CRUD ผ่าน Postman
gRPC ใน Postman จะสามารถเลือก Method ทั้งหมดจาก .proto ที่อัปโหลดไปมาลองทดสอบเรียก CreateBook, GetBook, UpdateBook, DeleteBook, ListBooks กันหน่อย
มาทดสอบกันเริ่มจาก CreateBook

GetBook

UpdateBook

DeleteBook

แล้วสุดท้ายเราจะลอง List รายชื่อหนังสือ โดยที่ก่อน List ให้ลองสร้างรายการหนังสือสักสองรอบดู

🎉 และนี่คือการสร้างระบบ CRUD ด้วย gRPC + Node.js ครบทุกขั้นตอน! เหมาะสำหรับต่อยอดไปใช้กับระบบจริง เช่น Microservices หรือ API Gateway ได้เลยครับผม