@nano-app/backend 0.10.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.
Files changed (2) hide show
  1. package/README.md +32 -32
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -4,11 +4,11 @@
4
4
 
5
5
  Preconfigured Express server with **file-convention routes** and **plugins**: it mounts the base middlewares, discovers your routes, formats every error as JSON, and groups extra endpoints in plugins.
6
6
 
7
- - Node ≥ 20, ESM.
7
+ - Node ≥ 22, ESM.
8
8
  - Optional PostgreSQL layer through your generated Prisma client. Disabled by default: without it no database code is loaded.
9
9
 
10
10
  ```bash
11
- npm install @nano-app/backend
11
+ pnpm add @nano-app/backend
12
12
  ```
13
13
 
14
14
  ## Quick start
@@ -24,17 +24,17 @@ src/
24
24
 
25
25
  ```ts
26
26
  // src/app.ts
27
- import { createNanoApp } from "@nano-app/backend";
27
+ import { createNanoApp } from '@nano-app/backend';
28
28
 
29
29
  createNanoApp({ port: 3000 });
30
30
  ```
31
31
 
32
32
  ```ts
33
33
  // src/routes/health.route.ts
34
- import { createRoute } from "@nano-app/backend";
34
+ import { createRoute } from '@nano-app/backend';
35
35
 
36
36
  export default createRoute({
37
- GET: async () => ({ success: true, result: { status: "ok" } }),
37
+ GET: async () => ({ success: true, result: { status: 'ok' } }),
38
38
  });
39
39
  ```
40
40
 
@@ -52,9 +52,9 @@ Each `*.route.ts` under `mainPlugin.routesDir` default-exports an object with th
52
52
 
53
53
  ```ts
54
54
  export default createRoute({
55
- expression: "/:id", // → /users/:id
55
+ expression: '/:id', // → /users/:id
56
56
  GET: async req => ({ success: true, result: { id: req.params.id } }),
57
- DELETE: async req => ({ success: true, message: "Deleted" }),
57
+ DELETE: async req => ({ success: true, message: 'Deleted' }),
58
58
  });
59
59
  ```
60
60
 
@@ -89,9 +89,9 @@ Every option is optional.
89
89
 
90
90
  ```ts
91
91
  createNanoApp({
92
- settings: { "trust proxy": 1 },
92
+ settings: { 'trust proxy': 1 },
93
93
  packages: {
94
- cors: { origin: "https://my-app.com" },
94
+ cors: { origin: 'https://my-app.com' },
95
95
  helmet: false,
96
96
  rateLimit: { windowMs: 60_000, max: 120 },
97
97
  },
@@ -103,7 +103,7 @@ createNanoApp({
103
103
  Off until enabled: at startup nano-app generates the Prisma client of your schema, checks that its tables can be read and passes it to your handlers as their second parameter. **It never modifies your database**: applying the schema (`prisma db push`, `prisma migrate dev`, `prisma migrate deploy`) is up to you, with Prisma.
104
104
 
105
105
  ```bash
106
- npm install prisma @prisma/client @prisma/adapter-pg pg # prisma too: the startup generates the client
106
+ pnpm add prisma @prisma/client @prisma/adapter-pg pg # prisma too: the startup generates the client
107
107
  ```
108
108
 
109
109
  ```prisma
@@ -130,8 +130,8 @@ createNanoApp({ mainPlugin: { database: { enabled: true } } });
130
130
 
131
131
  ```ts
132
132
  // src/routes/users.route.ts
133
- import { createRoute } from "@nano-app/backend";
134
- import type { PrismaClient } from "../../.nano-app/prisma-clients/app/generated/client";
133
+ import { createRoute } from '@nano-app/backend';
134
+ import type { PrismaClient } from '../../.nano-app/prisma-clients/app/generated/client';
135
135
 
136
136
  export default createRoute<PrismaClient>({
137
137
  GET: async (_req, { db }) => ({ success: true, result: await db!.user.findMany() }),
@@ -157,12 +157,12 @@ export default createRoute<PrismaClient>({
157
157
  Users, sign-in and sessions with [Better Auth](https://www.better-auth.com), on your database. Off until `packages.betterAuth` gets its options (everything but `database`: nano-app passes your Prisma client).
158
158
 
159
159
  ```bash
160
- npm install better-auth
160
+ pnpm add better-auth
161
161
  ```
162
162
 
163
163
  ```ts
164
164
  // src/auth.ts: the options, and an instance only for the Better Auth CLI
165
- import { betterAuth, type BetterAuthOptions } from "better-auth";
165
+ import { betterAuth, type BetterAuthOptions } from 'better-auth';
166
166
 
167
167
  export const authOptions = { emailAndPassword: { enabled: true } } satisfies BetterAuthOptions;
168
168
  export const auth = betterAuth(authOptions);
@@ -170,8 +170,8 @@ export const auth = betterAuth(authOptions);
170
170
 
171
171
  ```ts
172
172
  // src/app.ts
173
- import { createNanoApp } from "@nano-app/backend";
174
- import { authOptions } from "./auth";
173
+ import { createNanoApp } from '@nano-app/backend';
174
+ import { authOptions } from './auth';
175
175
 
176
176
  createNanoApp({
177
177
  packages: { betterAuth: authOptions },
@@ -203,20 +203,20 @@ A plugin is a group of endpoints mounted under a base path. Your routes are moun
203
203
 
204
204
  ```ts
205
205
  // src/plugins/hello/index.ts
206
- import { createPlugin } from "@nano-app/backend";
207
- import { dirname, resolve } from "node:path";
208
- import { fileURLToPath } from "node:url";
206
+ import { createPlugin } from '@nano-app/backend';
207
+ import { dirname, resolve } from 'node:path';
208
+ import { fileURLToPath } from 'node:url';
209
209
 
210
210
  export default createPlugin((options?: { basePath?: string }) => ({
211
- name: "hello",
212
- basePath: options?.basePath ?? "/hello",
213
- routesDir: resolve(dirname(fileURLToPath(import.meta.url)), "routes"),
211
+ name: 'hello',
212
+ basePath: options?.basePath ?? '/hello',
213
+ routesDir: resolve(dirname(fileURLToPath(import.meta.url)), 'routes'),
214
214
  }));
215
215
  ```
216
216
 
217
217
  ```ts
218
218
  // src/plugins/hello/routes/index.route.ts → /hello
219
- import { createRoute } from "@nano-app/backend";
219
+ import { createRoute } from '@nano-app/backend';
220
220
 
221
221
  export default createRoute({
222
222
  GET: async () => ({ success: true, result: { ok: true } }),
@@ -225,8 +225,8 @@ export default createRoute({
225
225
 
226
226
  ```ts
227
227
  // src/app.ts
228
- import { createNanoApp } from "@nano-app/backend";
229
- import hello from "./plugins/hello";
228
+ import { createNanoApp } from '@nano-app/backend';
229
+ import hello from './plugins/hello';
230
230
 
231
231
  createNanoApp({ plugins: [hello()] });
232
232
  ```
@@ -245,12 +245,12 @@ Two levels. Only the plugin's receives the Express app, so nothing app-wide depe
245
245
 
246
246
  ```ts
247
247
  export default createPlugin((options?: { publicPaths?: string[] }) => ({
248
- name: "guard",
249
- routesDir: resolve(dirname(fileURLToPath(import.meta.url)), "routes"),
248
+ name: 'guard',
249
+ routesDir: resolve(dirname(fileURLToPath(import.meta.url)), 'routes'),
250
250
  options: { publicPaths: options?.publicPaths ?? [] },
251
251
  callbacks: {
252
252
  onMount: async (app, { publicPaths }) => {
253
- app.use((req, _res, next) => next(publicPaths.includes(req.path) ? undefined : { status: 401, message: "Unauthorized" }));
253
+ app.use((req, _res, next) => next(publicPaths.includes(req.path) ? undefined : { status: 401, message: 'Unauthorized' }));
254
254
  },
255
255
  },
256
256
  }));
@@ -267,9 +267,9 @@ A plugin with a database follows the same rules, with its client in `.nano-app/p
267
267
  The framework mounts the 404 and the error handler: an unmatched request becomes a `404`, and anything a handler throws becomes an `ApiResponse`.
268
268
 
269
269
  ```ts
270
- throw { status: 409, message: "Already exists" }; // → 409 { success: false, warning: false, message: "Already exists" }
271
- throw "Field name is required"; // → 400 { success: false, message: "Field name is required" }
272
- throw new Error("boom"); // → 500, logged to stderr
270
+ throw { status: 409, message: 'Already exists' }; // → 409 { success: false, warning: false, message: "Already exists" }
271
+ throw 'Field name is required'; // → 400 { success: false, message: "Field name is required" }
272
+ throw new Error('boom'); // → 500, logged to stderr
273
273
  GET /unknown // → 404 { success: false, warning: false, message: "/unknown not found" }
274
274
  ```
275
275
 
@@ -282,7 +282,7 @@ GET /unknown // → 404 { success: false,
282
282
  | `createPlugin(creator)` | Types a plugin factory and its options. |
283
283
  | `createPluginCallbacks(callbacks)` | Types the callbacks of a plugin (`createPluginCallbacks<PrismaClient>` types `db`), e.g. to keep them in their own file. |
284
284
 
285
- Also exported: the configuration types (`NanoAppConfig`, `PackagesConfig`, `PluginConfig`, `PluginStage`, `MainPluginConfig`, `DatabaseConfig`), the Express `Request` type and the HTTP contract (`ApiResponse`, `ApiPromise`, `QueryParams`, `RouteEntry`, `Handler`, `Handlers`, `NanoObject`) and the callback types (`Params`, `Callbacks`, `PluginCallbacks`).
285
+ Also exported: the `NanoApp` class that `createNanoApp` returns, the configuration types (`NanoAppConfig`, `PackagesConfig`, `PluginConfig`, `PluginStage`, `MainPluginConfig`, `DatabaseConfig`), the Express `Request` type and the HTTP contract (`ApiResponse`, `ApiPromise`, `QueryParams`, `RouteEntry`, `Handler`, `Handlers`, `NanoObject`) and the callback types (`Params`, `Callbacks`, `PluginCallbacks`).
286
286
 
287
287
  ## Development
288
288
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nano-app/backend",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Express server with file-convention routes, plugins and an optional PostgreSQL layer",
5
5
  "type": "module",
6
6
  "main": "build/index.js",