@devmoods/express-extras 0.56.0 → 0.57.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 +79 -11
- package/dist/auth/token.js +1 -0
- package/dist/router.d.ts +4 -4
- package/dist/router.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,33 +1,101 @@
|
|
|
1
1
|
# @devmoods/express-extras
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Express evolved: Type-safe routing, elegant transactions, and production-ready patterns for modern Node.js
|
|
4
4
|
|
|
5
5
|
_Extremely opinionated_ set of utilities for cranking out production-ready Node.js apps in a timely manner. We assume you are using PostgreSQL as the main database, Redis for data you don't want in Postgres and [Faktory](https://contribsys.com/faktory/) for background jobs.
|
|
6
6
|
|
|
7
7
|
- Only works with [ECMAScript Modules (ESM)](https://nodejs.org/api/esm.html)
|
|
8
|
-
- Thin layer on top of [Express](https://expressjs.com) with better support for `async`/`await`
|
|
8
|
+
- Thin layer on top of [Express](https://expressjs.com) with better support for `async`/`await` and type safety.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
const router = createRouter();
|
|
12
|
+
|
|
13
|
+
router.put(
|
|
14
|
+
'/posts/:id',
|
|
15
|
+
{
|
|
16
|
+
request: type({ title: 'string' }),
|
|
17
|
+
response: type({ id: 'number', title: 'string' }),
|
|
18
|
+
},
|
|
19
|
+
async (req) => {
|
|
20
|
+
await delay(1000);
|
|
21
|
+
return {
|
|
22
|
+
id: req.params.id,
|
|
23
|
+
...req.body,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- Clean graceful shutdown procedures and built-in health checks. Runs perfectly in Kubernetes.
|
|
9
30
|
- PostgreSQL with transactions and migrations
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bin/dx manage migrate up
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
await postgres.transaction(() => {
|
|
38
|
+
const post = await postgres.get<{ title: string }>(
|
|
39
|
+
sql`SELECT title FROM posts WHERE id = ${id} FOR UPDATE`,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
await postgres.query(
|
|
43
|
+
sql`UPDATE posts SET ${sql.spreadUpdate({ title: post.title.split('').reverse().join('') })} WHERE id = ${id}`,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Nested transactions with savepoints
|
|
48
|
+
await postgres.transaction(() => {
|
|
49
|
+
})
|
|
50
|
+
} catch () {
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error('please rollback');
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- CSRF protection
|
|
58
|
+
- Built in auth backends! Email+password, passwordless, easy to extend with more
|
|
59
|
+
- OpenAPI v3 generator
|
|
10
60
|
- Redis
|
|
11
|
-
- Slack
|
|
61
|
+
- Slack/Discord client for sending operational notifications
|
|
12
62
|
- CLI commands inspired by Django's management commands
|
|
13
|
-
|
|
14
|
-
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
bin/dx manage --help
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
registerManagementCommand((command, cli) => {
|
|
70
|
+
command('hello').action(() => {
|
|
71
|
+
cli.writeLine('Hello World');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- Delayed/async jobs with [Faktory](https://contribsys.com/faktory/)
|
|
77
|
+
- Validation of HTTP requests using [Standard Schema](https://github.com/standard-schema/standard-schema), including [`jsonSchema()`](https://json-schema.org/) and [arktype](https://arktype.io/).
|
|
15
78
|
- Logging helpers to write well-formatted contextual JSON logs to `stdout`
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const logger = getLogger();
|
|
82
|
+
|
|
83
|
+
logger.info('hello world', { foo: 'bar', baz: 10 });
|
|
84
|
+
// {"level":"info","hostname":"machine.local","pid":43149,"name":"default","message":"hello world","foo":"bar","baz":10,"time":"2024-01-02T17:25:28.610Z","v":1}
|
|
85
|
+
```
|
|
86
|
+
|
|
16
87
|
- Request local context using [AsyncLocalStorage](https://nodejs.org/api/async_context.html) (e.g. request ids)
|
|
17
88
|
- [RFC 7807 Problem Details for HTTP APIs](https://tools.ietf.org/html/rfc7807) by default
|
|
18
|
-
- Works best with the [pnpm](https://pnpm.io/) package manager
|
|
19
89
|
- Uses [Sentry](https://sentry.io/) for external error monitoring
|
|
20
|
-
-
|
|
21
|
-
- Graceful shutdown
|
|
22
|
-
- Read configuration from `.env` files
|
|
90
|
+
- [The Twelve-Factor App](https://12factor.net/)
|
|
23
91
|
|
|
24
92
|
## Goals
|
|
25
93
|
|
|
26
|
-
- Time-to-market
|
|
94
|
+
- Time-to-market and top-notch developer experience.
|
|
27
95
|
|
|
28
96
|
## Non-goals
|
|
29
97
|
|
|
30
|
-
-
|
|
98
|
+
- Support for multiple different database vendors and job processing systems.
|
|
31
99
|
|
|
32
100
|
## Example
|
|
33
101
|
|
package/dist/auth/token.js
CHANGED
|
@@ -105,6 +105,7 @@ export function setAccessTokenCookie(res, token) {
|
|
|
105
105
|
}
|
|
106
106
|
else {
|
|
107
107
|
res.cookie(ACCESS_TOKEN_COOKIE_NAME, token, {
|
|
108
|
+
expires: new Date(Date.now() + config.value.AUTH_TOKEN_EXPIRES_IN * 1000),
|
|
108
109
|
httpOnly: true,
|
|
109
110
|
sameSite,
|
|
110
111
|
secure: process.env.NODE_ENV === 'production',
|
package/dist/router.d.ts
CHANGED
|
@@ -19,19 +19,19 @@ export declare class TypedRouter {
|
|
|
19
19
|
get<Path extends string, RequestBodySchema extends StandardSchemaV1<any> | undefined = undefined, ResponseBodySchema extends StandardSchemaV1<any> | undefined = undefined, RequestQuerySchema extends StandardSchemaV1<any> | undefined = undefined>(path: Path, validation: ValidationSpec<RequestBodySchema, ResponseBodySchema, RequestQuerySchema>, ...middleware: [
|
|
20
20
|
...RequestHandler[],
|
|
21
21
|
TypedRequestHandler<ExtractParams<Path>, ResponseBodySchema, RequestBodySchema, RequestQuerySchema>
|
|
22
|
-
]):
|
|
22
|
+
]): this;
|
|
23
23
|
post<Path extends string, RequestBodySchema extends StandardSchemaV1<any> | undefined = undefined, ResponseBodySchema extends StandardSchemaV1<any> | undefined = undefined, RequestQuerySchema extends StandardSchemaV1<any> | undefined = undefined>(path: Path, validation: ValidationSpec<RequestBodySchema, ResponseBodySchema, RequestQuerySchema>, ...middleware: [
|
|
24
24
|
...RequestHandler[],
|
|
25
25
|
TypedRequestHandler<ExtractParams<Path>, ResponseBodySchema, RequestBodySchema, RequestQuerySchema>
|
|
26
|
-
]):
|
|
26
|
+
]): this;
|
|
27
27
|
put<Path extends string, RequestBodySchema extends StandardSchemaV1<any> | undefined = undefined, ResponseBodySchema extends StandardSchemaV1<any> | undefined = undefined, RequestQuerySchema extends StandardSchemaV1<any> | undefined = undefined>(path: Path, validation: ValidationSpec<RequestBodySchema, ResponseBodySchema, RequestQuerySchema>, ...middleware: [
|
|
28
28
|
...RequestHandler[],
|
|
29
29
|
TypedRequestHandler<ExtractParams<Path>, ResponseBodySchema, RequestBodySchema, RequestQuerySchema>
|
|
30
|
-
]):
|
|
30
|
+
]): this;
|
|
31
31
|
delete<Path extends string, RequestBodySchema extends StandardSchemaV1<any> | undefined = undefined, ResponseBodySchema extends StandardSchemaV1<any> | undefined = undefined, RequestQuerySchema extends StandardSchemaV1<any> | undefined = undefined>(path: Path, validation: ValidationSpec<RequestBodySchema, ResponseBodySchema, RequestQuerySchema>, ...middleware: [
|
|
32
32
|
...RequestHandler[],
|
|
33
33
|
TypedRequestHandler<ExtractParams<Path>, ResponseBodySchema, RequestBodySchema, RequestQuerySchema>
|
|
34
|
-
]):
|
|
34
|
+
]): this;
|
|
35
35
|
use(...middleware: RequestHandler[]): this;
|
|
36
36
|
use(...middleware: [string, ...RequestHandler[]]): this;
|
|
37
37
|
getRouter(): import("express-serve-static-core").Router;
|
package/dist/router.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devmoods/express-extras",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"description": "Kubernetes-ready utilities for rapid Node.js application development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
],
|
|
45
45
|
"license": "ISC",
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@devmoods/eslint-config": "^2.
|
|
47
|
+
"@devmoods/eslint-config": "^2.7.0",
|
|
48
48
|
"@types/koa-compose": "^3.2.8",
|
|
49
49
|
"@types/node": "22.13.10",
|
|
50
50
|
"@types/supertest": "^6.0.2",
|