astro-routify 1.2.2 β 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +367 -28
- package/dist/core/RouteTrie.d.ts +4 -3
- package/dist/core/RouteTrie.js +132 -35
- package/dist/core/RouterBuilder.d.ts +96 -13
- package/dist/core/RouterBuilder.js +243 -21
- package/dist/core/decorators.d.ts +29 -0
- package/dist/core/decorators.js +49 -0
- package/dist/core/defineGroup.d.ts +33 -17
- package/dist/core/defineGroup.js +66 -23
- package/dist/core/defineHandler.d.ts +31 -13
- package/dist/core/defineHandler.js +1 -23
- package/dist/core/defineRoute.d.ts +32 -3
- package/dist/core/defineRoute.js +36 -9
- package/dist/core/defineRouter.d.ts +11 -1
- package/dist/core/defineRouter.js +79 -16
- package/dist/core/internal/createJsonStreamRoute.d.ts +1 -1
- package/dist/core/internal/createJsonStreamRoute.js +12 -4
- package/dist/core/middlewares.d.ts +47 -0
- package/dist/core/middlewares.js +110 -0
- package/dist/core/openapi.d.ts +17 -0
- package/dist/core/openapi.js +84 -0
- package/dist/core/registry.d.ts +15 -0
- package/dist/core/registry.js +26 -0
- package/dist/core/responseHelpers.d.ts +22 -5
- package/dist/core/responseHelpers.js +121 -20
- package/dist/core/stream.d.ts +3 -3
- package/dist/core/stream.js +16 -9
- package/dist/core/streamJsonArray.d.ts +1 -1
- package/dist/core/streamJsonArray.js +1 -1
- package/dist/index.d.ts +338 -57
- package/dist/index.js +5 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -16,6 +16,21 @@ Define API routes using clean, flat structures β no folders or boilerplate log
|
|
|
16
16
|
npm install astro-routify
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
## π Contents
|
|
20
|
+
- [Installing](#installing)
|
|
21
|
+
- [Quickstart](#quickstart)
|
|
22
|
+
- [Mental Model](#mental-model)
|
|
23
|
+
- [Which API should I use?](#which-api-should-i-use)
|
|
24
|
+
- [Core Concepts](#core-concepts)
|
|
25
|
+
- [Advanced Matching](#advanced-matching)
|
|
26
|
+
- [Middleware & Security](#middleware--security)
|
|
27
|
+
- [Single-entry Routing](#single-entry-routing)
|
|
28
|
+
- [Auto-Discovery & Scaling](#auto-discovery--scaling)
|
|
29
|
+
- [Advanced Features](#advanced-features)
|
|
30
|
+
- [Response Helpers](#response-helpers)
|
|
31
|
+
- [Performance](#performance)
|
|
32
|
+
- [Non-goals](#non-goals)
|
|
33
|
+
|
|
19
34
|
## β‘οΈ Quickstart
|
|
20
35
|
|
|
21
36
|
```ts
|
|
@@ -55,6 +70,40 @@ builder
|
|
|
55
70
|
export const ALL = builder.build(); // catch-all
|
|
56
71
|
```
|
|
57
72
|
|
|
73
|
+
## π§ Mental Model
|
|
74
|
+
|
|
75
|
+
`astro-routify` compiles your route definitions into a **Trie (prefix tree)** at startup.
|
|
76
|
+
At runtime, each request performs a deterministic path + method lookup and executes the matched handler inside Astroβs native API context.
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
[Astro Endpoint]
|
|
80
|
+
|
|
|
81
|
+
astro-routify
|
|
82
|
+
|
|
|
83
|
+
Route Trie
|
|
84
|
+
|
|
|
85
|
+
Handler
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
> β‘ **Cold-start performance**: Routes are compiled once at startup; there is no per-request route parsing. This makes it ideal for edge and serverless environments.
|
|
89
|
+
|
|
90
|
+
### Which API should I use?
|
|
91
|
+
|
|
92
|
+
| Use case | Recommended |
|
|
93
|
+
|----------|-------------|
|
|
94
|
+
| Small / explicit APIs | `defineRouter()` |
|
|
95
|
+
| Single-entry / catch-all | `RouterBuilder` |
|
|
96
|
+
| Vertical slices (glob) | `addModules()` |
|
|
97
|
+
| Large apps / plugins | Global registry |
|
|
98
|
+
|
|
99
|
+
### π Trie Invariants
|
|
100
|
+
For architectural stability and predictable performance, the following invariants are enforced:
|
|
101
|
+
- **Single Dynamic Branching**: A node has at most one dynamic parameter child (`paramChild`).
|
|
102
|
+
- **Unified Param Node**: The `paramChild` represents *any* `:param` at that depth; parameter names are bound from route-specific metadata during matching.
|
|
103
|
+
- **Structural Identity**: Two routes differing only by parameter name (e.g., `/u/:id` and `/u/:slug`) are considered structurally identical.
|
|
104
|
+
- **Deterministic Match Order**: Static > Regex (sorted by specificity) > Param > Wildcard > Catch-all.
|
|
105
|
+
- **Terminal Catch-all**: `**` matches are only allowed as the final segment.
|
|
106
|
+
|
|
58
107
|
## π‘ Full Example
|
|
59
108
|
|
|
60
109
|
You can find an implementation example in the [astro-routify-example](https://github.com/oamm/astro-routify-example)
|
|
@@ -76,14 +125,10 @@ endpoint system.
|
|
|
76
125
|
- π§© Use middleware, access cookies, headers, and request bodies exactly as you would in a normal Astro endpoint.
|
|
77
126
|
- β
Flat-file, code-based routing (no folders required)
|
|
78
127
|
- β
Dynamic segments (`:id`)
|
|
79
|
-
- β
ALL-mode for
|
|
128
|
+
- β
ALL-mode for single-entry routing (`RouterBuilder`)
|
|
80
129
|
- β
Built-in response helpers (`ok`, `created`, etc.)
|
|
81
130
|
- β
Trie-based matcher for fast route lookup
|
|
82
131
|
- β
Fully typed β no magic strings
|
|
83
|
-
- π **Streaming support**
|
|
84
|
-
- `stream()` β raw streaming with backpressure support (e.g. SSE, logs, custom protocols)
|
|
85
|
-
- `streamJsonND()` β newline-delimited JSON streaming (NDJSON)
|
|
86
|
-
- `streamJsonArray()` β server-side streamed JSON arrays
|
|
87
132
|
|
|
88
133
|
> π See [CHANGELOG.md](./CHANGELOG.md) for recent updates and improvements.
|
|
89
134
|
---
|
|
@@ -92,14 +137,33 @@ endpoint system.
|
|
|
92
137
|
|
|
93
138
|
### `defineRoute()`
|
|
94
139
|
|
|
95
|
-
Declare a single route
|
|
140
|
+
Declare a single route. Now supports middlewares and metadata.
|
|
141
|
+
|
|
142
|
+
> π‘ `defineRoute` supports two signatures: you can pass a full `Route` object, or specify `method`, `path`, and `handler` as separate arguments.
|
|
143
|
+
|
|
144
|
+
### `RoutifyContext` & `Context`
|
|
145
|
+
|
|
146
|
+
The context object passed to handlers and middlewares extends Astro's `APIContext`. For better ergonomics, you can use the `Context` type alias:
|
|
96
147
|
|
|
97
148
|
```ts
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
149
|
+
import { type Context } from 'astro-routify';
|
|
150
|
+
|
|
151
|
+
const handler = (ctx: Context) => ok('hello');
|
|
101
152
|
```
|
|
102
153
|
|
|
154
|
+
Properties include:
|
|
155
|
+
|
|
156
|
+
- `params`: Route parameters (e.g., `:id`). Matching and capture operate on **decoded** path segments. Decoding occurs exactly once per segment prior to matching.
|
|
157
|
+
- `query`: A read-only snapshot of parsed query parameters. Supports multi-value keys (`string | string[]`).
|
|
158
|
+
- `searchParams`: The raw `URLSearchParams` object. Note: Mutations to `searchParams` do not reflect in `ctx.query`.
|
|
159
|
+
- `state`: A shared object container for passing data between middlewares and handlers.
|
|
160
|
+
|
|
161
|
+
#### Path normalization & basePath stripping
|
|
162
|
+
- `basePath` stripping occurs before decoding and normalization.
|
|
163
|
+
- Stripping happens only on segment boundaries (e.g., `/api/users` matches, `/apiusers` does not).
|
|
164
|
+
- Trailing slashes are normalized by segmentization; `/users` and `/users/` are equivalent.
|
|
165
|
+
- All matching and parameter capture operate on decoded segments.
|
|
166
|
+
|
|
103
167
|
### `defineRouter()`
|
|
104
168
|
|
|
105
169
|
Group multiple routes under one HTTP method handler:
|
|
@@ -110,11 +174,115 @@ export const GET = defineRouter([
|
|
|
110
174
|
]);
|
|
111
175
|
```
|
|
112
176
|
|
|
177
|
+
### Advanced Matching
|
|
178
|
+
|
|
179
|
+
`astro-routify` supports advanced routing patterns including wildcards and regex constraints.
|
|
180
|
+
|
|
181
|
+
#### 1. Wildcards
|
|
182
|
+
- `*` matches exactly one segment.
|
|
183
|
+
- `**` matches zero or more segments (catch-all). **Must be at the end of the path.**
|
|
184
|
+
- Captures the remaining path into `ctx.params['*']`.
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
builder.addGet('/files/*/download', () => ok('one segment'));
|
|
188
|
+
builder.addGet('/static/**', () => ok('all segments'));
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### 2. Regex Constraints
|
|
192
|
+
You can constrain parameters using regex by wrapping the pattern in parentheses: `:param(regex)`.
|
|
193
|
+
|
|
194
|
+
> β οΈ **Specificity Note**: Regex sorting is deterministic (longer pattern first) but heuristic. Users should avoid overlapping regex constraints at the same depth.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
// Matches only numeric IDs
|
|
198
|
+
builder.addGet('/users/:id(\\d+)', ({ params }) => ok(params.id));
|
|
199
|
+
|
|
200
|
+
// Matches hex colors
|
|
201
|
+
builder.addGet('/color/:hex(^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$)', ({ params }) => ok(params.hex));
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### 3. Matching Priority
|
|
205
|
+
When multiple routes could match a path, the router follows a deterministic priority order:
|
|
206
|
+
1. **Static Match** (e.g., `/p/static`)
|
|
207
|
+
2. **Regex Match** (e.g., `/p/:id(\\d+)`)
|
|
208
|
+
3. **Param Match** (e.g., `/p/:id`)
|
|
209
|
+
4. **Wildcard Match** (e.g., `/p/*`)
|
|
210
|
+
5. **Catch-all Match** (e.g., `/**`)
|
|
211
|
+
|
|
212
|
+
### π‘οΈ Middleware & Security
|
|
213
|
+
|
|
214
|
+
`astro-routify` provides a powerful middleware system and built-in security helpers.
|
|
215
|
+
|
|
216
|
+
#### 1. Middleware Support
|
|
217
|
+
Middleware can be applied globally, to groups, or to individual routes.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
const builder = new RouterBuilder();
|
|
221
|
+
|
|
222
|
+
// Global middleware
|
|
223
|
+
builder.use(async (ctx, next) => {
|
|
224
|
+
const start = performance.now();
|
|
225
|
+
const res = await next();
|
|
226
|
+
console.log(`Duration: ${performance.now() - start}ms`);
|
|
227
|
+
return res;
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Group middleware
|
|
231
|
+
builder.group('/admin')
|
|
232
|
+
.use(checkAuth)
|
|
233
|
+
.addGet('/dashboard', (ctx) => ok('Admin only'));
|
|
234
|
+
|
|
235
|
+
// Route middleware
|
|
236
|
+
builder.addPost('/user', validate(UserSchema), (ctx) => {
|
|
237
|
+
return ok(ctx.state.body);
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### 2. Request Validation
|
|
242
|
+
Built-in `validate()` middleware works with Zod, Valibot, or any library implementing a `safeParse` method.
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import { validate } from 'astro-routify';
|
|
246
|
+
import { z } from 'zod';
|
|
247
|
+
|
|
248
|
+
const UserSchema = z.object({
|
|
249
|
+
name: z.string(),
|
|
250
|
+
email: z.string().email()
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
builder.addPost('/register', validate({ body: UserSchema }), (ctx) => {
|
|
254
|
+
const user = ctx.state.body; // Fully typed if using TypeScript correctly
|
|
255
|
+
return ok(user);
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
#### 3. Security Middlewares (CORS & Headers)
|
|
260
|
+
Protect your API with `cors()` and `securityHeaders()`.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { cors, securityHeaders } from 'astro-routify';
|
|
264
|
+
|
|
265
|
+
builder.use(cors({ origin: 'https://example.com' }));
|
|
266
|
+
builder.use(securityHeaders());
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Centralized Error Handling
|
|
270
|
+
Handle all API errors in one place:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
export const ALL = createRouter({
|
|
274
|
+
onError: (err, ctx) => {
|
|
275
|
+
console.error(err);
|
|
276
|
+
return json({ error: 'Something went wrong' }, 500);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
113
281
|
> π§ `defineRouter()` supports all HTTP methods β but Astro only executes the method you export (`GET`, `POST`, etc.)
|
|
114
282
|
|
|
115
|
-
|
|
283
|
+
## π§± Single-entry Routing
|
|
116
284
|
|
|
117
|
-
Use `RouterBuilder` when you want to build routes dynamically, catch
|
|
285
|
+
Use `RouterBuilder` when you want to build routes dynamically, catch-all HTTP methods via `ALL`, or organize routes more
|
|
118
286
|
fluently with helpers.
|
|
119
287
|
|
|
120
288
|
```ts
|
|
@@ -130,47 +298,136 @@ builder
|
|
|
130
298
|
export const ALL = builder.build();
|
|
131
299
|
```
|
|
132
300
|
|
|
133
|
-
|
|
301
|
+
## π Auto-Discovery & Scaling
|
|
302
|
+
|
|
303
|
+
To avoid a long list of manual registrations, you can use `addModules` combined with Vite's `import.meta.glob`. This allows
|
|
304
|
+
you to define routes anywhere in your project (near your components) and have them automatically registered.
|
|
305
|
+
|
|
306
|
+
> π‘ When passing the glob result directly to the router, you **don't** need to set `autoRegister: true` in your routes. The router will automatically discover all exported routes from the modules.
|
|
134
307
|
|
|
135
308
|
```ts
|
|
136
|
-
|
|
137
|
-
|
|
309
|
+
// src/pages/api/[...all].ts
|
|
310
|
+
import { RouterBuilder, createRouter } from 'astro-routify';
|
|
138
311
|
|
|
139
|
-
builder
|
|
312
|
+
// 1. Using the builder
|
|
313
|
+
const builder = new RouterBuilder();
|
|
314
|
+
builder.addModules(import.meta.glob('../../**/*.routes.ts', { eager: true }));
|
|
315
|
+
export const ALL = builder.build();
|
|
316
|
+
|
|
317
|
+
// 2. Or using the one-liner helper
|
|
318
|
+
export const ALL = createRouter(
|
|
319
|
+
import.meta.glob('../../**/*.routes.ts', { eager: true }),
|
|
320
|
+
{ debug: true } // optional: enable match logging
|
|
321
|
+
);
|
|
140
322
|
```
|
|
141
323
|
|
|
142
|
-
|
|
143
|
-
> structure and reusability.
|
|
324
|
+
### π‘οΈ Agnostic Auto-Registration (Global Registry)
|
|
144
325
|
|
|
145
|
-
|
|
326
|
+
If you want to avoid passing glob results or knowing the relative path to your routes, you can use the **global registry**. By setting the `autoRegister` flag or using **decorators**, routes will register themselves as soon as their module is loaded.
|
|
146
327
|
|
|
147
|
-
|
|
328
|
+
##### 1. Enable Auto-Registration in your routes
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
// src/components/User/User.routes.ts
|
|
332
|
+
import { defineRoute, defineGroup, ok, Get } from 'astro-routify';
|
|
333
|
+
|
|
334
|
+
// Option A: Using the flag
|
|
335
|
+
export const GET_USER = defineRoute('GET', '/users/:id', ({params}) => ok({id: params.id}), true);
|
|
336
|
+
|
|
337
|
+
// Option B: Using a group flag
|
|
338
|
+
defineGroup('/admin', (g) => {
|
|
339
|
+
g.addGet('/stats', () => ok({}));
|
|
340
|
+
}, true);
|
|
341
|
+
|
|
342
|
+
// Option C: Using Decorators (requires experimentalDecorators: true)
|
|
343
|
+
class UserRoutes {
|
|
344
|
+
@Get('/profile')
|
|
345
|
+
static getProfile() { return ok({ name: 'Alex' }); }
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
##### 2. Initialize the router agnostically
|
|
350
|
+
|
|
351
|
+
In your catch-all endpoint (e.g., `src/pages/api/[...slug].ts`), you need to trigger the loading of your route files.
|
|
352
|
+
|
|
353
|
+
> **Why is the glob needed?**
|
|
354
|
+
> Even with auto-registration, Vite only executes files that are explicitly imported. The `import.meta.glob` call below tells Vite to find and execute your route files so they can register themselves in the global registry. Without this, the registry would remain empty.
|
|
355
|
+
|
|
356
|
+
###### Using `createRouter()` (Recommended)
|
|
148
357
|
|
|
149
|
-
|
|
358
|
+
The `createRouter` helper is the easiest way to get started. It automatically picks up everything from the global registry.
|
|
150
359
|
|
|
151
360
|
```ts
|
|
152
|
-
|
|
361
|
+
// src/pages/api/[...slug].ts
|
|
362
|
+
import { createRouter } from 'astro-routify';
|
|
153
363
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
364
|
+
// 1. Trigger loading of all route files.
|
|
365
|
+
// We don't need to pass the result to createRouter, but we must call it.
|
|
366
|
+
import.meta.glob('/src/**/*.routes.ts', { eager: true });
|
|
367
|
+
|
|
368
|
+
// 2. createRouter() will automatically pick up everything.
|
|
369
|
+
export const ALL = createRouter({
|
|
370
|
+
debug: true,
|
|
371
|
+
basePath: '/api'
|
|
372
|
+
});
|
|
159
373
|
```
|
|
160
374
|
|
|
161
|
-
|
|
375
|
+
###### Using `RouterBuilder`
|
|
376
|
+
|
|
377
|
+
If you need more control, you can use `RouterBuilder` manually. You must explicitly call `.addRegistered()` to pull in routes from the global registry.
|
|
162
378
|
|
|
163
379
|
```ts
|
|
164
|
-
|
|
380
|
+
// src/pages/api/[...slug].ts
|
|
381
|
+
import { RouterBuilder, notFound, internalError } from 'astro-routify';
|
|
382
|
+
|
|
383
|
+
// 1. Load your routes
|
|
384
|
+
// This triggers Vite to execute the files and populate the global registry.
|
|
385
|
+
import.meta.glob('/src/**/*.routes.ts', { eager: true });
|
|
386
|
+
|
|
387
|
+
// 2. Construct the builder with optional configuration
|
|
388
|
+
const builder = new RouterBuilder({
|
|
389
|
+
basePath: '/api',
|
|
390
|
+
debug: true, // Enable logging
|
|
391
|
+
onNotFound: () => notFound('Custom 404'),
|
|
392
|
+
onError: (err) => internalError(err)
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// 3. Add auto-registered routes and build
|
|
396
|
+
export const ALL = builder
|
|
397
|
+
.addRegistered()
|
|
398
|
+
.build();
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
> π‘ **The Catch-All Slug**: The filename `[...slug].ts` tells Astro to match any path under that directory. For example, if placed in `src/pages/api/[...slug].ts`, it matches `/api/users`, `/api/ping`, etc. `astro-routify` then takes over and matches the rest of the path against your defined routes.
|
|
402
|
+
|
|
403
|
+
> β οΈ In production (non-HMR) builds, duplicate route registrations with the same `method:path` MAY emit warnings and the last registration wins. In development/HMR flows, the registry intentionally preserves history and the builder deduplicates using a last-wins policy.
|
|
404
|
+
|
|
405
|
+
You can also still manually add routes or groups:
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
const users = defineGroup("/users")
|
|
409
|
+
.addGet("/:id", ({params}) => ok({id: params.id}));
|
|
410
|
+
|
|
411
|
+
builder.addGroup(users);
|
|
412
|
+
builder.addGet("/ping", () => ok("pong"));
|
|
165
413
|
```
|
|
166
414
|
|
|
415
|
+
## β‘ Advanced Features
|
|
416
|
+
|
|
167
417
|
### π Streaming responses
|
|
168
418
|
|
|
419
|
+
#### Lifecycle & Guarantees
|
|
420
|
+
- **Short-circuiting**: Returning a stream result (e.g., from `stream()`) short-circuits the middleware chain; `next()` must not be called after the response starts.
|
|
421
|
+
- **Abort Semantics**: If the client disconnects, the stream closes silently. Any internal controllers are closed via `AbortSignal`. Cleanup hooks should be idempotent.
|
|
422
|
+
|
|
169
423
|
#### Raw stream (e.g., Server-Sent Events)
|
|
170
424
|
|
|
425
|
+
`stream()` automatically handles SSE headers and auto-formats string chunks with a `state: ` prefix and double-newlines.
|
|
426
|
+
|
|
171
427
|
```ts
|
|
172
428
|
stream('/clock', async ({response}) => {
|
|
173
429
|
const timer = setInterval(() => {
|
|
430
|
+
// Automatically sent as "state: <iso-date>\n\n"
|
|
174
431
|
response.write(new Date().toISOString());
|
|
175
432
|
}, 1000);
|
|
176
433
|
|
|
@@ -208,6 +465,79 @@ streamJsonArray('/items', async ({response}) => {
|
|
|
208
465
|
|
|
209
466
|
```
|
|
210
467
|
|
|
468
|
+
### π OpenAPI (Swagger) Generation
|
|
469
|
+
|
|
470
|
+
Automatically generate API documentation from your router instance.
|
|
471
|
+
|
|
472
|
+
- **Catch-all (`**`)**: Represented as `{rest}` parameter.
|
|
473
|
+
- **Wildcard (`*`)**: Represented as `{any}` parameter.
|
|
474
|
+
- **Regex**: Mapped to a string schema with a `pattern` constraint.
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
import { generateOpenAPI } from 'astro-routify';
|
|
478
|
+
|
|
479
|
+
const router = builder.build();
|
|
480
|
+
const spec = generateOpenAPI(router, {
|
|
481
|
+
title: 'My API',
|
|
482
|
+
version: '1.0.0'
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Serve the spec
|
|
486
|
+
builder.addGet('/openapi.json', () => ok(spec));
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
> π While `.register()` is still available, it's **deprecated** in favor of `.addGroup()` and `.addRoute()` for better
|
|
490
|
+
> structure and reusability.
|
|
491
|
+
|
|
492
|
+
Your route files can export single routes, groups, or arrays:
|
|
493
|
+
|
|
494
|
+
```ts
|
|
495
|
+
// src/components/User/UserList.routes.ts
|
|
496
|
+
import { defineRoute, defineGroup, ok } from 'astro-routify';
|
|
497
|
+
|
|
498
|
+
export const GET = defineRoute('GET', '/users', () => ok([]));
|
|
499
|
+
|
|
500
|
+
export const AdminRoutes = defineGroup('/admin')
|
|
501
|
+
.addPost('/reset', () => ok('done'));
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
#### π Development & Debugging
|
|
505
|
+
|
|
506
|
+
`astro-routify` provides built-in logging to help you see your route table during development.
|
|
507
|
+
|
|
508
|
+
- **Auto-logging**: In development mode (`NODE_ENV=development`), `RouterBuilder` automatically prints the registered routes to the console when `build()` is called.
|
|
509
|
+
- **Match Tracing**: Set `debug: true` in `RouterOptions` to see a log of every incoming request and which route it matched (or why it failed with 404/405).
|
|
510
|
+
|
|
511
|
+
```ts
|
|
512
|
+
const router = new RouterBuilder({ debug: true });
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## π Response Helpers
|
|
518
|
+
|
|
519
|
+
Avoid boilerplate `new Response(JSON.stringify(...))`.
|
|
520
|
+
|
|
521
|
+
> π‘ **Header Precedence**: Explicit headers provided via `ResultResponse` (e.g., `ok(data, { 'Content-Type': '...' })`) always take precedence over inferred defaults.
|
|
522
|
+
|
|
523
|
+
```ts
|
|
524
|
+
import {fileResponse} from 'astro-routify';
|
|
525
|
+
|
|
526
|
+
ok(data); // 200 OK
|
|
527
|
+
created(data); // 201 Created
|
|
528
|
+
noContent(); // 204
|
|
529
|
+
notFound("Missing"); // 404
|
|
530
|
+
internalError(err); // 500
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### π File downloads
|
|
534
|
+
|
|
535
|
+
```ts
|
|
536
|
+
fileResponse(content, "application/pdf", "report.pdf"); // sets Content-Type and Content-Disposition
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
211
541
|
---
|
|
212
542
|
|
|
213
543
|
## π Param Matching
|
|
@@ -349,6 +679,15 @@ Results may vary slightly on different hardware.
|
|
|
349
679
|
|
|
350
680
|
---
|
|
351
681
|
|
|
682
|
+
## π― Non-goals
|
|
683
|
+
|
|
684
|
+
- **Not a replacement for Astro pages**: Use it for APIs, not for HTML rendering.
|
|
685
|
+
- **No runtime file watching**: Route discovery happens at startup.
|
|
686
|
+
- **No opinionated auth or ORM layer**: It's a router, not a framework.
|
|
687
|
+
- **No framework lock-in**: Works with any library (Zod, Valibot, etc.).
|
|
688
|
+
|
|
689
|
+
---
|
|
690
|
+
|
|
352
691
|
## π Designed to Scale
|
|
353
692
|
|
|
354
693
|
While focused on simplicity and speed today, `astro-routify` is designed to evolve β enabling more advanced routing
|
package/dist/core/RouteTrie.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { HttpMethod } from './HttpMethod';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Route } from './defineRoute';
|
|
3
3
|
interface RouteMatch {
|
|
4
|
-
|
|
4
|
+
route: Route | null;
|
|
5
5
|
allowed?: HttpMethod[];
|
|
6
6
|
params: Record<string, string | undefined>;
|
|
7
7
|
}
|
|
8
8
|
export declare class RouteTrie {
|
|
9
9
|
private readonly root;
|
|
10
|
-
insert(
|
|
10
|
+
insert(route: Route): void;
|
|
11
11
|
find(path: string, method: HttpMethod): RouteMatch;
|
|
12
|
+
private matchNode;
|
|
12
13
|
private segmentize;
|
|
13
14
|
}
|
|
14
15
|
export {};
|