Câte un handler pentru fiecare rută
Rutarea decide care handler răspunde la care cerere. Modulul node:http nu are un router propriu: el trimite fiecare cerere către o singură funcție, iar tu decizi unde merge mai departe. Asta cere doar câteva linii — și, scriindu-le o dată, vezi exact ce face un router.
Piesele sunt metoda cererii (req.method), calea ei și interogarea. new URL(req.url ?? "/", "http://localhost") separă calea de șirul de interogare, pentru că req.url este doar partea de după gazdă. Un tipar precum "/users/{id}" este apoi potrivit segment cu segment, iar bucata potrivită este dată handlerului ca params.id. Framework-urile adaugă facilități peste toate acestea, dar atât acoperă nevoile de zi cu zi.
Rute și valori din cale
Două rute: lista la /users și un singur utilizator la /users/{id}. Partea dintre acolade este parametrul de cale — bucata din URL care este dată.
O rută cu un parametru de cale
Același router trimite /users către un handler și /users/7 către altul, iar matchPath dă cifra 7 handlerului ca params.id — handlerul însuși nu taie niciun șir de caractere.
Ieșire: program TypeScript și rezultatul capturat
import { createServer, type IncomingMessage, type ServerResponse } from "node:http"
type RouteHandler = (
req: IncomingMessage,
res: ServerResponse,
params: Record<string, string>,
) => void
interface Route {
method: string
pattern: string
handler: RouteHandler
}
// Potrivește "/users/{id}" cu "/users/7" și returnează { id: "7" }.
// Returnează undefined când tiparul nu se potrivește cu calea.
function matchPath(pattern: string, path: string): Record<string, string> | undefined {
const want = pattern.split("/")
const got = path.split("/")
if (want.length !== got.length) return undefined
const params: Record<string, string> = {}
for (let i = 0; i < want.length; i++) {
if (want[i].startsWith("{") && want[i].endsWith("}")) {
params[want[i].slice(1, -1)] = got[i]
} else if (want[i] !== got[i]) {
return undefined
}
}
return params
}
function router(routes: Route[]) {
return (req: IncomingMessage, res: ServerResponse): void => {
const url = new URL(req.url ?? "/", "http://localhost")
for (const route of routes) {
const params = matchPath(route.pattern, url.pathname)
if (params !== undefined && route.method === req.method) {
route.handler(req, res, params)
return
}
}
res.writeHead(404).end("nu a fost găsit\n")
}
}
const routes: Route[] = [
{
method: "GET",
pattern: "/users",
handler: (req, res) => {
res.end("toți utilizatorii: Ana, Ben, Cleo\n")
},
},
{
method: "GET",
pattern: "/users/{id}",
handler: (req, res, params) => {
res.end(`un singur utilizator, id = ${params.id}\n`)
},
},
]
async function main(): Promise<void> {
const server = createServer(router(routes))
await new Promise<void>(resolve => server.listen(0, () => resolve()))
const port = (server.address() as { port: number }).port
for (const path of ["/users", "/users/7"]) {
const res = await fetch(`http://localhost:${port}${path}`)
process.stdout.write(`GET ${path} -> ` + (await res.text()))
}
server.close()
}
main()
Parametri de interogare
Partea de după ? conține parametrii de interogare — opțiuni pentru cerere, precum un text căutat sau o limită: /search?q=api&limit=2. Ei vin din url.searchParams, mereu sub formă de șiruri de caractere, iar get() returnează null pentru unul lipsă — astfel încât ?? poate oferi o valoare implicită.
Regula practică: calea spune care resursă, interogarea spune cum — filtrare, sortare, paginare.
Citirea șirului de interogare
Handlerul citește q și limit din interogare. A doua cerere omite limit, așa că handlerul revine la 10 — parametrii lipsă sunt ceva normal, pregătește-te pentru ei.
Ieșire: program TypeScript și rezultatul capturat
import { createServer, type IncomingMessage, type ServerResponse } from "node:http"
function searchHandler(req: IncomingMessage, res: ServerResponse): void {
// req.url conține doar calea și interogarea, așa că dăm lui URL o bază cu care să lucreze.
const url = new URL(req.url ?? "/", "http://localhost")
// get() returnează null când parametrul lipsește — ?? oferă valoarea implicită.
const q = url.searchParams.get("q") ?? ""
const limit = url.searchParams.get("limit") ?? "10"
res.end(`se caută ${q} cu limita ${limit}\n`)
}
async function main(): Promise<void> {
const server = createServer((req, res) => {
const url = new URL(req.url ?? "/", "http://localhost")
if (req.method === "GET" && url.pathname === "/search") {
searchHandler(req, res)
return
}
res.writeHead(404).end("nu a fost găsit\n")
})
await new Promise<void>(resolve => server.listen(0, () => resolve()))
const port = (server.address() as { port: number }).port
for (const path of ["/search?q=api&limit=2", "/search?q=json"]) {
const res = await fetch(`http://localhost:${port}${path}`)
process.stdout.write(`GET ${path} -> ` + (await res.text()))
}
server.close()
}
main()