@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,265 @@
|
|
|
1
|
+
# Controllers
|
|
2
|
+
|
|
3
|
+
Controllers are plain classes in `app/Controllers/`. Each public method is an
|
|
4
|
+
action bound to a route. They're resolved from the [container](./container.md),
|
|
5
|
+
so they get dependency injection and a fresh instance per request.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import type { Ctx } from "@shaferllc/keel/core";
|
|
9
|
+
import { json, param } from "@shaferllc/keel/core";
|
|
10
|
+
|
|
11
|
+
export class PostController {
|
|
12
|
+
index() {
|
|
13
|
+
return json({ posts: [] });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
show() {
|
|
17
|
+
return json({ id: param("id") });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Bind actions in your routes with a `[Controller, method]` tuple:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
router.get("/posts", [PostController, "index"]);
|
|
26
|
+
router.get("/posts/:id", [PostController, "show"]);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
A controller action is just one of the [three handler forms](./routing.md#three-kinds-of-handler)
|
|
30
|
+
the router accepts — the array form. The other two (a closure, or a ready-made
|
|
31
|
+
`Response`) live inline on the route; controllers are for anything with enough
|
|
32
|
+
weight to earn its own class.
|
|
33
|
+
|
|
34
|
+
## The request context
|
|
35
|
+
|
|
36
|
+
Every action is called with the request [context](./request-response.md) as its
|
|
37
|
+
first argument — the `Ctx` type, which is Hono's `Context`. You can read from it
|
|
38
|
+
directly, or ignore it and reach for the ambient request helpers (`param`,
|
|
39
|
+
`query`, `body`) that resolve the current request from async-context storage.
|
|
40
|
+
Both styles work; pick whichever reads better.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import type { Ctx } from "@shaferllc/keel/core";
|
|
44
|
+
import { param } from "@shaferllc/keel/core";
|
|
45
|
+
|
|
46
|
+
export class PostController {
|
|
47
|
+
// Take the context explicitly…
|
|
48
|
+
show(c: Ctx) {
|
|
49
|
+
return c.json({ id: c.req.param("id") });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// …or lean on the ambient helpers and drop the argument.
|
|
53
|
+
edit() {
|
|
54
|
+
return json({ id: param("id") });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The context is what Keel hands the action under the hood — the router resolves
|
|
60
|
+
your controller from the container, then calls
|
|
61
|
+
`action.call(controller, c)`, so `this` is the controller instance and the sole
|
|
62
|
+
argument is the `Ctx`.
|
|
63
|
+
|
|
64
|
+
## Dependency injection
|
|
65
|
+
|
|
66
|
+
A controller's constructor receives the container, so it can resolve anything:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import type { Container, Ctx } from "@shaferllc/keel/core";
|
|
70
|
+
import { Mailer } from "../Services/Mailer.js";
|
|
71
|
+
|
|
72
|
+
export class UserController {
|
|
73
|
+
constructor(private app: Container) {}
|
|
74
|
+
|
|
75
|
+
store() {
|
|
76
|
+
const mailer = this.app.make(Mailer);
|
|
77
|
+
// …
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The container instantiates the controller with `new Controller(container)` — an
|
|
83
|
+
unbound class is auto-built, no registration needed. That happens **per
|
|
84
|
+
request**: each hit resolves a fresh instance, so it's safe to stash
|
|
85
|
+
request-scoped state on `this` without leaking it across requests.
|
|
86
|
+
|
|
87
|
+
If you'd rather inject specific services than the whole container, give the
|
|
88
|
+
controller a constructor that takes them and bind it in a
|
|
89
|
+
[service provider](./providers.md), pulling each dependency out of the container:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// InvoiceController's constructor takes a Mailer, not the container.
|
|
93
|
+
app.bind(InvoiceController, (c) => new InvoiceController(c.make(Mailer)));
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Single-action controllers
|
|
97
|
+
|
|
98
|
+
For a controller that does one thing, define a `handle` method and reference the
|
|
99
|
+
class with no method name:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
export class PublishPost {
|
|
103
|
+
handle() {
|
|
104
|
+
return json({ published: true });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
router.post("/posts/:id/publish", [PublishPost]); // calls handle()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`[Controller]` and `[Controller, "handle"]` are equivalent — the method name
|
|
112
|
+
defaults to `"handle"` when the tuple has just one element. Referencing a method
|
|
113
|
+
the controller doesn't define throws at request time:
|
|
114
|
+
`Controller [PublishPost] has no method [handle].`
|
|
115
|
+
|
|
116
|
+
## Lazy-loaded controllers
|
|
117
|
+
|
|
118
|
+
Pass a `() => import(...)` loader instead of the class, and the controller is
|
|
119
|
+
only imported when its route is first hit — handy for large apps and cold
|
|
120
|
+
starts:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
router.get("/reports", [() => import("../Controllers/ReportController.js"), "index"]);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The loader may resolve to a default export or the class itself — Keel unwraps
|
|
127
|
+
`.default` if present, otherwise uses the module value directly. Both work:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
// default export
|
|
131
|
+
export default class ReportController { index() { /* … */ } }
|
|
132
|
+
|
|
133
|
+
// named export — point the loader at the property
|
|
134
|
+
router.get("/reports", [
|
|
135
|
+
() => import("../Controllers/ReportController.js").then((m) => m.ReportController),
|
|
136
|
+
"index",
|
|
137
|
+
]);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The loader must be an **arrow function** (or any function with no `prototype`).
|
|
141
|
+
Keel distinguishes an eager controller from a lazy loader by checking for a
|
|
142
|
+
`prototype` — classes have one, arrow functions don't — so a lazy controller
|
|
143
|
+
written as a `function` declaration would be mistaken for a class. Stick to
|
|
144
|
+
`() => import(...)`.
|
|
145
|
+
|
|
146
|
+
## Resource controllers
|
|
147
|
+
|
|
148
|
+
Generate a RESTful controller with all seven actions:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm run keel make:controller Post --resource
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
That writes `app/Controllers/PostController.ts` with the conventional set —
|
|
155
|
+
`index`, `create`, `store`, `show`, `edit`, `update`, `destroy`:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import type { Ctx } from "@shaferllc/keel/core";
|
|
159
|
+
|
|
160
|
+
export class PostController {
|
|
161
|
+
index(c: Ctx) { return c.json({ action: "index" }); }
|
|
162
|
+
create(c: Ctx) { return c.json({ action: "create" }); }
|
|
163
|
+
store(c: Ctx) { return c.json({ action: "store" }); }
|
|
164
|
+
show(c: Ctx) { return c.json({ action: "show" }); }
|
|
165
|
+
edit(c: Ctx) { return c.json({ action: "edit" }); }
|
|
166
|
+
update(c: Ctx) { return c.json({ action: "update" }); }
|
|
167
|
+
destroy(c: Ctx) { return c.json({ action: "destroy" }); }
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Drop `--resource` (or `-r`) for a bare controller with a single `index` action.
|
|
172
|
+
|
|
173
|
+
Then wire the whole set up in one line (see [Routing → Resource routes](./routing.md#resource-routes)):
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
router.resource("posts", PostController);
|
|
177
|
+
router.resource("posts.comments", CommentController); // nested
|
|
178
|
+
router.resource("posts", PostController)
|
|
179
|
+
.apiOnly()
|
|
180
|
+
.as("articles")
|
|
181
|
+
.params({ posts: "post" })
|
|
182
|
+
.use(["store", "update", "destroy"], auth);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`router.resource` maps each of the seven route entries onto the matching
|
|
186
|
+
controller method by name — so a resource controller just needs methods with
|
|
187
|
+
those names. Trim the set with `.only()` / `.except()` / `.apiOnly()` when you
|
|
188
|
+
don't implement all seven.
|
|
189
|
+
|
|
190
|
+
## Related
|
|
191
|
+
|
|
192
|
+
- [Routing](./routing.md) — the `Router` methods (`get`, `post`, `resource`, …)
|
|
193
|
+
that bind these controllers, plus closures and static-response handlers.
|
|
194
|
+
- [Container](./container.md) — how controllers (and their dependencies) are
|
|
195
|
+
resolved and constructed.
|
|
196
|
+
- [Request & response](./request-response.md) — the `Ctx` object and the ambient
|
|
197
|
+
`param` / `query` / `json` helpers actions use.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## API reference
|
|
202
|
+
|
|
203
|
+
Controllers are a usage pattern, not an exported API — you write the classes, and
|
|
204
|
+
the [`Router`](./routing.md) binds them. The two exported types you touch when
|
|
205
|
+
typing an action or a route are `Ctx` and `RouteHandler`, both from
|
|
206
|
+
`@shaferllc/keel/core`.
|
|
207
|
+
|
|
208
|
+
### Types
|
|
209
|
+
|
|
210
|
+
#### `Ctx`
|
|
211
|
+
|
|
212
|
+
`type Ctx = Context` (Hono's request `Context`)
|
|
213
|
+
|
|
214
|
+
The request context passed as the first argument to every route handler and
|
|
215
|
+
controller action. Read params/headers/body off it and build responses with it —
|
|
216
|
+
or ignore it and use the ambient request helpers.
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import type { Ctx } from "@shaferllc/keel/core";
|
|
220
|
+
|
|
221
|
+
export class UserController {
|
|
222
|
+
show(c: Ctx) {
|
|
223
|
+
const id = c.req.param("id"); // route param
|
|
224
|
+
const q = c.req.query("expand"); // query string
|
|
225
|
+
return c.json({ id, expand: q }); // JSON response
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Notes:** it's an alias for Hono's `Context`, so anything in Hono's context API
|
|
231
|
+
(`c.req`, `c.json`, `c.html`, `c.header`, `c.get`/`c.set`, `c.env`) is available.
|
|
232
|
+
An action may also take no argument and use `param()`/`query()`/`json()` instead,
|
|
233
|
+
which resolve the current request from async-context storage — see
|
|
234
|
+
[request & response](./request-response.md).
|
|
235
|
+
|
|
236
|
+
#### `RouteHandler`
|
|
237
|
+
|
|
238
|
+
`type RouteHandler = HandlerFn | ControllerAction | Response`
|
|
239
|
+
|
|
240
|
+
The union every `Router` verb accepts as its handler. A controller action is the
|
|
241
|
+
`ControllerAction` arm: `[Controller]`, `[Controller, "method"]`, or a lazy
|
|
242
|
+
`[() => import(...), "method"]`.
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import type { RouteHandler } from "@shaferllc/keel/core";
|
|
246
|
+
import { json } from "@shaferllc/keel/core";
|
|
247
|
+
|
|
248
|
+
// each arm of the union is a valid handler
|
|
249
|
+
const closure: RouteHandler = () => json({ ok: true });
|
|
250
|
+
const staticResp: RouteHandler = json({ status: "ok" });
|
|
251
|
+
const action: RouteHandler = [UserController, "show"];
|
|
252
|
+
const single: RouteHandler = [PublishPost]; // calls handle()
|
|
253
|
+
const lazy: RouteHandler = [
|
|
254
|
+
() => import("../Controllers/ReportController.js"),
|
|
255
|
+
"index",
|
|
256
|
+
];
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Notes:** the constituent types (`HandlerFn`, `ControllerAction`,
|
|
260
|
+
`ControllerRef`, `LazyController`) are internal to the router and not exported —
|
|
261
|
+
annotate values as `RouteHandler` when you need an explicit type. The router
|
|
262
|
+
turns any of these into an executable function at boot; a controller arm is
|
|
263
|
+
resolved from the container per request. For the verbs that consume a
|
|
264
|
+
`RouteHandler` (`get`, `post`, `put`, `patch`, `delete`, `any`, `route`,
|
|
265
|
+
`resource`, …), see [Routing → API reference](./routing.md#api-reference).
|
package/docs/cors.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# CORS
|
|
2
|
+
|
|
3
|
+
Cross-Origin Resource Sharing lets browsers on other origins call your API. The
|
|
4
|
+
`cors()` middleware sets the `Access-Control-*` headers and answers preflight
|
|
5
|
+
`OPTIONS` requests for you.
|
|
6
|
+
|
|
7
|
+
## Enabling
|
|
8
|
+
|
|
9
|
+
Register it in your [HTTP kernel](./middleware.md) (app-wide) or on a route group:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { cors } from "@shaferllc/keel/core";
|
|
13
|
+
|
|
14
|
+
// In the kernel — applies to every route
|
|
15
|
+
this.use(cors());
|
|
16
|
+
|
|
17
|
+
// Or scoped to an API group
|
|
18
|
+
router.group(() => { /* … */ }).use(cors({ origin: ["https://app.example.com"] }));
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
With no options, `cors()` reflects the caller's origin — convenient in
|
|
22
|
+
development. **Lock it down in production** with an explicit allowlist.
|
|
23
|
+
|
|
24
|
+
## Options
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
cors({
|
|
28
|
+
origin: ["https://app.example.com"], // true (reflect) | false | "*" | string[] | (origin, c) => …
|
|
29
|
+
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
30
|
+
headers: true, // true (reflect requested) | string[]
|
|
31
|
+
exposeHeaders: ["X-Request-Id"], // response headers JS may read
|
|
32
|
+
credentials: true, // send Access-Control-Allow-Credentials
|
|
33
|
+
maxAge: 86400, // preflight cache seconds; null to omit
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- **`origin`** — `true` reflects the request origin, `false` blocks everything,
|
|
38
|
+
`"*"` allows any, an array is an allowlist, and a `(origin, c) => …` predicate
|
|
39
|
+
returns `true`/`false`/a specific origin for dynamic decisions (e.g. any
|
|
40
|
+
`localhost` port in dev).
|
|
41
|
+
- **`credentials`** — when on, the spec forbids `"*"`, so `cors()` automatically
|
|
42
|
+
reflects the concrete origin and adds `Vary: Origin`.
|
|
43
|
+
- **`headers`** — `true` echoes whatever the browser asks for in the preflight;
|
|
44
|
+
an array pins an explicit allowlist.
|
|
45
|
+
|
|
46
|
+
## Preflight
|
|
47
|
+
|
|
48
|
+
Browsers send an `OPTIONS` request with `Access-Control-Request-Method` before
|
|
49
|
+
certain cross-origin calls. `cors()` detects these and responds `204` with the
|
|
50
|
+
allow headers directly — your route never runs. Everything else falls through to
|
|
51
|
+
your handler with the CORS response headers attached.
|