A high-performance API router for Astro built on a Trie matcher.
Define API routes using clean, flat structures β no folders or boilerplate logic.
npm install astro-routify
// src/pages/api/index.ts
import {
defineRoute,
defineRouter,
defineGroup,
HttpMethod,
ok,
} from 'astro-routify';
const userGroup = defineGroup('/users', (group) => {
group.addGet('/:id', ({params}) => ok({id: params.id}));
});
export const GET = defineRouter([
defineRoute(HttpMethod.GET, '/ping', () => ok('pong')),
...userGroup.getRoutes(),
]);
Or to handle everything in a single place:
import {RouterBuilder, ok} from 'astro-routify';
const builder = new RouterBuilder();
builder
.addGet('/ping', () => ok('pong'))
.addPost('/submit', async ({request}) => {
const body = await request.json();
return ok({received: body});
});
export const ALL = builder.build(); // catch-all
You can find an implementation example in the astro-routify-example repository. It showcases a minimal Astro app with API endpoints configured under:
/src/pages/api/[...path].ts
This setup demonstrates how to route requests dynamically using astro-routify, while still leveraging Astro's native endpoint system.
- β‘ Fully compatible with Astroβs native APIContext β no extra setup needed.
- π§© Use middleware, access cookies, headers, and request bodies exactly as you would in a normal Astro endpoint.
- β Flat-file, code-based routing (no folders required)
- β
Dynamic segments (
:id
) - β
ALL-mode for monolithic routing (
RouterBuilder
) - β
Built-in response helpers (
ok
,created
, etc.) - β Trie-based matcher for fast route lookup
- β Fully typed β no magic strings
- π Streaming support
stream()
β raw streaming with backpressure support (e.g. SSE, logs, custom protocols)streamJsonND()
β newline-delimited JSON streaming (NDJSON)streamJsonArray()
β server-side streamed JSON arrays
π See CHANGELOG.md for recent updates and improvements.
Declare a single route:
defineRoute(HttpMethod.GET, "/users/:id", ({params}) => {
return ok({userId: params.id});
});
Group multiple routes under one HTTP method handler:
export const GET = defineRouter([
defineRoute(HttpMethod.GET, "/health", () => ok("ok"))
]);
π§
defineRouter()
supports all HTTP methods β but Astro only executes the method you export (GET
,POST
, etc.)
Use RouterBuilder
when you want to build routes dynamically, catch all HTTP methods via ALL
, or organize routes more
fluently with helpers.
const builder = new RouterBuilder();
builder
.addGet("/ping", () => ok("pong"))
.addPost("/submit", async ({request}) => {
const body = await request.json();
return ok({received: body});
});
export const ALL = builder.build();
You can also group routes:
const users = defineGroup("/users")
.addGet("/:id", ({params}) => ok({id: params.id}));
builder.addGroup(users);
π While
.register()
is still available, it's deprecated in favor of.addGroup()
and.addRoute()
for better structure and reusability.
Avoid boilerplate new Response(JSON.stringify(...))
:
import {fileResponse} from 'astro-routify';
ok(data); // 200 OK
created(data); // 201 Created
noContent(); // 204
notFound("Missing"); // 404
internalError(err); // 500
fileResponse(content, "application/pdf", "report.pdf"); // sets Content-Type and Content-Disposition
stream('/clock', async ({response}) => {
const timer = setInterval(() => {
response.write(new Date().toISOString());
}, 1000);
setTimeout(() => {
clearInterval(timer);
response.close();
}, 5000);
});
streamJsonND('/updates', async ({response}) => {
response.send({step: 1});
await delay(500);
response.send({step: 2});
response.close();
});
streamJsonArray('/items', async ({response}) => {
for (let i = 0; i < 3; i++) {
response.send({id: i});
}
response.close();
});
Any route param like :id
is extracted into ctx.params
:
const builder = new RouterBuilder();
builder.addGet("/users/:id", ({params}) => ok({userId: params.id}));
//OR
defineRoute(HttpMethod.GET, "/items/:id", ({params}) => {
return ok({itemId: params.id});
});
// src/pages/api/[...slug].ts
export const GET = async ({request}) => {
const url = new URL(request.url);
const path = url.pathname;
if (path.startsWith('/api/users/')) {
// Try to extract ID
const id = path.split('/').pop();
return new Response(JSON.stringify({id}), {
status: 200,
headers: {'Content-Type': 'application/json'},
});
}
if (path === '/api/users') {
return new Response(JSON.stringify([{id: 1}, {id: 2}]), {
status: 200,
headers: {'Content-Type': 'application/json'},
});
}
if (path === '/api/ping') {
return new Response(JSON.stringify({pong: true}), {
status: 200,
headers: {'Content-Type': 'application/json'}
});
}
return new Response('Not Found', {status: 404});
};
src/
ββ pages/
β ββ api/
β β ββ users/
β β β ββ index.ts // GET all users
β β β ββ [id]/
β β β β ββ index.ts // GET / POST / DELETE for a user
β β ββ ping.ts
// src/pages/api/[...slug].ts
const builder = new RouterBuilder();
builder.addGet("/ping", () => ok({pong: true}));
builder.addGet("/users/:id", ({params}) => ok({userId: params.id}));
// OR
export const ALL = defineRouter([
defineRoute(HttpMethod.GET, "/ping", () => ok({pong: true})),
defineRoute(HttpMethod.GET, "/users/:id", ({params}) => ok({id: params.id}))
]);
astro-routify
uses a Trie structure for fast route and method matching.
Itβs optimized for real-world route hierarchies, and avoids nested if
chains.
Realistic and synthetic benchmarks using vitest bench
.
Tests ran on a mid-range development setup:
- CPU: Intel Core i5-7600K @ 3.80GHz (4 cores)
- RAM: 16 GB DDR4
- GPU: NVIDIA GeForce GTX 1080 (8 GB)
- OS: Windows 10 Pro 64-bit
- Node.js: v20.x
- Benchmark Tool: Vitest Bench
Results may vary slightly on different hardware.
β RouteTrie performance - realistic route shapes
Β· Static route lookup (5000) 1,819,681 req/sec
Β· Param route: /users/:userId 1,708,264 req/sec
Β· Nested param route: /users/:id/orders/:oid 1,326,324 req/sec
Β· Blog route: /blog/:year/:month/:slug 1,220,882 req/sec
Β· Nonexistent path 1,621,934 req/sec
β RouteTrie performance
Β· Lookup in SMALL (100 routes) 1,948,385 req/sec
Β· Lookup in MEDIUM (1000 routes) 1,877,248 req/sec
Β· Lookup in LARGE (10000 routes) 1,908,279 req/sec
Β· Lookup non-existent route in LARGE 1,962,051 req/sec
β‘ Performance stays consistently fast even with 10k+ routes
While focused on simplicity and speed today, astro-routify
is designed to evolve β enabling more advanced routing
patterns in the future.
MIT β Β© 2025 Alex Mora
If this project helps you, consider buying me a coffee. Every drop keeps the code flowing!