วันนี้ลองเล่น และลองอ่าน 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