@nano-app/plugins 0.9.0 → 0.11.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
CHANGED
|
@@ -19,9 +19,9 @@ createNanoApp({ plugins: [health()] });
|
|
|
19
19
|
|
|
20
20
|
| Plugin | Import | Endpoints | Options |
|
|
21
21
|
| -------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
|
|
22
|
-
| `health` | `import health from '@nano-app/plugins/health'` | `GET /health` → `{ success: true, result: { status: "ok" } }
|
|
23
|
-
| `crud` | `import crud from '@nano-app/plugins/crud'` | On the tables listed in `models`, by their name in the app's schema (any other answers 404): `GET` and `QUERY` on `/api/<model>{/<id>}` → a list `{ result }` or one record, with `QueryParams` as the body of `QUERY`: Drizzle's relational query (`where`, `columns`, `orderBy`, `limit`, `offset`, `with`); `POST /api/<model>`, `PUT` and `DELETE` on `/api/<model>/<id>` → the record created, updated or deleted. Postgres casts `<id>` to the type of the `id` column. With Better Auth, each method asks for a permission on the table: `read` (`GET`, `QUERY`), `create`, `update`, `delete`, see [crud and related tables](#crud-and-related-tables) | `models` (the published ones), `dbSchema` (the app's Drizzle schema, as in `
|
|
24
|
-
| `rate-limit` | `import rateLimit from '@nano-app/plugins/rate-limit'` | None: limits every request of the routes and plugins mounted after it, per client IP (see [Client IP](../backend/README.md#client-ip)); the addresses of an IPv6 `/56` network are one client. Each request spends a token of the client's bucket, which holds `capacity` and refills `refillPerSecond` tokens per second; without one it answers `429` with `message` and `Retry-After` (seconds until the next token). List it first in `plugins`. The buckets live in the memory of each process | `capacity` (default `100`), `refillPerSecond` (default `1`), `message` |
|
|
22
|
+
| `health` | `import health from '@nano-app/plugins/health'` | `GET /health` → `{ success: true, result: { status: "ok" } }`. Public: it answers without a session | `basePath` (default `"/health"`) |
|
|
23
|
+
| `crud` | `import crud from '@nano-app/plugins/crud'` | On the tables listed in `models`, by their name in the app's schema (any other answers 404): `GET` and `QUERY` on `/api/<model>{/<id>}` → a list `{ result }` or one record, with `QueryParams` as the body of `QUERY`: Drizzle's relational query (`where`, `columns`, `orderBy`, `limit`, `offset`, `with`); `POST /api/<model>`, `PUT` and `DELETE` on `/api/<model>/<id>` → the record created, updated or deleted. Postgres casts `<id>` to the type of the `id` column. With Better Auth, each method asks for a permission on the table: `read` (`GET`, `QUERY`), `create`, `update`, `delete`, see [crud and related tables](#crud-and-related-tables) | `models` (the published ones), `dbSchema` (the app's Drizzle schema, as in `database.schema`), `validations` (zod schemas of the bodies, see [crud and the bodies](#crud-and-the-bodies)), `basePath` (default `"/api"`) |
|
|
24
|
+
| `rate-limit` | `import rateLimit from '@nano-app/plugins/rate-limit'` | None: limits every request of the routes and plugins mounted after it, per client IP (see [Client IP](../backend/README.md#client-ip)); the addresses of an IPv6 `/56` network are one client. Each request spends a token of the client's bucket, which holds `capacity` and refills `refillPerSecond` tokens per second; without one it answers `429` with `message` and `Retry-After` (seconds until the next token). List it first in `plugins.before`. The buckets live in the memory of each process | `capacity` (default `100`), `refillPerSecond` (default `1`), `message` |
|
|
25
25
|
|
|
26
26
|
What is not CRUD, such as creating a record with business logic, goes in a route of your own with its own URL: a route of yours on a `crud` URL takes every method of it.
|
|
27
27
|
|
|
@@ -49,7 +49,7 @@ crud({
|
|
|
49
49
|
|
|
50
50
|
### crud and related tables
|
|
51
51
|
|
|
52
|
-
A `crud` query runs on the app's client, so it reaches the related tables Drizzle's relations give: `with` loads them and `where` filters by them, also the ones that are not in `models`. With Better Auth, the permission asked is only the one of the URL's table. What a query may load is up to the app: the `read` validation of the model decides it (a `z.strictObject` without `with` keeps a query in its table). Better Auth's tables
|
|
52
|
+
A `crud` query runs on the app's client, so it reaches the related tables Drizzle's relations give: `with` loads them and `where` filters by them, also the ones that are not in `models`. With Better Auth, the permission asked is only the one of the URL's table. What a query may load is up to the app: the `read` validation of the model decides it (a `z.strictObject` without `with` keeps a query in its table). Better Auth's tables live in `nano_auth`, outside the app's schema: `crud` does not reach them. A Postgres error of a write (a duplicate, a missing reference) answers 500.
|
|
53
53
|
|
|
54
54
|
### Layout
|
|
55
55
|
|
|
@@ -86,7 +86,6 @@ import { fileURLToPath } from 'node:url';
|
|
|
86
86
|
export default createPlugin((options?: { basePath?: string }) => ({
|
|
87
87
|
name: 'hello',
|
|
88
88
|
basePath: options?.basePath ?? '/hello',
|
|
89
|
-
stage: 'before',
|
|
90
89
|
routesDir: resolve(dirname(fileURLToPath(import.meta.url)), 'routes'),
|
|
91
90
|
}));
|
|
92
91
|
```
|
|
@@ -105,10 +104,10 @@ export default createRoute({
|
|
|
105
104
|
import { createNanoApp } from '@nano-app/backend';
|
|
106
105
|
import hello from './plugins/hello';
|
|
107
106
|
|
|
108
|
-
createNanoApp({ plugins: [hello()] });
|
|
107
|
+
createNanoApp({ plugins: { before: [hello()] } });
|
|
109
108
|
```
|
|
110
109
|
|
|
111
|
-
|
|
110
|
+
The app lists it in `plugins.before` or `plugins.after` its own routes. Mount order: base middlewares, Better Auth, `before` plugins, your routes, `after` plugins, 404, error handler. With Better Auth on, its routes need a session unless they are `public`.
|
|
112
111
|
|
|
113
112
|
Set `options` in the returned config to pass values to the plugin's routes: every handler and callback receives them in `params`, next to `db`. Callbacks: `callbacks.onMount(app, params)` on the plugin, once, before its endpoints (app-level middlewares go here); `onInit(params)` and `onBefore(req, method, params)` on each route. See the [backend README](../backend/README.md#callbacks).
|
|
114
113
|
|
|
@@ -129,15 +128,15 @@ Publish a separate package. Do not add it to this repository unless a maintainer
|
|
|
129
128
|
}
|
|
130
129
|
```
|
|
131
130
|
|
|
132
|
-
Export the `createPlugin` factory as the package entry. The consumer installs both packages and
|
|
131
|
+
Export the `createPlugin` factory as the package entry. The consumer installs both packages and lists it in `plugins.before` or `plugins.after`.
|
|
133
132
|
|
|
134
133
|
Use MIT or Apache-2.0 if you want the plugin listed from this repo later. Open an issue with the package name and a short description. A maintainer adds the link. The plugin stays in your repository.
|
|
135
134
|
|
|
136
135
|
### Database
|
|
137
136
|
|
|
138
|
-
Optional. Pass `database: { schema }` on the plugin config: the module of its Drizzle tables, with its `relations` when it has them.
|
|
137
|
+
Optional. Pass `database: { schema }` on the plugin config: the module of its Drizzle tables, with its `relations` when it has them. Keep its tables in the Postgres schema `nano_<plugin name>`, reserved for it, which drizzle-kit creates when the module exports its `pgSchema`; a table in the `nano_*` schema of another fails the startup.
|
|
139
138
|
|
|
140
|
-
|
|
139
|
+
`push: true` applies them at startup, and `migrations` the folder the plugin ships; without them whoever installs it applies them with drizzle-kit. Handlers and callbacks receive its client as `params.db`.
|
|
141
140
|
|
|
142
141
|
### What belongs in this repository
|
|
143
142
|
|
package/build/crud/index.js
CHANGED
|
@@ -22,7 +22,7 @@ function list(aRecords) {
|
|
|
22
22
|
}
|
|
23
23
|
/** A list of the table, or one record by its id, with Drizzle's relational query of the body */
|
|
24
24
|
async function read(_oReq, { db, onBeforeResult }) {
|
|
25
|
-
const { model, page, answer, body } = onBeforeResult
|
|
25
|
+
const { model, page, answer, body } = onBeforeResult;
|
|
26
26
|
return answer(await db.query[model].findMany({
|
|
27
27
|
limit: DEFAULT_LIMIT,
|
|
28
28
|
...body,
|
|
@@ -63,15 +63,15 @@ var index_route_default = createRoute({
|
|
|
63
63
|
GET: read,
|
|
64
64
|
QUERY: read,
|
|
65
65
|
POST: async (_oReq, { db, onBeforeResult }) => {
|
|
66
|
-
const { table, body } = onBeforeResult
|
|
66
|
+
const { table, body } = onBeforeResult;
|
|
67
67
|
return first(await db.insert(table).values(body).returning());
|
|
68
68
|
},
|
|
69
69
|
PUT: async (_oReq, { db, onBeforeResult }) => {
|
|
70
|
-
const { table, id, body } = onBeforeResult
|
|
70
|
+
const { table, id, body } = onBeforeResult;
|
|
71
71
|
return first(await db.update(table).set(body).where(eq(table.id, id)).returning());
|
|
72
72
|
},
|
|
73
73
|
DELETE: async (_oReq, { db, onBeforeResult }) => {
|
|
74
|
-
const { table, id } = onBeforeResult
|
|
74
|
+
const { table, id } = onBeforeResult;
|
|
75
75
|
return first(await db.delete(table).where(eq(table.id, id)).returning());
|
|
76
76
|
}
|
|
77
77
|
});
|
package/build/health/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { createRoute } from "@nano-app/backend";
|
|
2
2
|
// health/routes/index.route.ts
|
|
3
3
|
/** Answers that the server is up */
|
|
4
|
-
var index_route_default = createRoute({
|
|
4
|
+
var index_route_default = createRoute({
|
|
5
|
+
public: true,
|
|
6
|
+
GET: async () => ({ result: { status: "ok" } })
|
|
7
|
+
});
|
|
5
8
|
export { index_route_default as default };
|
|
@@ -9,7 +9,6 @@ var rate_limit_default = createPlugin((options) => {
|
|
|
9
9
|
const { capacity = 100, refillPerSecond = 1, message = "Too many requests, please try again later." } = options ?? {};
|
|
10
10
|
return {
|
|
11
11
|
name: "rate-limit",
|
|
12
|
-
stage: "before",
|
|
13
12
|
routesDir: "",
|
|
14
13
|
callbacks: { onMount: async (oApp) => {
|
|
15
14
|
const fnRefill = (oBucket, iNow) => Math.min(capacity, oBucket.iTokens + (iNow - oBucket.iTime) / 1e3 * refillPerSecond);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nano-app/plugins",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Official plugins for @nano-app/backend",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
|
-
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
|
|
37
|
+
"node": "^22.23.4 || ^24.22.1 || >=26.12.1",
|
|
38
|
+
"bun": ">=1.4.4"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
41
|
"@nano-app/backend": ">=0.15.0",
|