@nano-app/backend 0.12.1 → 0.12.3
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 +9 -8
- package/build/index.d.ts +1 -16
- package/build/index.js +21 -31
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ createNanoApp({ port: 3000 });
|
|
|
34
34
|
import { createRoute } from '@nano-app/backend';
|
|
35
35
|
|
|
36
36
|
export default createRoute({
|
|
37
|
-
GET: async () => ({
|
|
37
|
+
GET: async () => ({ result: { status: 'ok' } }),
|
|
38
38
|
});
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -53,16 +53,17 @@ Each `*.route.ts` under `mainPlugin.routesDir` default-exports an object with th
|
|
|
53
53
|
```ts
|
|
54
54
|
export default createRoute({
|
|
55
55
|
expression: '/:id', // → /users/:id
|
|
56
|
-
GET: async req => ({
|
|
57
|
-
DELETE: async req => ({
|
|
56
|
+
GET: async req => ({ result: { id: req.params.id } }),
|
|
57
|
+
DELETE: async req => ({ message: 'Deleted' }),
|
|
58
58
|
});
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
Each handler receives `(req, params)` and returns an `ApiResponse`. `params` holds the plugin's `options`, `db` (the Prisma client when a database is enabled, see [Database](#database)) and `onBeforeResult` (see [Callbacks](#callbacks)):
|
|
61
|
+
Each handler receives `(req, params)` and returns an `ApiResponse` without `success`: its `status` is the HTTP status (`200` by default) and the framework sets `success` to `status < 400`. `params` holds the plugin's `options`, `db` (the Prisma client when a database is enabled, see [Database](#database)) and `onBeforeResult` (see [Callbacks](#callbacks)):
|
|
62
62
|
|
|
63
63
|
```ts
|
|
64
64
|
type ApiResponse<T> = {
|
|
65
|
-
|
|
65
|
+
status?: number; // HTTP status, 200 by default; not sent in the body
|
|
66
|
+
success?: boolean; // set by the framework: status < 400
|
|
66
67
|
result?: T;
|
|
67
68
|
message?: string;
|
|
68
69
|
warning?: boolean;
|
|
@@ -137,7 +138,7 @@ import { createRoute } from '@nano-app/backend';
|
|
|
137
138
|
import type { PrismaClient } from '../../.nano-app/prisma-clients/app/generated/client';
|
|
138
139
|
|
|
139
140
|
export default createRoute<PrismaClient>({
|
|
140
|
-
GET: async (_req, { db }) => ({
|
|
141
|
+
GET: async (_req, { db }) => ({ result: await db!.user.findMany() }),
|
|
141
142
|
});
|
|
142
143
|
```
|
|
143
144
|
|
|
@@ -222,7 +223,7 @@ export default createPlugin((options?: { basePath?: string }) => ({
|
|
|
222
223
|
import { createRoute } from '@nano-app/backend';
|
|
223
224
|
|
|
224
225
|
export default createRoute({
|
|
225
|
-
GET: async () => ({
|
|
226
|
+
GET: async () => ({ result: { ok: true } }),
|
|
226
227
|
});
|
|
227
228
|
```
|
|
228
229
|
|
|
@@ -263,7 +264,7 @@ The endpoints of one plugin have no guaranteed order: one route's `onInit` must
|
|
|
263
264
|
|
|
264
265
|
Everything is mounted on one Express application, in this order: base middlewares → `before` plugins → your routes → `after` plugins → 404 → error handler.
|
|
265
266
|
|
|
266
|
-
A plugin with a database follows the same rules, with its client in `.nano-app/prisma-clients/<name>`: a plugin of yours points `database.schemaPath` at its schema and you apply it with Prisma. Two plugins with the same name and a database fail at startup. If that folder holds the client of another schema from an earlier start, the startup warns and replaces it. An official plugin of `@nano-app/plugins` needs nothing from you: at startup nano-app applies its migrations to its own `nano_*` schema. See [
|
|
267
|
+
A plugin with a database follows the same rules, with its client in `.nano-app/prisma-clients/<name>`: a plugin of yours points `database.schemaPath` at its schema and you apply it with Prisma. Two plugins with the same name and a database fail at startup. If that folder holds the client of another schema from an earlier start, the startup warns and replaces it. An official plugin of `@nano-app/plugins` needs nothing from you: at startup nano-app applies its migrations to its own `nano_*` schema. See [`@nano-app/plugins`](https://github.com/Nano-Nathan/nano-app/blob/main/plugins/README.md#database).
|
|
267
268
|
|
|
268
269
|
## Errors
|
|
269
270
|
|
package/build/index.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ import { Options } from "express-rate-limit";
|
|
|
4
4
|
import "dotenv/config";
|
|
5
5
|
import "jiti";
|
|
6
6
|
import { BetterAuthOptions } from "better-auth";
|
|
7
|
-
//#region ../core/types/api.d.ts
|
|
8
7
|
/** API response */
|
|
9
8
|
type ApiResponse<T = any> = {
|
|
9
|
+
status?: number;
|
|
10
10
|
success?: boolean;
|
|
11
11
|
result?: T;
|
|
12
12
|
message?: string;
|
|
@@ -22,8 +22,6 @@ type QueryParams = {
|
|
|
22
22
|
orderby?: NanoObject<'asc' | 'desc'>;
|
|
23
23
|
select?: string;
|
|
24
24
|
};
|
|
25
|
-
//#endregion
|
|
26
|
-
//#region ../core/types/objects.d.ts
|
|
27
25
|
/** Extra params of handlers and callbacks */
|
|
28
26
|
type Params<DB = any> = Partial<NanoObject & {
|
|
29
27
|
db?: DB;
|
|
@@ -67,8 +65,6 @@ type RouteEntry<DB = any> = {
|
|
|
67
65
|
} & Handlers<DB>;
|
|
68
66
|
/** Generic object */
|
|
69
67
|
type NanoObject<T = any> = Record<string, T>;
|
|
70
|
-
//#endregion
|
|
71
|
-
//#region src/types/database.d.ts
|
|
72
68
|
/** Database client configuration. */
|
|
73
69
|
type DatabaseConfig = {
|
|
74
70
|
/** Whether the database is enabled. Defaults to false */
|
|
@@ -76,8 +72,6 @@ type DatabaseConfig = {
|
|
|
76
72
|
/** Prisma schema file (`.prisma`), relative to `cwd` */
|
|
77
73
|
schemaPath: string;
|
|
78
74
|
};
|
|
79
|
-
//#endregion
|
|
80
|
-
//#region src/types/plugin.d.ts
|
|
81
75
|
/** When a plugin is mounted, relative to the consumer routes */
|
|
82
76
|
type PluginStage = 'before' | 'after';
|
|
83
77
|
/** Plugin configuration: a group of endpoints */
|
|
@@ -115,8 +109,6 @@ type MainPluginConfig<Options = NanoObject> = {
|
|
|
115
109
|
/** Plugin callbacks */
|
|
116
110
|
callbacks?: PluginCallbacks;
|
|
117
111
|
};
|
|
118
|
-
//#endregion
|
|
119
|
-
//#region src/types/app.d.ts
|
|
120
112
|
/** Configuration of the framework's built-in packages */
|
|
121
113
|
type PackagesConfig = {
|
|
122
114
|
/** Morgan configuration */
|
|
@@ -156,14 +148,10 @@ type NanoAppConfig = {
|
|
|
156
148
|
/** Main plugin to mount. */
|
|
157
149
|
mainPlugin?: MainPluginConfig;
|
|
158
150
|
};
|
|
159
|
-
//#endregion
|
|
160
|
-
//#region src/app/config.d.ts
|
|
161
151
|
/** Merges the consumer configuration over the initial one; the main plugin is always named "app" */
|
|
162
152
|
declare function mergeConfig(oConfig?: NanoAppConfig): NanoAppConfig & Required<Pick<NanoAppConfig, 'settings' | 'plugins' | 'packages'>> & {
|
|
163
153
|
mainPlugin: PluginConfig;
|
|
164
154
|
};
|
|
165
|
-
//#endregion
|
|
166
|
-
//#region src/app/NanoApp.d.ts
|
|
167
155
|
/** Main NanoApp class */
|
|
168
156
|
declare class NanoApp {
|
|
169
157
|
protected oApp: Express;
|
|
@@ -176,8 +164,6 @@ declare class NanoApp {
|
|
|
176
164
|
/** Adds the packages according to the configuration */
|
|
177
165
|
private setPackages;
|
|
178
166
|
}
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region src/app/creators.d.ts
|
|
181
167
|
/** Creates a new NanoApp instance */
|
|
182
168
|
export declare function createNanoApp(oConfig?: NanoAppConfig): NanoApp;
|
|
183
169
|
/** Creates and types a route object. */
|
|
@@ -187,5 +173,4 @@ type Creator<Options> = (options?: Options) => PluginConfig<Options>;
|
|
|
187
173
|
export declare function createPlugin<Options>(fnCreator: Creator<Options>): Creator<Options>;
|
|
188
174
|
/** Creates and types the callbacks of a plugin. */
|
|
189
175
|
export declare function createPluginCallbacks<DB>(oCallbacks: PluginCallbacks<DB>): PluginCallbacks<DB>;
|
|
190
|
-
//#endregion
|
|
191
176
|
export { type ApiPromise, type ApiResponse, type Callbacks, type DatabaseConfig, type Handler, type Handlers, type MainPluginConfig, NanoApp, type NanoAppConfig, type NanoObject, type PackagesConfig, type Params, type PluginCallbacks, type PluginConfig, type PluginStage, type QueryParams, type Request, type RouteEntry };
|
package/build/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
12
12
|
import { createJiti } from "jiti";
|
|
13
13
|
import { execFileSync } from "node:child_process";
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
|
-
|
|
15
|
+
// ../core/const.ts
|
|
16
16
|
/** HTTP methods a route can handle */
|
|
17
17
|
const HTTP_METHODS = [
|
|
18
18
|
"GET",
|
|
@@ -25,8 +25,7 @@ const HTTP_METHODS = [
|
|
|
25
25
|
const OFFICIAL_PLUGINS = "@nano-app/plugins";
|
|
26
26
|
/** Error when the database URL is missing at startup */
|
|
27
27
|
const MISSING_DATABASE_URL = "[nano-app] [ERROR] The DATABASE_URL environment variable is not defined.";
|
|
28
|
-
|
|
29
|
-
//#region ../core/loader/Validator.ts
|
|
28
|
+
// ../core/loader/Validator.ts
|
|
30
29
|
const STORE_TYPES = ["app", "api"];
|
|
31
30
|
/** Checks the consumer's files: invalid ones are warned about and discarded */
|
|
32
31
|
var Validator = class Validator {
|
|
@@ -83,8 +82,7 @@ var Validator = class Validator {
|
|
|
83
82
|
return this.oValidators[sExtension]?.()(oEntry, sFilePath) ? oEntry : void 0;
|
|
84
83
|
}
|
|
85
84
|
};
|
|
86
|
-
|
|
87
|
-
//#region ../core/loader/FileReader.ts
|
|
85
|
+
// ../core/loader/FileReader.ts
|
|
88
86
|
/** Reads and loads files from disk, resolved from `cwd` */
|
|
89
87
|
var FileReader = class {
|
|
90
88
|
/** Loader of the consumer's modules; the Prisma runtime is loaded natively */
|
|
@@ -110,8 +108,7 @@ var FileReader = class {
|
|
|
110
108
|
return sAbsolutePath;
|
|
111
109
|
}
|
|
112
110
|
};
|
|
113
|
-
|
|
114
|
-
//#region ../core/utils/Formatter.ts
|
|
111
|
+
// ../core/utils/Formatter.ts
|
|
115
112
|
/** Formats values for logs and messages */
|
|
116
113
|
var Formatter = class {
|
|
117
114
|
/** Message of anything thrown: its `message` when it has one, else the value itself */
|
|
@@ -124,8 +121,7 @@ var Formatter = class {
|
|
|
124
121
|
return oDate.toISOString().slice(0, 19).replace("T", " ");
|
|
125
122
|
}
|
|
126
123
|
};
|
|
127
|
-
|
|
128
|
-
//#region ../core/loader/FileLoader.ts
|
|
124
|
+
// ../core/loader/FileLoader.ts
|
|
129
125
|
/** Finds, loads and validates the consumer's files */
|
|
130
126
|
var FileLoader = class {
|
|
131
127
|
/**
|
|
@@ -157,8 +153,7 @@ var FileLoader = class {
|
|
|
157
153
|
return Validator.validate(oEntries, extension);
|
|
158
154
|
}
|
|
159
155
|
};
|
|
160
|
-
|
|
161
|
-
//#region src/routes/Endpoint.ts
|
|
156
|
+
// src/routes/Endpoint.ts
|
|
162
157
|
/** Base class for all endpoints */
|
|
163
158
|
var Endpoint = class {
|
|
164
159
|
oMethods;
|
|
@@ -191,13 +186,17 @@ var Endpoint = class {
|
|
|
191
186
|
...oRequestParams,
|
|
192
187
|
onBeforeResult
|
|
193
188
|
})).then((oResponse) => {
|
|
194
|
-
|
|
189
|
+
const { status, ...oBody } = oResponse;
|
|
190
|
+
const iStatus = status ?? 200;
|
|
191
|
+
oRes.status(iStatus).json({
|
|
192
|
+
...oBody,
|
|
193
|
+
success: iStatus < 400
|
|
194
|
+
});
|
|
195
195
|
}).catch(fnNext);
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
};
|
|
199
|
-
|
|
200
|
-
//#region src/routes/Router.ts
|
|
199
|
+
// src/routes/Router.ts
|
|
201
200
|
/** Turns a folder of `*.route.ts` files into Express paths and endpoints */
|
|
202
201
|
var Router = class {
|
|
203
202
|
oRoutes = {};
|
|
@@ -227,8 +226,7 @@ var Router = class {
|
|
|
227
226
|
return `${this.sBasePath}${sEndpoint.replace(/\/index$/, "")}${sExpression}` || "/";
|
|
228
227
|
}
|
|
229
228
|
};
|
|
230
|
-
|
|
231
|
-
//#region src/auth/BetterAuth.ts
|
|
229
|
+
// src/auth/BetterAuth.ts
|
|
232
230
|
/** Better Auth on the app's Prisma client: the instance, its handler and a session guard */
|
|
233
231
|
var BetterAuth = class {
|
|
234
232
|
/** Creates the instance on the app's client, its handler and the guard */
|
|
@@ -260,8 +258,7 @@ var BetterAuth = class {
|
|
|
260
258
|
};
|
|
261
259
|
}
|
|
262
260
|
};
|
|
263
|
-
|
|
264
|
-
//#region src/database/Prisma.ts
|
|
261
|
+
// src/database/Prisma.ts
|
|
265
262
|
/** Runs the consumer's Prisma CLI: generates clients and applies official plugins' migrations */
|
|
266
263
|
var Prisma = class {
|
|
267
264
|
/** Folder of a client and its files, in the project */
|
|
@@ -336,8 +333,7 @@ var Prisma = class {
|
|
|
336
333
|
}
|
|
337
334
|
}
|
|
338
335
|
};
|
|
339
|
-
|
|
340
|
-
//#region src/database/Database.ts
|
|
336
|
+
// src/database/Database.ts
|
|
341
337
|
/** Prisma clients of the app and its plugins, one per name */
|
|
342
338
|
var Database = class {
|
|
343
339
|
static oAdapter;
|
|
@@ -396,8 +392,7 @@ var Database = class {
|
|
|
396
392
|
return JSON.parse(readFileSync(join(sDir, "package.json"), "utf-8")).name === OFFICIAL_PLUGINS;
|
|
397
393
|
}
|
|
398
394
|
};
|
|
399
|
-
|
|
400
|
-
//#region src/routes/Plugin.ts
|
|
395
|
+
// src/routes/Plugin.ts
|
|
401
396
|
/** A group of routes, with its options, callbacks and database */
|
|
402
397
|
var Plugin = class {
|
|
403
398
|
oConfig;
|
|
@@ -426,8 +421,7 @@ var Plugin = class {
|
|
|
426
421
|
for (const { path, endpoint } of await oRouter.mount()) oApp.all(path, await endpoint.mount(oParams));
|
|
427
422
|
}
|
|
428
423
|
};
|
|
429
|
-
|
|
430
|
-
//#region src/app/config.ts
|
|
424
|
+
// src/app/config.ts
|
|
431
425
|
/** Initial application configuration */
|
|
432
426
|
const INITIAL_CONFIG = {
|
|
433
427
|
/** Application port */
|
|
@@ -505,8 +499,7 @@ function mergeConfig(oConfig = {}) {
|
|
|
505
499
|
}
|
|
506
500
|
};
|
|
507
501
|
}
|
|
508
|
-
|
|
509
|
-
//#region src/app/errors.ts
|
|
502
|
+
// src/app/errors.ts
|
|
510
503
|
/** Whether anything thrown is an object, read as a HandlerError */
|
|
511
504
|
function isHandlerError(oError) {
|
|
512
505
|
return typeof oError === "object" && oError !== null;
|
|
@@ -543,8 +536,7 @@ const errorHandler = (oError, _oReq, oRes, _fnNext) => {
|
|
|
543
536
|
if (status >= 500) console.error("[nano-app] [ERROR]", oError);
|
|
544
537
|
oRes.status(status).json(body);
|
|
545
538
|
};
|
|
546
|
-
|
|
547
|
-
//#region src/app/NanoApp.ts
|
|
539
|
+
// src/app/NanoApp.ts
|
|
548
540
|
/** Main NanoApp class */
|
|
549
541
|
var NanoApp = class {
|
|
550
542
|
oApp;
|
|
@@ -598,8 +590,7 @@ var NanoApp = class {
|
|
|
598
590
|
if (oConfig.urlencoded) this.oApp.use(express.urlencoded(oConfig.urlencoded));
|
|
599
591
|
}
|
|
600
592
|
};
|
|
601
|
-
|
|
602
|
-
//#region src/app/creators.ts
|
|
593
|
+
// src/app/creators.ts
|
|
603
594
|
/** Creates a new NanoApp instance */
|
|
604
595
|
function createNanoApp(oConfig) {
|
|
605
596
|
return new NanoApp(oConfig);
|
|
@@ -615,5 +606,4 @@ function createPlugin(fnCreator) {
|
|
|
615
606
|
function createPluginCallbacks(oCallbacks) {
|
|
616
607
|
return oCallbacks;
|
|
617
608
|
}
|
|
618
|
-
//#endregion
|
|
619
609
|
export { NanoApp, createNanoApp, createPlugin, createPluginCallbacks, createRoute };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nano-app/backend",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3",
|
|
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",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"compression": "^1.8.2",
|
|
47
47
|
"cors": "^2.8.6",
|
|
48
|
-
"dotenv": "^18.0.
|
|
48
|
+
"dotenv": "^18.0.9",
|
|
49
49
|
"express": "^5.3.0",
|
|
50
|
-
"express-rate-limit": "^8.7.
|
|
50
|
+
"express-rate-limit": "^8.7.2",
|
|
51
51
|
"glob": "^13.0.6",
|
|
52
52
|
"helmet": "^8.3.0",
|
|
53
53
|
"jiti": "^2.7.0",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@prisma/adapter-pg": "^7.10.1",
|
|
58
58
|
"@prisma/client": "^7.10.1",
|
|
59
|
-
"better-auth": "^1.7.
|
|
59
|
+
"better-auth": "^1.7.15",
|
|
60
60
|
"pg": "^8.23.1"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"@types/cors": "^2.8.19",
|
|
81
81
|
"@types/express": "^5.0.6",
|
|
82
82
|
"@types/morgan": "^1.9.10",
|
|
83
|
-
"@types/node": "^26.6.
|
|
83
|
+
"@types/node": "^26.6.6",
|
|
84
84
|
"@types/pg": "^8.20.0",
|
|
85
|
-
"better-auth": "^1.7.
|
|
85
|
+
"better-auth": "^1.7.15",
|
|
86
86
|
"pg": "^8.23.1",
|
|
87
87
|
"prisma": "^7.10.1",
|
|
88
|
-
"tsdown": "^0.23.
|
|
88
|
+
"tsdown": "^0.23.4",
|
|
89
89
|
"tsx": "^4.23.15",
|
|
90
90
|
"typescript": "^7.0.2"
|
|
91
91
|
}
|