Cod în jurul fiecărui handler
Middleware este cod care rulează în jurul handlerelor tale: înainte ca cererea să ajungă la ele, după ce răspunsul pleacă de la ele sau în locul lor. Jurnalizare, autentificare, limitarea ratei, compresie — toate sunt middleware.
Aceeași idee circulă sub trei nume: framework-urile web spun middleware, gRPC spune interceptor, unele ecosisteme spun filtru. Un singur concept, trei vocabulare.
În TypeScript, un middleware este o funcție care primește un handler și returnează un handler împachetat — exact șablonul Decorator din grupul de șabloane de proiectare, iar o stivă de astfel de funcții se comportă ca un Chain of Responsibility.
Un middleware de jurnalizare
withLogging împachetează orice handler: afișează cererea care intră, lasă handlerul să lucreze și afișează din nou la ieșire. Handlerul însuși rămâne complet neștiutor.
Jurnalizarea reală adaugă de obicei o marcă de timp și o durată. Aceste exemple le lasă pe amândouă deoparte intenționat, ca rezultatul afișat să fie același la fiecare rulare.
Împachetezi o dată, jurnalizezi tot
Un singur împachetator în jurul întregului router și ambele căi sunt jurnalizate — jurnalizarea este scrisă o singură dată, în împachetator, nu în fiecare handler.
Ieșire: program TypeScript și rezultatul capturat
import { createServer, type IncomingMessage, type ServerResponse } from "node:http"
type Handler = (req: IncomingMessage, res: ServerResponse) => void
// Middleware: primește un handler, returnează un handler împachetat.
function withLogging(next: Handler): Handler {
return (req, res) => {
console.log("-> ", req.method, req.url)
next(req, res)
console.log("<- gata")
}
}
interface Route {
method: string
path: string
handler: Handler
}
function router(routes: Route[]): Handler {
return (req, res) => {
const url = new URL(req.url ?? "/", "http://localhost")
const route = routes.find(r => r.method === req.method && r.path === url.pathname)
if (route === undefined) {
res.writeHead(404).end("negăsit\n")
return
}
route.handler(req, res)
}
}
const routes: Route[] = [
{ method: "GET", path: "/users", handler: (req, res) => res.end("Ana, Ben\n") },
{ method: "GET", path: "/notes", handler: (req, res) => res.end("cumpărături, idei\n") },
]
async function main(): Promise<void> {
// Un singur împachetator în jurul întregului router: fiecare rută este jurnalizată.
const server = createServer(withLogging(router(routes)))
await new Promise<void>(resolve => server.listen(0, () => resolve()))
const port = (server.address() as { port: number }).port
for (const path of ["/users", "/notes"]) {
const res = await fetch(`http://localhost:${port}${path}`)
console.log("corp:", (await res.text()).trim())
}
server.close()
}
main()
Middleware care oprește cererea
Un middleware poate și să refuze: o verificare de autentificare care nu găsește o cheie validă scrie 401 Unauthorized și nu apelează niciodată handlerul următor.
Înlănțuit ca withLogging(withAuth(handler)), cererea trece prin straturi în ordine — iar răspunsul iese înapoi prin ele.
Un strat de autentificare în lanț
Cererea fără antetul X-API-Key este oprită de stratul de autentificare cu 401 — handlerul nu rulează niciodată. Cu cheia, cererea trece prin tot lanțul și ajunge la date.
Ieșire: program TypeScript și rezultatul capturat
import { createServer, type IncomingMessage, type ServerResponse } from "node:http"
type Handler = (req: IncomingMessage, res: ServerResponse) => void
function withLogging(next: Handler): Handler {
return (req, res) => {
console.log("->", req.method, req.url)
next(req, res)
}
}
// Middleware de autentificare: poate opri cererea înainte ca handlerul să ruleze.
function withAuth(next: Handler): Handler {
return (req, res) => {
if (req.headers["x-api-key"] !== "secret123") {
res.writeHead(401).end("cheie API lipsă sau greșită\n")
return
}
next(req, res)
}
}
const report: Handler = (req, res) => {
res.end("raportul secret\n")
}
async function main(): Promise<void> {
// Cererea trece prin straturi în ordine.
const server = createServer(withLogging(withAuth(report)))
await new Promise<void>(resolve => server.listen(0, () => resolve()))
const port = (server.address() as { port: number }).port
for (const key of ["", "secret123"]) {
const headers: Record<string, string> = {}
if (key !== "") headers["X-API-Key"] = key
const res = await fetch(`http://localhost:${port}/report`, { headers })
console.log(" stare:", res.status, "-", (await res.text()).trim())
}
server.close()
}
main()