Fastify 101 - ทดลองเล่น Fastify เบื้องต้น
วันนี้ลองเล่น และลองอ่าน Overview ตัว Fastify เบื้องต้นดู ว่ามันเป็นยังไง หลังจากได้ยินมาซักพักแล้ว อยากรู้ว่าถ้าเปรียบเทียบกับ Express มันมีความเหมือน หรือต่างกันมากน้อยแค่ไหน และถ้าคนเขียน Express มาก่อน มาเขียน Fastify ต้องเปลี่ยน ต้องปรับอะไรแค่ไหนบ้าง
จาก Benchmark นี้ ตัว Fastify เคลมว่า ตัวเค้าเอง มี request/sec มากที่สุด (จากเว็บของ Fastify นะครับ ซึ่งจะมี bias มั้ย อันนี้ ตัดสินใจเอาเองครับ และก็ผล benchmark มันแค่ route เดียว ที่ Hello World ถ้าเว็บมัน complex มี feature มากขึ้น นอกจากเรื่อง framework แล้ว code quality ก็มีผลเช่นกัน)
- Fastify 58,946 req/sec
- Koa 46,240 req/sec
- Restify 35,207 req/sec
- Hapi 29,391 req/sec
- Express 12,863 req/sec
ถ้าดูจาก benchmark คือ Express แย่สุด แต่ตัว Express ก็เหมือนเป็น framework คู่กับ Node.js ไปแล้ว และ web framework หลังๆ ก็เอารูปแบบการเขียน express หรือบางทีก็เป็น express เวอร์ชั่น lightweight built-in ไปเลยก็มี เช่น Next.js Middleware API, Nest.js เป็นต้น
Fastify
เริ่มแรกเลย เข้าเว็บ Fastify อ่าน Quick Start และลอง Getting Started จะเห็นว่า Hello World เขียนแทบไม่ต่างจาก Express.js เลย
// Require the framework and instantiate it
const fastify = require('fastify')({ logger: true })
// Declare a route
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
// Run the server!
const start = async () => {
try {
await fastify.listen(3000)
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
นอกจากนี้ก็มี CLI ให้เราทำการ generate fastify ได้
npm install -g fastify-cli
สร้างโปรเจ็คด้วย fastify-cli
fastify generate hello-fastify
Routing
การกำหนด Route ใน Fastify ทำได้หลายแบบ แบบแรกคือ
fastify.route(options);
ซึ่ง options
ก็มี properties เช่น
method
- ตาม HTTP Method เลย คือGET',
PUT,
POST,
DELETE,
PATCH,
OPTIONS` เป็นต้นurl
- path ของ Url ที่เราต้องการ match กับ route.schema
- เอาไว้ validate และ serializationhandler(request, resply)
- function ที่เอาไว้ handle request
ตัวอย่าง ประมาณนี้
fastify.route({
method: 'GET',
url: '/',
handler: async (request, reply) => {
return { hello: 'world' };
}
});
หรือแบบใช้ function เป็นชื่อ HTTP Method คล้ายๆ Express ก็ทำได้ เช่น
fastify.get('/', (request, reply) => {});
fastify.post('/', (request, reply) => {});
fastify.get('/:id', (request, reply) => {});
Request
ตัว Request ก็รับค่าไม่ต่างจาก Express เช่น กำหนด route ไว้แบบนี้
fastify.post('/:id', async (request, reply) => {});
รับค่า จาก request payload / request body
request.body;
// { data: 'your data' }
รับค่า Query string เช่น POST /:id?name=test
request.query;
// { name: 'test' }
รับค่าจาก params เช่น POST /1000
request.params;
// { id: 1000 }
รับค่าจาก request headers
request.headers;
// { host: 'localhost:5555', 'user-agent': 'curl/7.79.1', accept: '*/*' }
Reply
Reply ก็เหมือน response
หรือ res
ของ Express เอาไว้กำหนดค่าต่างๆ การส่ง response ไป client เช่น
- กำหนด cookie
set-cookie
reply.header('set-cookie', 'something');
- กำหนด statusCode
reply.statusCode = 400;
- กำหนด header / code / send
reply.code(200).header('Content-Type', 'application/json').send({ hello: 'world' });
- อ่าน Reply เพิ่มเติม Fastify - Reply
Validation
ตัวอย่างการทำ validation ง่ายๆ เช่น เราจะส่ง POST /
ด้วย id
และ username
ซึ่งถ้าส่ง property ไม่ครบ ก็ถูก reject (กำหนด type ให้ properties และกำหนด required
เป็น property ที่ต้องการ)
fastify.route({
method: 'POST',
url: '/',
schema: {
body: {
type: 'object',
required: ['id', 'username'],
properties: {
id: { type: 'number' },
username: { type: 'string' }
}
}
},
handler: (request, reply) => {
return { success: true };
}
});
รายละเอียด Validation เพิ่มเติม Validation-And-Serialization
Plugins
ถ้าหากเราเปรียบเทียบว่า ใน JavaScript ทุกๆอย่างคือ Object ใน Fastify ก็มองทุกๆอย่าง เป็น Plugin เช่นกัน
syntax การ register plugin:
fastify.register(yourPlugin);
ตัวอย่าง Core Plugin ของ Fastify
- fastify-cookie - สำหรับ get/set cookies.
- fastify-cors - เอาไว้กำหนด CORS
- fastify-csrf - เอาไว้กัน CSRF Attack.
- fastify-helmet - เป็น security header ที่ wrap ตัว helmet อีกที
- fastify-jwt - เอาไว้จัดการ JWT sign/verify ต่างๆ
- fastify-mongodb - MongoDB plugin
- fastify-postgres - สำหรับ PostgreSQL
- fastify-rate-limit - จัดการเรื่อง Rate Limit
- fastify-routes - เอาไว้ทำ route แบบกำหนดเป็น map (Array) ได้
TypeScript
ตัว Fastify ก็เขียนด้วย TypeScript ได้ปกติ เพียงแค่ใช้ ts และ types/node
npm i -D typescript @types/node
Initial typescript
npx tsc --init
สร้างไฟล์ main.ts
ขึ้นมา
import fastify from 'fastify';
const server = fastify();
server.get('/ping', async (request, reply) => {
return 'pong\n';
});
server.listen(8080, (err, address) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log(`Server listening at ${address}`);
});
Compile typescript จะได้ไฟล์ main.js
tsc
ทดสอบรัน
node main.js
รองรับ Generic เช่น server.get()
ตัว generic object มี properties ได้ เช่น QueryString,
Headers`
interface IQuerystring {
username: string;
password: string;
}
interface IHeaders {
'h-Custom': string;
}
ส่วน Route เราก็ define แบบ Generic โดยใช้ custom type ได้แบบนี้
server.get<{
Querystring: IQuerystring;
Headers: IHeaders;
}>('/auth', async (request, reply) => {
const { username, password } = request.query;
const customerHeader = request.headers['h-Custom'];
// do something with request data
return `logged in!`;
});
อ่านเพิ่มเติม Fastify - TypeScript
นอกเหนือจากที่เขียนไว้ ก็ยังมี Concept และรายละเอียดอื่นๆ ให้ศึกษาเพิ่มเติม จาก Docs / Reference ครับ เช่น
Conclusion
สรุป ผมว่าคนที่เคยเขียน Express.js มาก่อน ถ้ามาจับ Fastify จริงๆ แล้ว แทบไม่ต่างกันเลย แนวคิดก็คล้ายกัน ตัว Syntax ก็เขียนคล้ายๆ กัน มีแค่เรื่อง Plugin ที่อาจจะต่างกันนิดหน่อย ส่วน syntax ตัวไหนไม่เข้าใจ ไม่ถนัด ก็อ่าน Docs / API มีรายละเอียดเกือบหมด
สุดท้ายแล้ว ผมคิดว่า ถ้าเรามี Fundamental ที่ดี เข้าใจพื้นฐาน เข้าใจการทำงานของ Web / API เราก็สามารถใช้ Framework หรือเครื่องมือไหนๆ ก็ได้ ไม่เข้าใจตรงไหน ก็อ่าน Docs เพิ่มเติมเอา
Happy Coding ❤️
- Authors
- Name
- Chai Phonbopit
- Website
- @Phonbopit