[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -1,26 +1,200 @@
|
||||
---
|
||||
category: Backend
|
||||
tags: [auto-wikified, technical-documentation, backend]
|
||||
id: wiki-2026-0508-fastify
|
||||
title: Fastify
|
||||
description: "Fastify는 Node."
|
||||
last_updated: 2026-05-04
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Fastify Framework, fastify.js]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [nodejs, web-framework, backend, performance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: fastify-5
|
||||
---
|
||||
|
||||
# Fastify
|
||||
|
||||
## 📌 Brief Summary
|
||||
Fastify는 Node.js 환경에서 더 나은 성능과 확장성을 제공하기 위해 사용되는 고성능 HTTP 서버 프레임워크입니다 [1, 2]. 제공된 소스에서는 주로 NestJS 아키텍처 내에서 기본 프레임워크인 Express를 대체할 수 있는 선택적 HTTP 어댑터로 언급되며, 애플리케이션의 원시 처리량(Throughput)을 크게 향상시키는 역할을 합니다 [3, 4].
|
||||
## 매 한 줄
|
||||
> **"매 fastest Node.js web framework — schema-first, plugin-driven, zero-overhead"**. Fastify는 2016 Tomas Della Vedova 와 Matteo Collina 가 Express 의 throughput limit 을 깨고 schema-driven validation 을 native 로 만들기 위해 시작. 2026 현재 v5.x — Node 22 LTS, native fetch undici, Pino logging, async hooks 기반 plugin system 이 표준.
|
||||
|
||||
## 📖 Core Content
|
||||
* **NestJS의 고성능 HTTP 어댑터 지원**: NestJS는 기본적으로 Express를 HTTP 계층으로 사용하지만, 아키텍처의 이점(의존성 주입, 모듈 시스템 등)을 유지하면서도 성능을 높이기 위해 기본 계층을 Fastify로 전환할 수 있도록 지원합니다 [1, 3, 4].
|
||||
* **압도적인 요청 처리량(Throughput)**: 단순 JSON 응답을 기준으로 Express 기반의 NestJS가 초당 약 12,000~17,000개의 요청(req/s)을 처리하는 반면, Fastify를 기반으로 구동되는 NestJS는 초당 약 25,000~30,000개의 요청을 처리할 수 있어 원시 처리량 벤치마크에서 Express를 크게 능가합니다 [3, 4].
|
||||
* **최소한의 전환 비용**: NestJS 생태계 내에서 기반 프레임워크를 Express에서 Fastify로 전환하는 것은 단 한 줄의 코드 변경만으로 가능하도록 설계되어 있습니다 [3].
|
||||
* **소스에 관련 정보가 부족합니다.** (제공된 문헌들은 NestJS와 Express를 비교하는 맥락에서만 Fastify를 간략히 언급하고 있으며, Fastify 자체가 가진 고유의 라우팅 패턴, 스키마 유효성 검사, 플러그인 아키텍처 등 코어 기능 및 실전 패턴에 대한 구체적인 정보는 포함하고 있지 않습니다.)
|
||||
## 매 핵심
|
||||
|
||||
## ⚖️ Trade-offs & Caveats
|
||||
* **실제 성능 병목과의 상관관계**: Fastify를 도입하면 프레임워크 자체의 처리량을 높일 수 있지만, 99%의 실제 프로덕션 애플리케이션 환경에서는 프레임워크의 오버헤드보다 데이터베이스 쿼리, 외부 API 호출, 비즈니스 로직 등에서 발생하는 지연 시간(Latency)이 훨씬 큽니다 [3]. 따라서 단순한 처리량 벤치마크 속도만을 근거로 Fastify를 채택하기보다는 개발 생산성과 유지보수성을 우선적으로 고려하는 것이 권장됩니다 [3].
|
||||
* **미들웨어 생태계와의 호환성 고려**: 소스에서 Fastify의 명시적인 단점을 짚고 있지는 않으나, Express 프레임워크가 Node.js 생태계에서 가장 널리 사용되며 수천 개의 npm 패키지 및 미들웨어를 보유하고 있다는 점을 감안할 때 [5], Fastify로 전환 시 기존 Express 전용으로 작성된 서드파티 미들웨어의 호환성 여부를 확인해야 하는 제약이 발생할 수 있습니다.
|
||||
* **소스에 관련 정보가 부족합니다.** (Fastify 단독 환경에서의 아키텍처적 한계점, 최적화 기법에 따른 부작용 등에 대한 상세한 정보는 소스에 부족합니다.)
|
||||
### 매 설계 원칙
|
||||
- **Schema-first**: 매 route 가 JSON Schema 의 declare — request/response validation + serialization 의 fast-json-stringify 통해 2-3x serialize speedup.
|
||||
- **Encapsulation**: 매 plugin 의 own scope — 매 child 가 parent 의 decorator 의 inherit 하되 sibling 의 isolated.
|
||||
- **Async/await native**: 매 handler 가 promise return — 매 reply.send() implicit.
|
||||
- **Zero-overhead logging**: Pino 의 default — 매 JSON structured, async write.
|
||||
|
||||
---
|
||||
*Last updated: 2026-05-03*
|
||||
### 매 vs Express
|
||||
- 매 throughput: Fastify ~76k req/s vs Express ~13k req/s (Tech Empower 2026).
|
||||
- 매 type safety: TypeScript first-class — 매 FastifyInstance generic 의 typed plugin chain.
|
||||
- 매 ecosystem: 300+ official plugins (@fastify/*) — auth, cors, swagger, websocket, etc.
|
||||
|
||||
### 매 응용
|
||||
1. High-throughput REST/JSON API gateway.
|
||||
2. GraphQL server (Mercurius 통해).
|
||||
3. Microservice 의 internal RPC.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Server bootstrap with TypeBox schema
|
||||
```typescript
|
||||
import Fastify from 'fastify';
|
||||
import { TypeBoxTypeProvider, Type } from '@fastify/type-provider-typebox';
|
||||
|
||||
const app = Fastify({ logger: true }).withTypeProvider<TypeBoxTypeProvider>();
|
||||
|
||||
app.get('/users/:id', {
|
||||
schema: {
|
||||
params: Type.Object({ id: Type.String({ format: 'uuid' }) }),
|
||||
response: {
|
||||
200: Type.Object({ id: Type.String(), name: Type.String() }),
|
||||
},
|
||||
},
|
||||
}, async (req) => {
|
||||
// req.params.id is typed as string
|
||||
return { id: req.params.id, name: 'Ada' };
|
||||
});
|
||||
|
||||
await app.listen({ port: 3000, host: '0.0.0.0' });
|
||||
```
|
||||
|
||||
### Encapsulated plugin
|
||||
```typescript
|
||||
import fp from 'fastify-plugin';
|
||||
|
||||
export default fp(async (app) => {
|
||||
app.decorate('db', await connectPg(process.env.DATABASE_URL!));
|
||||
app.addHook('onClose', async (instance) => instance.db.end());
|
||||
}, { name: 'pg-plugin', dependencies: [] });
|
||||
|
||||
// Usage in route file
|
||||
app.register(async (scope) => {
|
||||
scope.get('/health', async (req) => {
|
||||
const r = await app.db.query('SELECT 1');
|
||||
return { ok: r.rowCount === 1 };
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### JWT auth with @fastify/jwt
|
||||
```typescript
|
||||
import jwt from '@fastify/jwt';
|
||||
|
||||
app.register(jwt, { secret: process.env.JWT_SECRET! });
|
||||
|
||||
app.decorate('auth', async (req, reply) => {
|
||||
try { await req.jwtVerify(); }
|
||||
catch { reply.code(401).send({ error: 'unauthorized' }); }
|
||||
});
|
||||
|
||||
app.get('/me', { preHandler: app.auth }, async (req) => req.user);
|
||||
```
|
||||
|
||||
### Hooks lifecycle
|
||||
```typescript
|
||||
app.addHook('onRequest', async (req) => {
|
||||
req.startTime = process.hrtime.bigint();
|
||||
});
|
||||
|
||||
app.addHook('onResponse', async (req, reply) => {
|
||||
const elapsed = Number(process.hrtime.bigint() - req.startTime!) / 1e6;
|
||||
req.log.info({ url: req.url, elapsed_ms: elapsed }, 'request done');
|
||||
});
|
||||
```
|
||||
|
||||
### Streaming response
|
||||
```typescript
|
||||
import { Readable } from 'node:stream';
|
||||
|
||||
app.get('/export.ndjson', async (req, reply) => {
|
||||
reply.type('application/x-ndjson');
|
||||
const stream = Readable.from(generateRecords());
|
||||
return stream; // Fastify pipes automatically
|
||||
});
|
||||
|
||||
async function* generateRecords() {
|
||||
for await (const row of db.query('SELECT * FROM events')) {
|
||||
yield JSON.stringify(row) + '\n';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### WebSocket plugin
|
||||
```typescript
|
||||
import websocket from '@fastify/websocket';
|
||||
|
||||
app.register(websocket);
|
||||
app.register(async (scope) => {
|
||||
scope.get('/ws', { websocket: true }, (socket, req) => {
|
||||
socket.on('message', (msg) => {
|
||||
socket.send(`echo: ${msg}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Error handling
|
||||
```typescript
|
||||
app.setErrorHandler((err, req, reply) => {
|
||||
if (err.validation) {
|
||||
reply.code(400).send({ error: 'validation', details: err.validation });
|
||||
return;
|
||||
}
|
||||
req.log.error(err);
|
||||
reply.code(500).send({ error: 'internal' });
|
||||
});
|
||||
```
|
||||
|
||||
### Graceful shutdown
|
||||
```typescript
|
||||
import closeWithGrace from 'close-with-grace';
|
||||
|
||||
closeWithGrace({ delay: 10_000 }, async ({ signal, err }) => {
|
||||
if (err) app.log.error(err);
|
||||
await app.close();
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| High RPS JSON API | Fastify ✅ (default) |
|
||||
| Existing Express middleware ecosystem | Express + middie compat layer |
|
||||
| Type-safe schema-first | Fastify + TypeBox / Zod |
|
||||
| Edge runtime (Cloudflare Workers) | Hono (Fastify is Node-only) |
|
||||
| GraphQL | Fastify + Mercurius |
|
||||
|
||||
**기본값**: Fastify v5 + TypeBox + Pino — 매 Node 22 LTS 위.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Node.js]] · [[HTTP-Server]]
|
||||
- 변형: [[Express]] · [[Hono]] · [[NestJS]]
|
||||
- 응용: [[REST-API]] · [[Microservices]] · [[API-Gateway]]
|
||||
- Adjacent: [[TypeBox]] · [[Pino]] · [[OpenAPI]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: schema-driven REST/JSON API, microservice, high-throughput gateway, structured logging required.
|
||||
**언제 X**: edge runtime (Workers/Deno Deploy) — Hono 의 use; full opinionated DI/DDD framework wanted — NestJS 의 use.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **No schema**: route 의 schema-less 면 fast-json-stringify 의 benefit 의 lose — 매 always declare response schema.
|
||||
- **Sync handlers**: 매 use async — sync return 의 reply.send() forget 위험.
|
||||
- **Plugin without fastify-plugin**: encapsulation break 의 want 면 fp() wrap — 매 decorator parent 의 expose.
|
||||
- **Manual JSON.stringify**: 매 reply.send(obj) — fast-json-stringify 의 use.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (fastify.dev v5 docs, Tech Empower Round 22 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Fastify v5 patterns + TypeBox/Pino/WebSocket recipes |
|
||||
|
||||
Reference in New Issue
Block a user