1. Pengenalan Fastify
Fastify adalah web framework yang sangat cepat dan berbiaya rendah untuk Node.js, dikembangkan oleh Matteo Collina dan Tomas Della Vedova. Fastify dirancang untuk memberikan performa terbaik dengan overhead yang minimal β menjadikannya salah satu framework HTTP tercepat yang tersedia untuk Node.js.
Fastify menggunakan JSON Schema untuk validasi dan serialisasi data, yang tidak hanya memastikan keamanan input, tetapi juga mempercepat serialisasi JSON hingga 2-3x lebih cepat dibandingkan JSON.stringify(). Arsitektur plugin-nya membuat kode sangat modular dan mudah di-maintain.
Mengapa Memilih Fastify?
| Keunggulan | Penjelasan |
|---|---|
| Performa Tinggi | Hingga 30,000+ requests/detik β jauh lebih cepat dari Express (~7,000 req/s) |
| JSON Schema Validation | Validasi input built-in dengan JSON Schema yang juga mempercepat serialisasi |
| Plugin Architecture | Sistem plugin yang sangat modular untuk encapsulation dan reuse |
| TypeScript Support | TypeScript support bawaan dengan type inference yang sangat baik |
| Lifecycle Hooks | 10 hooks yang mencakup seluruh lifecycle request |
| Logging Built-in | Pino logger terintegrasi β logging performant dengan zero-cost abstraction |
Fastify vs Express.js
| Aspek | Fastify | Express.js |
|---|---|---|
| Performa | ~30,000 req/s | ~7,000 req/s |
| Validasi | JSON Schema built-in | Manual / express-validator |
| Serialisasi | fast-json-stringify (2-3x lebih cepat) | JSON.stringify() |
| Logging | Pino (built-in) | Manual / morgan / winston |
| Architecture | Plugin-based (encapsulated) | Middleware-based |
| TypeScript | First-class support | Perlu @types/express |
| Learning Curve | π‘ Sedang | π’ Mudah |
| Ekosistem | π‘ Berkembang pesat | π’ Sangat besar |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β FASTIFY REQUEST LIFECYCLE β β β β Incoming Request β β β β β βΌ β β ββββββββββββββββ β β β onRequest β β Hook: Logging, CORS, timing β β ββββββββ¬ββββββββ β β βΌ β β ββββββββββββββββ β β β preParsing β β Hook: Modify raw body β β ββββββββ¬ββββββββ β β βΌ β β ββββββββββββββββ β β β preParsing β β Parse request body β β ββββββββ¬ββββββββ β β βΌ β β ββββββββββββββββ β β β preValidationβ β Hook: Before schema validation β β ββββββββ¬ββββββββ β β βΌ β β ββββββββββββββββ β β β Validation β β JSON Schema validation β β ββββββββ¬ββββββββ β β βΌ β β ββββββββββββββββ β β β preHandler β β Hook: Auth check, rate limiting β β ββββββββ¬ββββββββ β β βΌ β β ββββββββββββββββ β β β Handler β β Route handler (business logic) β β ββββββββ¬ββββββββ β β βΌ β β ββββββββββββββββ β β βonSend/ β β Hook: Modify response, logging β β βonResponse β β β ββββββββββββββββ β β βΌ β β Response Sent β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. Setup dan Instalasi
Instalasi Dasar
# Buat proyek baru mkdir my-fastify-api cd my-fastify-api npm init -y # Instal Fastify npm install fastify # Untuk TypeScript (opsional tapi direkomendasikan) npm install -D typescript @types/node tsx # Buat file konfigurasi TypeScript npx tsc --init
Hello World
// src/server.ts
import Fastify from 'fastify';
// Buat instance Fastify
const fastify = Fastify({
logger: true // Aktifkan Pino logger
});
// Definisikan route
fastify.get('/', async (request, reply) => {
return { message: 'Halo dari Fastify!' };
});
// Jalankan server
const start = async () => {
try {
await fastify.listen({ port: 3000, host: '0.0.0.0' });
console.log('Server berjalan di http://localhost:3000');
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();
# Jalankan dengan tsx (TypeScript execution)
npx tsx src/server.ts
# Atau dengan npm script
# package.json:
# "scripts": {
# "dev": "tsx watch src/server.ts",
# "build": "tsc",
# "start": "node dist/server.js"
# }
Struktur Proyek yang Direkomendasikan
my-fastify-api/ βββ src/ β βββ plugins/ # Fastify plugins β β βββ db.ts # Database plugin β β βββ auth.ts # Auth plugin β β βββ swagger.ts # Swagger plugin β βββ routes/ # Route definitions β β βββ products/ β β β βββ index.ts # /products routes β β β βββ schemas.ts # JSON schemas β β β βββ handlers.ts β β βββ users/ β β βββ index.ts β β βββ schemas.ts β β βββ handlers.ts β βββ types/ # TypeScript types β β βββ fastify.d.ts # Fastify type augmentations β βββ utils/ # Utility functions β βββ app.ts # App setup (plugins, routes) β βββ server.ts # Entry point βββ tests/ # Tests βββ .env # Environment variables βββ tsconfig.json βββ package.json βββ README.md
3. Routing di Fastify
Fastify mendukung beberapa cara untuk mendaftarkan routes: inline di instance, menggunakan fastify.register() dengan plugin, atau menggunakan @fastify/routes.
Dasar Routing
import Fastify from 'fastify';
const fastify = Fastify({ logger: true });
// === HTTP Methods ===
fastify.get('/products', async (request, reply) => {
return { products: [] };
});
fastify.get('/products/:id', async (request, reply) => {
const { id } = request.params as { id: string };
return { id, name: 'Produk #' + id };
});
fastify.post('/products', async (request, reply) => {
const body = request.body as any;
reply.code(201); // Status 201 Created
return { message: 'Produk dibuat', product: body };
});
fastify.put('/products/:id', async (request, reply) => {
const { id } = request.params as { id: string };
return { message: `Produk ${id} diupdate` };
});
fastify.delete('/products/:id', async (request, reply) => {
const { id } = request.params as { id: string };
reply.code(204); // No Content
return;
});
// === Prefix Routes ===
fastify.register(async function apiRoutes(app) {
app.get('/users', async () => ({ users: [] }));
app.get('/users/:id', async (req) => {
const { id } = req.params as { id: string };
return { id, name: 'User #' + id };
});
app.post('/users', async (req, reply) => {
reply.code(201);
return { message: 'User dibuat' };
});
}, { prefix: '/api/v1' });
// Hasil: /api/v1/users, /api/v1/users/:id
Route Options dan Config
// Route dengan full options
fastify.route({
method: 'GET',
url: '/products',
schema: {
querystring: {
type: 'object',
properties: {
page: { type: 'integer', default: 1 },
limit: { type: 'integer', default: 20 },
category: { type: 'string' }
}
},
response: {
200: {
type: 'object',
properties: {
products: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
price: { type: 'number' }
}
}
},
total: { type: 'integer' }
}
}
}
},
handler: async (request, reply) => {
const { page, limit, category } = request.query as any;
// ... fetch products
return { products: [], total: 0 };
}
});
// Shorthand dengan schema
fastify.post('/products', {
schema: {
body: {
type: 'object',
required: ['name', 'price'],
properties: {
name: { type: 'string', minLength: 1 },
price: { type: 'number', minimum: 0 },
description: { type: 'string' }
}
}
}
}, async (request, reply) => {
// request.body sudah tervalidasi dan bertipe!
const { name, price, description } = request.body as any;
return { name, price, description };
});
4. Sistem Plugin
Plugin adalah cara utama untuk memperluas fungsionalitas Fastify. Setiap plugin memiliki encapsulation scope sendiri β artinya route, hook, dan dekorator yang didaftarkan di dalam plugin tidak mempengaruhi luar plugin tersebut.
Membuat Plugin
// plugins/db.ts β Database plugin
import fp from 'fastify-plugin';
import { PrismaClient } from '@prisma/client';
// Deklarasi type augmentation
declare module 'fastify' {
interface FastifyInstance {
db: PrismaClient;
}
}
async function dbPlugin(fastify: FastifyInstance) {
const prisma = new PrismaClient();
// Dekorasi instance dengan Prisma
fastify.decorate('db', prisma);
// Cleanup saat server ditutup
fastify.addHook('onClose', async () => {
await prisma.$disconnect();
fastify.log.info('Database connection closed');
});
}
// fp() memastikan plugin hanya di-load sekali
export default fp(dbPlugin, {
name: 'db'
});
// === routes/products.ts β Product routes plugin ===
import { FastifyInstance } from 'fastify';
export default async function productRoutes(fastify: FastifyInstance) {
// GET /products β Ambil semua produk
fastify.get('/', async (request, reply) => {
const products = await fastify.db.product.findMany({
orderBy: { createdAt: 'desc' }
});
return { products };
});
// GET /products/:id β Ambil produk by ID
fastify.get('/:id', async (request, reply) => {
const { id } = request.params as { id: string };
const product = await fastify.db.product.findUnique({
where: { id: parseInt(id) }
});
if (!product) {
reply.code(404);
return { error: 'Produk tidak ditemukan' };
}
return { product };
});
// POST /products β Buat produk baru
fastify.post('/', async (request, reply) => {
const body = request.body as any;
const product = await fastify.db.product.create({
data: body
});
reply.code(201);
return { product };
});
}
Mendaftarkan Plugin
// app.ts β App setup
import Fastify from 'fastify';
import dbPlugin from './plugins/db';
import productRoutes from './routes/products';
import userRoutes from './routes/users';
export function buildApp() {
const fastify = Fastify({
logger: {
level: 'info',
transport: {
target: 'pino-pretty', // Format log yang indah
options: { colorize: true }
}
}
});
// Register plugins
fastify.register(dbPlugin);
// Register routes dengan prefix
fastify.register(productRoutes, { prefix: '/api/products' });
fastify.register(userRoutes, { prefix: '/api/users' });
// Health check
fastify.get('/health', async () => {
return { status: 'ok', timestamp: new Date().toISOString() };
});
return fastify;
}
// server.ts β Entry point
import { buildApp } from './app';
const app = buildApp();
app.listen({ port: 3000, host: '0.0.0.0' }, (err) => {
if (err) {
app.log.error(err);
process.exit(1);
}
});
5. Lifecycle Hooks
Fastify menyediakan 10 lifecycle hooks yang memungkinkan kamu menjalankan kode pada titik-titik spesifik dalam request lifecycle. Ini sangat berguna untuk logging, autentikasi, dan manipulasi request/response.
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
export default async function authHooks(fastify: FastifyInstance) {
// === onRequest: Paling awal, sebelum parsing body ===
fastify.addHook('onRequest', async (request, reply) => {
request.startTime = Date.now();
request.log.info({
method: request.method,
url: request.url
}, 'Incoming request');
});
// === preHandler: Setelah validasi, sebelum handler ===
fastify.addHook('preHandler', async (request, reply) => {
// Cek autentikasi untuk protected routes
const publicRoutes = ['/api/auth/login', '/api/auth/register', '/health'];
if (publicRoutes.includes(request.url)) return;
try {
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) {
reply.code(401).send({ error: 'Token tidak ditemukan' });
return;
}
const decoded = fastify.jwt.verify(token);
(request as any).user = decoded;
} catch (err) {
reply.code(401).send({ error: 'Token tidak valid' });
}
});
// === onSend: Saat response akan dikirim ===
fastify.addHook('onSend', async (request, reply, payload) => {
// Tambahkan response time header
const duration = Date.now() - (request.startTime || Date.now());
reply.header('X-Response-Time', `${duration}ms`);
return payload;
});
// === onResponse: Setelah response terkirim ===
fastify.addHook('onResponse', async (request, reply) => {
request.log.info({
statusCode: reply.statusCode,
duration: `${Date.now() - (request.startTime || Date.now())}ms`
}, 'Request completed');
});
// === onError: Saat terjadi error ===
fastify.addHook('onError', async (request, reply, error) => {
request.log.error({
err: error,
statusCode: error.statusCode
}, 'Request error');
});
// === Route-level hooks ===
fastify.get('/admin/dashboard', {
preHandler: [async (request, reply) => {
const user = (request as any).user;
if (user?.role !== 'admin') {
reply.code(403).send({ error: 'Akses ditolak' });
}
}]
}, async (request) => {
return { message: 'Admin Dashboard' };
});
}
6. Validasi dengan JSON Schema
Salah satu fitur paling powerful dari Fastify adalah validasi dan serialisasi berbasis JSON Schema. Saat kamu mendefinisikan schema untuk request dan response, Fastify secara otomatis:
- Memvalidasi input request (body, query, params, headers)
- Menolak request yang tidak valid dengan error message yang jelas
- Menggunakan
fast-json-stringifyuntuk serialisasi response yang super cepat - Memastikan hanya data yang didefinisikan di schema yang dikirim ke client
// schemas/productSchemas.ts
export const createProductSchema = {
body: {
type: 'object',
required: ['name', 'price', 'categoryId'],
properties: {
name: {
type: 'string',
minLength: 2,
maxLength: 200,
errorMessage: {
minLength: 'Nama produk minimal 2 karakter',
maxLength: 'Nama produk maksimal 200 karakter'
}
},
description: { type: 'string', maxLength: 2000 },
price: {
type: 'number',
minimum: 0,
exclusiveMinimum: 0,
errorMessage: {
minimum: 'Harga harus lebih dari 0'
}
},
stock: {
type: 'integer',
minimum: 0,
default: 0
},
categoryId: { type: 'integer' },
tags: {
type: 'array',
items: { type: 'string' },
maxItems: 10
},
isActive: { type: 'boolean', default: true }
},
additionalProperties: false
},
response: {
201: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
price: { type: 'number' },
stock: { type: 'integer' },
isActive: { type: 'boolean' },
createdAt: { type: 'string', format: 'date-time' }
}
}
}
};
export const getProductsSchema = {
querystring: {
type: 'object',
properties: {
page: { type: 'integer', default: 1, minimum: 1 },
limit: { type: 'integer', default: 20, minimum: 1, maximum: 100 },
category: { type: 'string' },
search: { type: 'string' },
sortBy: {
type: 'string',
enum: ['name', 'price', 'createdAt', 'stock']
},
order: {
type: 'string',
enum: ['asc', 'desc'],
default: 'desc'
}
}
},
response: {
200: {
type: 'object',
properties: {
products: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
price: { type: 'number' },
category: { type: 'string' }
}
}
},
pagination: {
type: 'object',
properties: {
page: { type: 'integer' },
limit: { type: 'integer' },
total: { type: 'integer' },
totalPages: { type: 'integer' }
}
}
}
}
}
};
export const getProductByIdSchema = {
params: {
type: 'object',
required: ['id'],
properties: {
id: { type: 'integer', minimum: 1 }
}
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
price: { type: 'number' },
description: { type: 'string' },
stock: { type: 'integer' },
category: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
},
reviews: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'integer' },
rating: { type: 'integer' },
comment: { type: 'string' },
author: { type: 'string' }
}
}
}
}
}
}
};
// === routes/products.ts β Menggunakan schema ===
import { createProductSchema, getProductsSchema, getProductByIdSchema } from '../schemas/productSchemas';
export default async function productRoutes(fastify: FastifyInstance) {
// Dengan schema validation
fastify.get('/', { schema: getProductsSchema }, async (request) => {
const { page, limit, category, search, sortBy, order } =
request.query as any;
// Data sudah tervalidasi dan bertipe!
const products = await fastify.db.product.findMany({
skip: (page - 1) * limit,
take: limit,
orderBy: { [sortBy]: order }
});
const total = await fastify.db.product.count();
return {
products,
pagination: { page, limit, total, totalPages: Math.ceil(total / limit) }
};
});
fastify.post('/', { schema: createProductSchema }, async (request, reply) => {
// request.body sudah tervalidasi β aman digunakan!
const body = request.body as any;
const product = await fastify.db.product.create({ data: body });
reply.code(201);
return product;
});
fastify.get('/:id', { schema: getProductByIdSchema }, async (request) => {
const { id } = request.params as any;
// ... fetch product
});
}
JSON Schema di Fastify bukan hanya untuk validasi β ia juga digunakan untuk serialisasi response. Fastify menggunakan fast-json-stringify yang menghasilkan fungsi serialisasi khusus untuk setiap schema. Hasilnya: serialisasi JSON yang 2-3x lebih cepat dari JSON.stringify() bawaan Node.js. Ini sangat signifikan untuk high-throughput API.
7. Error Handling
Fastify memiliki sistem error handling yang robust dengan setErrorHandler dan FastifyError yang mendukung HTTP status codes.
import Fastify, { FastifyError } from 'fastify';
const fastify = Fastify({ logger: true });
// === Global Error Handler ===
fastify.setErrorHandler((error: FastifyError, request, reply) => {
// Validation error dari JSON Schema
if (error.validation) {
request.log.warn({ err: error }, 'Validation error');
return reply.status(400).send({
error: 'Validasi Gagal',
message: error.message,
details: error.validation
});
}
// HTTP errors
if (error.statusCode) {
return reply.status(error.statusCode).send({
error: error.name,
message: error.message
});
}
// Unexpected errors β jangan expose internal details
request.log.error({ err: error }, 'Unexpected error');
return reply.status(500).send({
error: 'Internal Server Error',
message: 'Terjadi kesalahan pada server'
});
});
// === 404 Handler ===
fastify.setNotFoundHandler((request, reply) => {
reply.status(404).send({
error: 'Not Found',
message: `Route ${request.method} ${request.url} tidak ditemukan`
});
});
// === Custom Error Classes ===
class AppError extends Error {
statusCode: number;
constructor(message: string, statusCode: number = 500) {
super(message);
this.statusCode = statusCode;
this.name = 'AppError';
}
}
class NotFoundError extends AppError {
constructor(resource: string) {
super(`${resource} tidak ditemukan`, 404);
this.name = 'NotFoundError';
}
}
class ValidationError extends AppError {
details: any;
constructor(message: string, details?: any) {
super(message, 400);
this.name = 'ValidationError';
this.details = details;
}
}
// === Penggunaan di handler ===
fastify.get('/products/:id', async (request, reply) => {
const { id } = request.params as any;
const product = await fastify.db.product.findUnique({
where: { id: parseInt(id) }
});
if (!product) {
throw new NotFoundError('Produk');
}
return { product };
});
fastify.post('/products', async (request, reply) => {
const body = request.body as any;
if (body.price < 0) {
throw new ValidationError('Harga tidak boleh negatif', {
field: 'price',
value: body.price
});
}
// ... create product
});
8. TypeScript Integration
Fastify memiliki TypeScript support first-class. Kamu bisa mendapatkan full type inference untuk routes, plugins, request body, dan decorators.
// types/fastify.d.ts β Type augmentations
import 'fastify';
declare module 'fastify' {
// Augment FastifyInstance (decorate)
interface FastifyInstance {
db: PrismaClient;
authenticate: (request: FastifyRequest) => Promise<void>;
}
// Augment FastifyRequest
interface FastifyRequest {
user?: {
id: number;
email: string;
role: string;
};
startTime?: number;
}
}
// === Typed Route Handler ===
import { FastifyPluginAsyncTypebox, Type } from '@fastify/type-provider-typebox';
const productRoutes: FastifyPluginAsyncTypebox = async (fastify) => {
fastify.get('/', {
schema: {
querystring: Type.Object({
page: Type.Optional(Type.Integer({ minimum: 1, default: 1 })),
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100, default: 20 })),
}),
response: {
200: Type.Object({
products: Type.Array(
Type.Object({
id: Type.Integer(),
name: Type.String(),
price: Type.Number(),
})
),
total: Type.Integer(),
}),
},
},
}, async (request) => {
// request.query sudah bertipe: { page: number, limit: number }
const { page, limit } = request.query;
// ... fetch products
return { products: [], total: 0 };
});
};
export default productRoutes;
9. Database Integration
Fastify sangat mudah diintegrasikan dengan berbagai database. Berikut contoh menggunakan Prisma ORM dan fastify-postgres.
// plugins/db-prisma.ts β Prisma Plugin
import fp from 'fastify-plugin';
import { PrismaClient } from '@prisma/client';
import { FastifyInstance } from 'fastify';
async function prismaPlugin(fastify: FastifyInstance) {
const prisma = new PrismaClient({
log: [
{ level: 'query', emit: 'event' },
{ level: 'error', emit: 'stdout' },
{ level: 'warn', emit: 'stdout' }
]
});
// Log queries di development
if (process.env.NODE_ENV === 'development') {
prisma.$on('query', (e) => {
fastify.log.debug({
query: e.query,
duration: `${e.duration}ms`
}, 'DB Query');
});
}
await prisma.$connect();
fastify.log.info('Database connected');
fastify.decorate('db', prisma);
fastify.addHook('onClose', async () => {
await prisma.$disconnect();
});
}
export default fp(prismaPlugin, { name: 'prisma' });
// plugins/db-postgres.ts β Raw PostgreSQL dengan fastify-postgres
import fp from 'fastify-plugin';
import fastifyPostgres from '@fastify/postgres';
export default fp(async (fastify) => {
await fastify.register(fastifyPostgres, {
connectionString: process.env.DATABASE_URL
});
// Contoh query raw
fastify.get('/products-raw', async () => {
const { rows } = await fastify.pg.query(
'SELECT id, name, price FROM products ORDER BY created_at DESC LIMIT 20'
);
return { products: rows };
});
});
10. Autentikasi dan Otorisasi
Fastify menyediakan plugin @fastify/jwt dan @fastify/auth untuk autentikasi dan otorisasi.
// plugins/auth.ts
import fp from 'fastify-plugin';
import fastifyJwt from '@fastify/jwt';
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
export default fp(async (fastify: FastifyInstance) => {
// Register JWT plugin
await fastify.register(fastifyJwt, {
secret: process.env.JWT_SECRET || 'super-secret-key'
});
// Decorator: authenticate
fastify.decorate('authenticate', async (request: FastifyRequest, reply: FastifyReply) => {
try {
await request.jwtVerify();
} catch (err) {
reply.code(401).send({
error: 'Unauthorized',
message: 'Token tidak valid atau sudah kedaluwarsa'
});
}
});
// Decorator: requireRole
fastify.decorate('requireRole', (role: string) => {
return async (request: FastifyRequest, reply: FastifyReply) => {
const user = request.user as any;
if (user.role !== role) {
reply.code(403).send({
error: 'Forbidden',
message: 'Anda tidak memiliki akses ke resource ini'
});
}
};
});
});
// === routes/auth.ts β Login/Register ===
export default async function authRoutes(fastify: FastifyInstance) {
// POST /auth/register
fastify.post('/register', {
schema: {
body: {
type: 'object',
required: ['email', 'password', 'name'],
properties: {
email: { type: 'string', format: 'email' },
password: { type: 'string', minLength: 8 },
name: { type: 'string', minLength: 2 }
}
}
}
}, async (request, reply) => {
const { email, password, name } = request.body as any;
// Hash password
const hashedPassword = await fastify.bcrypt.hash(password, 10);
// Simpan user
const user = await fastify.db.user.create({
data: { email, password: hashedPassword, name }
});
// Generate JWT
const token = fastify.jwt.sign(
{ id: user.id, email: user.email, role: user.role },
{ expiresIn: '7d' }
);
reply.code(201);
return { token, user: { id: user.id, email, name } };
});
// POST /auth/login
fastify.post('/login', async (request, reply) => {
const { email, password } = request.body as any;
const user = await fastify.db.user.findUnique({ where: { email } });
if (!user) {
reply.code(401);
return { error: 'Email atau password salah' };
}
const isValid = await fastify.bcrypt.compare(password, user.password);
if (!isValid) {
reply.code(401);
return { error: 'Email atau password salah' };
}
const token = fastify.jwt.sign(
{ id: user.id, email: user.email, role: user.role },
{ expiresIn: '7d' }
);
return { token, user: { id: user.id, email, name: user.name } };
});
}
// === Protected routes ===
fastify.register(async function protectedRoutes(app) {
// Semua route di plugin ini memerlukan autentikasi
app.addHook('preHandler', app.authenticate);
app.get('/profile', async (request) => {
const user = request.user;
return { user };
});
app.get('/admin/users', {
preHandler: [app.requireRole('admin')]
}, async () => {
return { users: await fastify.db.user.findMany() };
});
}, { prefix: '/api' });
11. Fastify vs Express.js
| Aspek | Fastify | Express.js | Koa.js |
|---|---|---|---|
| Requests/detik | ~30,000 | ~7,000 | ~8,000 |
| Validasi | JSON Schema built-in | Manual / middleware | Manual / middleware |
| Serialisasi | fast-json-stringify | JSON.stringify() | JSON.stringify() |
| Architecture | Plugin-based | Middleware-based | Middleware-based (ctx) |
| Logging | Pino (built-in) | Morgan/Winston | Manual |
| TypeScript | β First-class | β οΈ @types/ | β οΈ @types/ |
| Ekosistem | π’ Besar | π’ Sangat besar | π‘ Sedang |
| Maturity | π‘ Matang | π’ Sangat matang | π‘ Matang |
12. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Fastify: