@shaferllc/keel 0.66.0 → 0.74.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/AGENTS.md +169 -0
- package/README.md +44 -6
- package/bin/keel-mcp.mjs +9 -0
- package/dist/api/api.config.stub +9 -0
- package/dist/api/config.d.ts +13 -0
- package/dist/api/config.js +14 -0
- package/dist/api/index.d.ts +16 -0
- package/dist/api/index.js +13 -0
- package/dist/api/provider.d.ts +10 -0
- package/dist/api/provider.js +17 -0
- package/dist/api/query.d.ts +35 -0
- package/dist/api/query.js +42 -0
- package/dist/api/resource.d.ts +91 -0
- package/dist/api/resource.js +188 -0
- package/dist/core/application.d.ts +5 -5
- package/dist/core/application.js +8 -2
- package/dist/core/cache.d.ts +81 -5
- package/dist/core/cache.js +188 -23
- package/dist/core/cli/stubs.d.ts +26 -0
- package/dist/core/cli/stubs.js +225 -0
- package/dist/core/console-prompt.d.ts +79 -0
- package/dist/core/console-prompt.js +239 -0
- package/dist/core/console-ui.d.ts +96 -0
- package/dist/core/console-ui.js +187 -0
- package/dist/core/console.d.ts +188 -0
- package/dist/core/console.js +395 -0
- package/dist/core/database.d.ts +70 -1
- package/dist/core/database.js +174 -15
- package/dist/core/env.d.ts +96 -0
- package/dist/core/env.js +140 -0
- package/dist/core/events.d.ts +129 -5
- package/dist/core/events.js +165 -7
- package/dist/core/health.d.ts +141 -0
- package/dist/core/health.js +226 -0
- package/dist/core/helpers.d.ts +9 -3
- package/dist/core/helpers.js +11 -3
- package/dist/core/http/kernel.d.ts +2 -0
- package/dist/core/http/kernel.js +48 -0
- package/dist/core/http/router.d.ts +5 -5
- package/dist/core/http/router.js +5 -5
- package/dist/core/i18n.d.ts +162 -0
- package/dist/core/i18n.js +472 -0
- package/dist/core/index.d.ts +39 -13
- package/dist/core/index.js +19 -6
- package/dist/core/instrumentation.d.ts +113 -0
- package/dist/core/instrumentation.js +52 -0
- package/dist/core/lock.d.ts +139 -0
- package/dist/core/lock.js +215 -0
- package/dist/core/logger.d.ts +89 -4
- package/dist/core/logger.js +167 -22
- package/dist/core/mail.d.ts +128 -7
- package/dist/core/mail.js +264 -16
- package/dist/core/notification.js +10 -1
- package/dist/core/package.d.ts +120 -0
- package/dist/core/package.js +169 -0
- package/dist/core/pages.d.ts +108 -0
- package/dist/core/pages.js +199 -0
- package/dist/core/queue.d.ts +134 -9
- package/dist/core/queue.js +325 -14
- package/dist/core/repl.d.ts +33 -0
- package/dist/core/repl.js +88 -0
- package/dist/core/scheduler.js +6 -0
- package/dist/core/social.d.ts +4 -4
- package/dist/core/social.js +4 -4
- package/dist/core/storage.d.ts +159 -6
- package/dist/core/storage.js +299 -7
- package/dist/core/telemetry.d.ts +208 -0
- package/dist/core/telemetry.js +383 -0
- package/dist/core/template.d.ts +2 -3
- package/dist/core/template.js +2 -3
- package/dist/core/testing.d.ts +170 -1
- package/dist/core/testing.js +504 -2
- package/dist/db/d1.js +13 -0
- package/dist/db/pg.d.ts +13 -0
- package/dist/db/pg.js +46 -4
- package/dist/mcp/server.d.ts +19 -0
- package/dist/mcp/server.js +355 -0
- package/dist/openapi/config.d.ts +28 -0
- package/dist/openapi/config.js +25 -0
- package/dist/openapi/doc.d.ts +40 -0
- package/dist/openapi/doc.js +20 -0
- package/dist/openapi/export.d.ts +8 -0
- package/dist/openapi/export.js +19 -0
- package/dist/openapi/gate.d.ts +15 -0
- package/dist/openapi/gate.js +27 -0
- package/dist/openapi/index.d.ts +19 -0
- package/dist/openapi/index.js +15 -0
- package/dist/openapi/openapi.config.stub +29 -0
- package/dist/openapi/provider.d.ts +18 -0
- package/dist/openapi/provider.js +35 -0
- package/dist/openapi/routes.d.ts +9 -0
- package/dist/openapi/routes.js +23 -0
- package/dist/openapi/spec.d.ts +23 -0
- package/dist/openapi/spec.js +132 -0
- package/dist/openapi/ui.d.ts +8 -0
- package/dist/openapi/ui.js +31 -0
- package/dist/openapi/zod.d.ts +12 -0
- package/dist/openapi/zod.js +46 -0
- package/dist/watch/config.d.ts +33 -0
- package/dist/watch/config.js +38 -0
- package/dist/watch/entry.d.ts +53 -0
- package/dist/watch/entry.js +105 -0
- package/dist/watch/gate.d.ts +20 -0
- package/dist/watch/gate.js +32 -0
- package/dist/watch/index.d.ts +21 -0
- package/dist/watch/index.js +17 -0
- package/dist/watch/migration.d.ts +7 -0
- package/dist/watch/migration.js +20 -0
- package/dist/watch/provider.d.ts +22 -0
- package/dist/watch/provider.js +58 -0
- package/dist/watch/prune.d.ts +11 -0
- package/dist/watch/prune.js +20 -0
- package/dist/watch/recorder.d.ts +24 -0
- package/dist/watch/recorder.js +39 -0
- package/dist/watch/routes.d.ts +13 -0
- package/dist/watch/routes.js +55 -0
- package/dist/watch/store.d.ts +54 -0
- package/dist/watch/store.js +158 -0
- package/dist/watch/ui/dist/watch.css +1 -0
- package/dist/watch/ui/dist/watch.js +555 -0
- package/dist/watch/ui-shell.d.ts +12 -0
- package/dist/watch/ui-shell.js +24 -0
- package/dist/watch/watch.config.stub +47 -0
- package/dist/watch/watchers.d.ts +12 -0
- package/dist/watch/watchers.js +156 -0
- package/docs/ai-manifest.json +3206 -0
- package/docs/ai.md +128 -0
- package/docs/api-resources.md +118 -0
- package/docs/architecture.md +331 -0
- package/docs/authentication.md +453 -0
- package/docs/authorization.md +167 -0
- package/docs/broadcasting.md +137 -0
- package/docs/broker.md +500 -0
- package/docs/cache.md +558 -0
- package/docs/configuration.md +385 -0
- package/docs/console.md +536 -0
- package/docs/container.md +467 -0
- package/docs/controllers.md +265 -0
- package/docs/cors.md +51 -0
- package/docs/database.md +631 -0
- package/docs/debugging.md +129 -0
- package/docs/decorators.md +127 -0
- package/docs/errors.md +395 -0
- package/docs/events.md +496 -0
- package/docs/examples/architecture-app.ts +27 -0
- package/docs/examples/authentication.ts +61 -0
- package/docs/examples/authorization.ts +79 -0
- package/docs/examples/broadcasting.ts +60 -0
- package/docs/examples/broker-cache-validate.ts +34 -0
- package/docs/examples/broker-fault-tolerance.ts +29 -0
- package/docs/examples/broker-middleware.ts +27 -0
- package/docs/examples/broker.ts +203 -0
- package/docs/examples/cache.ts +222 -0
- package/docs/examples/configuration.ts +121 -0
- package/docs/examples/console.ts +134 -0
- package/docs/examples/container.ts +134 -0
- package/docs/examples/controllers.ts +86 -0
- package/docs/examples/database.ts +208 -0
- package/docs/examples/debugging.ts +41 -0
- package/docs/examples/decorators.ts +40 -0
- package/docs/examples/errors.ts +121 -0
- package/docs/examples/events.ts +204 -0
- package/docs/examples/factories.ts +84 -0
- package/docs/examples/hashing.ts +71 -0
- package/docs/examples/health.ts +94 -0
- package/docs/examples/helpers.ts +171 -0
- package/docs/examples/hooks.ts +54 -0
- package/docs/examples/i18n.ts +117 -0
- package/docs/examples/inertia.ts +81 -0
- package/docs/examples/locks.ts +120 -0
- package/docs/examples/logger.ts +166 -0
- package/docs/examples/mail.ts +263 -0
- package/docs/examples/middleware.ts +119 -0
- package/docs/examples/migrations.ts +126 -0
- package/docs/examples/models.ts +239 -0
- package/docs/examples/notification.ts +124 -0
- package/docs/examples/pages.ts +82 -0
- package/docs/examples/providers.ts +123 -0
- package/docs/examples/queues.ts +254 -0
- package/docs/examples/rate-limiting.ts +42 -0
- package/docs/examples/redis.ts +99 -0
- package/docs/examples/request-response.ts +197 -0
- package/docs/examples/routing.ts +186 -0
- package/docs/examples/scheduling.ts +62 -0
- package/docs/examples/sessions.ts +102 -0
- package/docs/examples/static-files.ts +63 -0
- package/docs/examples/storage.ts +132 -0
- package/docs/examples/telemetry.ts +127 -0
- package/docs/examples/templates.ts +58 -0
- package/docs/examples/testing.ts +215 -0
- package/docs/examples/transformer.ts +141 -0
- package/docs/examples/transformers.ts +49 -0
- package/docs/examples/url-builder.ts +86 -0
- package/docs/examples/validation.ts +102 -0
- package/docs/examples/views.tsx +62 -0
- package/docs/examples/vite.ts +106 -0
- package/docs/factories.md +166 -0
- package/docs/getting-started.md +290 -0
- package/docs/hashing.md +259 -0
- package/docs/health.md +225 -0
- package/docs/helpers.md +347 -0
- package/docs/hono.md +186 -0
- package/docs/hooks.md +118 -0
- package/docs/i18n.md +302 -0
- package/docs/inertia.md +241 -0
- package/docs/locks.md +323 -0
- package/docs/logger.md +436 -0
- package/docs/mail.md +751 -0
- package/docs/middleware.md +425 -0
- package/docs/migrations.md +476 -0
- package/docs/models.md +810 -0
- package/docs/notifications.md +474 -0
- package/docs/openapi.md +111 -0
- package/docs/packages.md +118 -0
- package/docs/pages.md +217 -0
- package/docs/providers.md +363 -0
- package/docs/queues.md +679 -0
- package/docs/rate-limiting.md +155 -0
- package/docs/redis.md +178 -0
- package/docs/request-response.md +953 -0
- package/docs/routing.md +804 -0
- package/docs/scheduling.md +110 -0
- package/docs/security.md +85 -0
- package/docs/sessions.md +354 -0
- package/docs/social-auth.md +174 -0
- package/docs/static-files.md +211 -0
- package/docs/storage.md +452 -0
- package/docs/telemetry.md +263 -0
- package/docs/templates.md +314 -0
- package/docs/testing.md +376 -0
- package/docs/transformers.md +381 -0
- package/docs/url-builder.md +295 -0
- package/docs/validation.md +288 -0
- package/docs/views.md +267 -0
- package/docs/vite.md +434 -0
- package/docs/watch.md +118 -0
- package/llms-full.txt +19816 -0
- package/llms.txt +127 -0
- package/package.json +42 -7
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# Middleware
|
|
2
|
+
|
|
3
|
+
Middleware wraps every request, running code before and after your route
|
|
4
|
+
handler. Keel uses Hono's middleware signature, so a middleware is just an async
|
|
5
|
+
function of `(c, next)` — the same shape you'd write for a bare Hono app.
|
|
6
|
+
|
|
7
|
+
There are two ways a middleware runs: **globally**, on every request (registered
|
|
8
|
+
in the HTTP kernel), or **per route/group**, attached where you declare the
|
|
9
|
+
route. Global middleware is for cross-cutting concerns (logging, CORS, request
|
|
10
|
+
IDs); route middleware is for guards that only some routes need (auth, admin).
|
|
11
|
+
|
|
12
|
+
## The HTTP kernel
|
|
13
|
+
|
|
14
|
+
Global middleware is registered in `app/Http/Kernel.ts`, which extends the
|
|
15
|
+
framework's `HttpKernel`:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { HttpKernel, Application } from "@shaferllc/keel/core";
|
|
19
|
+
import { requestLogger } from "./Middleware/requestLogger.js";
|
|
20
|
+
|
|
21
|
+
export class Kernel extends HttpKernel {
|
|
22
|
+
constructor(app: Application) {
|
|
23
|
+
super(app);
|
|
24
|
+
this.use(requestLogger); // runs on every request, in order
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Call `this.use(...)` once per middleware. They run in the order added. The kernel
|
|
30
|
+
wires a few internal middleware first — context storage and container binding —
|
|
31
|
+
so by the time your global middleware runs, `c.get("app")` (the container) is
|
|
32
|
+
already set. Stack several by chaining or calling `use` repeatedly:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
export class Kernel extends HttpKernel {
|
|
36
|
+
constructor(app: Application) {
|
|
37
|
+
super(app);
|
|
38
|
+
this.use(requestLogger).use(cors).use(requestId);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The kernel also compiles the router's routes onto a Hono instance (`build()`,
|
|
44
|
+
called by `keel serve` — you never call it yourself) and turns thrown exceptions
|
|
45
|
+
and unmatched routes into responses. To replace that default rendering, register
|
|
46
|
+
a custom error handler:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export class Kernel extends HttpKernel {
|
|
50
|
+
constructor(app: Application) {
|
|
51
|
+
super(app);
|
|
52
|
+
this.use(requestLogger);
|
|
53
|
+
this.onError((err, c) => c.json({ oops: String(err) }, 500));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
See [errors](./errors.md) for what the default handler does and how reportable
|
|
59
|
+
exceptions hook in.
|
|
60
|
+
|
|
61
|
+
## Writing middleware
|
|
62
|
+
|
|
63
|
+
A middleware is an async function of `(c, next)`. Do work, `await next()` to
|
|
64
|
+
pass control down the stack, then optionally do work on the way back up:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import type { MiddlewareHandler } from "hono";
|
|
68
|
+
|
|
69
|
+
export const requestLogger: MiddlewareHandler = async (c, next) => {
|
|
70
|
+
const start = performance.now();
|
|
71
|
+
await next(); // run the rest of the stack
|
|
72
|
+
const ms = (performance.now() - start).toFixed(1);
|
|
73
|
+
console.log(` ${c.req.method} ${c.req.path} → ${c.res.status} (${ms}ms)`);
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`c` is Hono's `Context` (Keel re-exports it as [`Ctx`](./routing.md)); `next`
|
|
78
|
+
advances to the next middleware, and eventually the route handler. Everything
|
|
79
|
+
before `await next()` runs on the way in; everything after runs on the way out,
|
|
80
|
+
in reverse order. Skip `next()` entirely to short-circuit (see below).
|
|
81
|
+
|
|
82
|
+
Generate a stub with:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm run keel make:middleware Auth
|
|
86
|
+
# -> app/Http/Middleware/authMiddleware.ts
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The generator strips a trailing `Middleware` from the name, PascalCases it, then
|
|
90
|
+
lower-cases the first letter for the filename and the exported const — so
|
|
91
|
+
`make:middleware Auth` writes `authMiddleware.ts` exporting `authMiddleware`, and
|
|
92
|
+
`make:middleware RateLimit` writes `rateLimitMiddleware.ts`. The stub is a ready
|
|
93
|
+
`MiddlewareHandler` with `before`/`after` markers around `await next()`.
|
|
94
|
+
|
|
95
|
+
## Named middleware
|
|
96
|
+
|
|
97
|
+
Register middleware by name once, then reference it by name on routes and groups
|
|
98
|
+
— no importing the function everywhere:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// routes/web.ts (or a service provider) — anywhere you hold the router.
|
|
102
|
+
router.named({
|
|
103
|
+
auth: authMiddleware,
|
|
104
|
+
admin: adminMiddleware,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
router.get("/dashboard", [DashboardController, "index"]).use("auth");
|
|
108
|
+
router.group(() => { /* … */ }).use(["auth", "admin"]);
|
|
109
|
+
router.resource("posts", PostController).use(["store", "update"], "auth");
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`router.named()` takes a map of names to **handlers** (functions, not other
|
|
113
|
+
names), and merges into any previously registered names — call it as many times
|
|
114
|
+
as you like. The `Router` is a singleton, so names registered on the instance
|
|
115
|
+
passed to your route file are the same ones the kernel resolves at build time.
|
|
116
|
+
|
|
117
|
+
You can still pass raw functions anywhere a name is accepted — the `.use()` /
|
|
118
|
+
`.middleware()` argument is a `MiddlewareRef` (`MiddlewareHandler | string`), so
|
|
119
|
+
you mix and match:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
router.get("/reports", [ReportController, "index"]).use(["auth", auditLog]);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Referencing an unregistered name throws when the app builds —
|
|
126
|
+
`No named middleware [auth]. Register it with router.named({ auth: … }).` — so
|
|
127
|
+
typos surface immediately at boot, not on the first matching request.
|
|
128
|
+
|
|
129
|
+
For **parameterized** middleware, use a factory that returns a handler:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const role = (name: string): MiddlewareHandler => async (c, next) => {
|
|
133
|
+
// check role === name …
|
|
134
|
+
await next();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
router.get("/admin", handler).use(role("admin"));
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
You can register the *result* of a factory as a name, too, if a fixed
|
|
141
|
+
configuration recurs:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
router.named({ admin: role("admin"), editor: role("editor") });
|
|
145
|
+
router.get("/admin", handler).use("admin");
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Short-circuiting
|
|
149
|
+
|
|
150
|
+
Return a response _without_ calling `next()` to stop the request early — handy
|
|
151
|
+
for auth guards:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
export const requireApiKey: MiddlewareHandler = async (c, next) => {
|
|
155
|
+
if (c.req.header("x-api-key") !== process.env.API_KEY) {
|
|
156
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
157
|
+
}
|
|
158
|
+
await next();
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Because the handler and every inner middleware never run, short-circuiting is how
|
|
163
|
+
guards enforce access. You can also `throw` an [HTTP exception](./errors.md)
|
|
164
|
+
instead of returning — the kernel's error handler renders it:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import { UnauthorizedException } from "@shaferllc/keel/core";
|
|
168
|
+
|
|
169
|
+
export const requireAuth: MiddlewareHandler = async (c, next) => {
|
|
170
|
+
if (!c.get("app").make(Auth).check()) throw new UnauthorizedException();
|
|
171
|
+
await next();
|
|
172
|
+
};
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Sharing data with handlers
|
|
176
|
+
|
|
177
|
+
Stash values on the context; downstream handlers read them back. Keel already
|
|
178
|
+
does this to expose the container as `c.get("app")`:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
export const withUser: MiddlewareHandler = async (c, next) => {
|
|
182
|
+
c.set("user", await lookupUser(c.req.header("authorization")));
|
|
183
|
+
await next();
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// later, in a handler:
|
|
187
|
+
const user = c.get("user");
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
To get type safety on custom context variables, augment Hono's
|
|
191
|
+
`ContextVariableMap` (see [`src/core/hono.d.ts`](../src/core/hono.d.ts) for how
|
|
192
|
+
Keel does it for `app`, `route`, `subdomains`, and `session`):
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
declare module "hono" {
|
|
196
|
+
interface ContextVariableMap {
|
|
197
|
+
user?: { id: number; name: string };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
With that in place `c.set("user", …)` and `c.get("user")` are fully typed
|
|
203
|
+
everywhere.
|
|
204
|
+
|
|
205
|
+
## Order of execution
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
requestLogger ┐ ┌ requestLogger
|
|
209
|
+
▼ │
|
|
210
|
+
requireApiKey ─┼─► handler ─┘
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Middleware added first is outermost: it runs first on the way in and last on the
|
|
214
|
+
way out. The full order for any request is:
|
|
215
|
+
|
|
216
|
+
1. Keel's internal middleware (context storage, container/subdomain binding).
|
|
217
|
+
2. Global middleware, in the order you `use()`d them in the kernel.
|
|
218
|
+
3. Group middleware, outermost group first.
|
|
219
|
+
4. Per-route middleware, in the order attached.
|
|
220
|
+
5. The route handler.
|
|
221
|
+
|
|
222
|
+
Group middleware always runs *before* the route's own middleware — a group
|
|
223
|
+
prepends its middleware to each contained route. So this:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
router.group(() => {
|
|
227
|
+
router.get("/posts/:id/edit", handler).use("owns-post");
|
|
228
|
+
}).use("auth");
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
runs `auth` (group), then `owns-post` (route), then `handler` — a natural
|
|
232
|
+
"authenticated, *and* owns this post" gate.
|
|
233
|
+
|
|
234
|
+
## Related
|
|
235
|
+
|
|
236
|
+
Attaching middleware to routes, groups, and resources is part of the
|
|
237
|
+
[routing](./routing.md) API — this page covers writing and registering the
|
|
238
|
+
handlers; routing covers where they hang.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## API reference
|
|
243
|
+
|
|
244
|
+
### `HttpKernel`
|
|
245
|
+
|
|
246
|
+
The base class your `app/Http/Kernel.ts` extends. It owns the global middleware
|
|
247
|
+
stack, compiles routes onto Hono, and renders errors. You construct your subclass
|
|
248
|
+
(the bootstrap does, via `app.singleton(HttpKernel, …)`); you don't construct
|
|
249
|
+
`HttpKernel` directly.
|
|
250
|
+
|
|
251
|
+
#### `use(mw)`
|
|
252
|
+
|
|
253
|
+
`use(mw: MiddlewareHandler): this`
|
|
254
|
+
|
|
255
|
+
Appends a middleware to the global stack — it runs on every request, in the order
|
|
256
|
+
added.
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
export class Kernel extends HttpKernel {
|
|
260
|
+
constructor(app: Application) {
|
|
261
|
+
super(app);
|
|
262
|
+
this.use(requestLogger).use(cors);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Notes:** returns `this`, so calls chain. Runs after Keel's internal setup
|
|
268
|
+
middleware, so `c.get("app")` is available. Only accepts a `MiddlewareHandler`
|
|
269
|
+
(a function) — global middleware isn't named, so there's no string form here.
|
|
270
|
+
|
|
271
|
+
#### `onError(handler)`
|
|
272
|
+
|
|
273
|
+
`onError(handler: (err: unknown, c: Context) => Response | Promise<Response>): this`
|
|
274
|
+
|
|
275
|
+
Registers a custom error handler that takes precedence over the default
|
|
276
|
+
HTML/JSON exception rendering.
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
this.onError((err, c) => c.json({ error: String(err) }, 500));
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Notes:** returns `this`. Reportable exceptions still get their `report()` hook
|
|
283
|
+
called before your handler runs; your handler fully replaces the default
|
|
284
|
+
`renderException` output. Last call wins.
|
|
285
|
+
|
|
286
|
+
#### `build()`
|
|
287
|
+
|
|
288
|
+
`build(): Hono`
|
|
289
|
+
|
|
290
|
+
Compiles the router's collected routes onto a fresh Hono instance, mounting the
|
|
291
|
+
global middleware, per-route middleware, domain dispatch, and the not-found /
|
|
292
|
+
error handlers.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
const hono = app.make(HttpKernel).build(); // done for you by `keel serve`
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Notes:** called by the framework at boot — you rarely call it yourself. Routes
|
|
299
|
+
bound to a `domain(...)` are compiled into per-host sub-apps and dispatched by
|
|
300
|
+
the `Host` header; everything else lands on the default app.
|
|
301
|
+
|
|
302
|
+
### `Router` (middleware methods)
|
|
303
|
+
|
|
304
|
+
The router (a container singleton — `app.make(Router)`) is where named middleware
|
|
305
|
+
lives. Its routing methods are documented in [routing](./routing.md); the
|
|
306
|
+
middleware-related surface is below.
|
|
307
|
+
|
|
308
|
+
#### `named(map)`
|
|
309
|
+
|
|
310
|
+
`named(map: Record<string, MiddlewareHandler>): this`
|
|
311
|
+
|
|
312
|
+
Registers named middleware, referenceable by name in `.use()` / `.middleware()`
|
|
313
|
+
on routes, groups, and resources.
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
router.named({ auth: authMiddleware, admin: adminMiddleware });
|
|
317
|
+
router.get("/dashboard", handler).use("auth");
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Notes:** merges into previously registered names (call it repeatedly). Values
|
|
321
|
+
must be handlers, not other names. Returns `this`.
|
|
322
|
+
|
|
323
|
+
#### `resolveMiddleware(ref)`
|
|
324
|
+
|
|
325
|
+
`resolveMiddleware(ref: MiddlewareRef): MiddlewareHandler`
|
|
326
|
+
|
|
327
|
+
Resolves a `MiddlewareRef` — a handler passes through unchanged; a string is
|
|
328
|
+
looked up in the named registry.
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
const mw = router.resolveMiddleware("auth"); // the registered authMiddleware
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Notes:** called by the kernel while compiling each route; you rarely call it
|
|
335
|
+
directly. Throws `No named middleware [name]. Register it with router.named({ name: … }).`
|
|
336
|
+
for an unknown name — which surfaces at build time, catching typos at boot.
|
|
337
|
+
|
|
338
|
+
### Applying middleware to routes
|
|
339
|
+
|
|
340
|
+
These live on the route builders returned by the router. Full signatures and
|
|
341
|
+
examples are in [routing](./routing.md); the middleware-relevant ones:
|
|
342
|
+
|
|
343
|
+
#### `Route.middleware(mw)` / `Route.use(mw)`
|
|
344
|
+
|
|
345
|
+
`middleware(mw: MiddlewareRef | MiddlewareRef[]): this`
|
|
346
|
+
`use(mw: MiddlewareRef | MiddlewareRef[]): this`
|
|
347
|
+
|
|
348
|
+
Attaches middleware that runs only for this route (after any group middleware).
|
|
349
|
+
`use` is an alias for `middleware`.
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
router.get("/dashboard", handler).use("auth");
|
|
353
|
+
router.get("/reports", handler).middleware(["auth", auditLog]);
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Notes:** appends — call repeatedly or pass an array to add several. Accepts
|
|
357
|
+
names or raw handlers (`MiddlewareRef`).
|
|
358
|
+
|
|
359
|
+
#### `RouteGroup.middleware(mw)` / `RouteGroup.use(mw)`
|
|
360
|
+
|
|
361
|
+
`middleware(mw: MiddlewareRef | MiddlewareRef[]): this`
|
|
362
|
+
`use(mw: MiddlewareRef | MiddlewareRef[]): this`
|
|
363
|
+
|
|
364
|
+
Attaches middleware to every route in the group. `use` is an alias.
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
router.group(() => {
|
|
368
|
+
router.get("/dashboard", handler);
|
|
369
|
+
router.get("/settings", handler);
|
|
370
|
+
}).use(["auth", "admin"]);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Notes:** *prepends* to each route's middleware, so group middleware runs before
|
|
374
|
+
per-route middleware. Groups nest: an outer group's middleware wraps an inner
|
|
375
|
+
group's.
|
|
376
|
+
|
|
377
|
+
#### `RouteResource.use(actions, mw)`
|
|
378
|
+
|
|
379
|
+
`use(actions: string[] | "*", mw: MiddlewareRef | MiddlewareRef[]): this`
|
|
380
|
+
|
|
381
|
+
Attaches middleware to specific resource actions (`index`, `store`, `show`, …),
|
|
382
|
+
or `"*"` for all of them.
|
|
383
|
+
|
|
384
|
+
```ts
|
|
385
|
+
router.resource("posts", PostController).use(["store", "update", "destroy"], "auth");
|
|
386
|
+
router.resource("admin", AdminController).use("*", "admin");
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Notes:** the action names are the RESTful set (`index`, `create`, `store`,
|
|
390
|
+
`show`, `edit`, `update`, `destroy`). Non-matching actions are left untouched.
|
|
391
|
+
|
|
392
|
+
### Interfaces & types
|
|
393
|
+
|
|
394
|
+
#### `MiddlewareHandler` (from `hono`)
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
type MiddlewareHandler = (c: Context, next: Next) => Promise<Response | void>;
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The shape every middleware implements — imported from `hono`, not Keel. Do work,
|
|
401
|
+
`await next()` to continue the stack, return a `Response` (or nothing). Return
|
|
402
|
+
early without calling `next()` to short-circuit.
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
import type { MiddlewareHandler } from "hono";
|
|
406
|
+
|
|
407
|
+
export const noCache: MiddlewareHandler = async (c, next) => {
|
|
408
|
+
await next();
|
|
409
|
+
c.header("Cache-Control", "no-store");
|
|
410
|
+
};
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### `MiddlewareRef`
|
|
414
|
+
|
|
415
|
+
`type MiddlewareRef = MiddlewareHandler | string`
|
|
416
|
+
|
|
417
|
+
What `.use()` / `.middleware()` accept: either a middleware handler, or the name
|
|
418
|
+
of one registered with `router.named()`.
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
import type { MiddlewareRef } from "@shaferllc/keel/core";
|
|
422
|
+
|
|
423
|
+
const guards: MiddlewareRef[] = ["auth", auditLog]; // names and functions mix
|
|
424
|
+
router.get("/reports", handler).use(guards);
|
|
425
|
+
```
|