express-generator-typescript 2.7.5 → 2.7.7
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-git +79 -85
- package/README.md +10 -12
- package/lib/project-files/gitignore +2 -1
- package/lib/project-files/package.json +4 -4
- package/lib/project-files/scripts/build.ts +0 -1
- package/lib/project-files/scripts/common/utils.ts +0 -1
- package/lib/project-files/src/common/constants/ENV.ts +0 -2
- package/lib/project-files/src/common/constants/HTTP_STATUS_CODES.ts +0 -1
- package/lib/project-files/src/common/constants/PATHS.ts +7 -3
- package/lib/project-files/src/common/util/route-errors.ts +0 -1
- package/lib/project-files/src/common/util/validators.ts +0 -1
- package/lib/project-files/src/models/User.ts +5 -8
- package/lib/project-files/src/repos/UserRepo.ts +6 -3
- package/lib/project-files/src/repos/database.test.json +1 -1
- package/lib/project-files/src/routes/UserRoutes.ts +0 -3
- package/lib/project-files/src/routes/common/types/index.ts +0 -1
- package/lib/project-files/src/routes/common/util/index.ts +0 -2
- package/lib/project-files/src/routes/index.ts +1 -3
- package/lib/project-files/src/server.ts +1 -1
- package/lib/project-files/src/services/UserService.ts +0 -3
- package/lib/project-files/tests/common/types/index.ts +2 -2
- package/lib/project-files/tests/common/util/index.ts +32 -15
- package/lib/project-files/tests/users.test.ts +7 -15
- package/package.json +18 -20
- package/lib/project-files/tests/common/Paths.ts +0 -5
package/README-git
CHANGED
|
@@ -1,85 +1,79 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<
|
|
6
|
-
<a href="https://www.npmjs.com/package/express-generator-typescript"
|
|
7
|
-
<a href="https://www.npmjs.com/package/express-generator-typescript"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
## Debugging
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Happy web deving :)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
## License
|
|
84
|
-
|
|
85
|
-
[MIT](LICENSE)
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img alt="express-generator-typescript" src="https://github.com/seanpmaxwell/express-generator-typescript/raw/master/express-typescript.png" width="420">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/v/express-generator-typescript.svg?style=for-the-badge&logo=npm" alt="NPM Version"></a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/dm/express-generator-typescript.svg?style=for-the-badge" alt="NPM Downloads"></a>
|
|
8
|
+
<a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/l/express-generator-typescript.svg?style=for-the-badge" alt="License"></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
# express-generator-typescript
|
|
12
|
+
|
|
13
|
+
Production-ready Express application scaffolding with TypeScript baked in. Generate a modern API starter in seconds that follows the [TypeScript best practices](https://github.com/seanpmaxwell/Typescript-Best-Practices/blob/main/README.md) used to build this tool.
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
`express-generator-typescript` creates a new Express application similar to the classic `express-generator` package, but the generated project is fully wired for TypeScript from day one. You get strict typing, linting, hot reloading, production builds, testing utilities, and sane defaults that focus on APIs (no view engine or opinionated ORM). Path aliases are preconfigured through `tsconfig-paths` and `_moduleAliases`, so referencing modules stays clean even as the app grows.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **TypeScript-first** – strict compiler settings, linting, and sensible tsconfig defaults ready to go.
|
|
22
|
+
- **API-centric** – no view engine or extra dependencies; ideal for SPAs, mobile backends, or services.
|
|
23
|
+
- **Productivity tooling** – includes nodemon, ts-node, hot reload scripts, Jest, ESLint, and production builds.
|
|
24
|
+
- **Path aliases** – aliases configured in `tsconfig.json` and `preload.js` so you can import modules cleanly.
|
|
25
|
+
- **Keeps dependencies lean** – no bundled ORM or UI layers; only the essentials for Express + TS development.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx express-generator-typescript
|
|
31
|
+
# or install globally
|
|
32
|
+
npm install -g express-generator-typescript
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# generate a project (defaults to express-gen-ts)
|
|
39
|
+
npx express-generator-typescript my-api
|
|
40
|
+
|
|
41
|
+
cd my-api
|
|
42
|
+
|
|
43
|
+
# start developing at http://localhost:3000
|
|
44
|
+
npm run dev
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Use `--use-yarn` if you prefer Yarn over npm. If you omit the project name, the generator creates `express-gen-ts`.
|
|
48
|
+
|
|
49
|
+
## CLI Options
|
|
50
|
+
|
|
51
|
+
| Option | Description |
|
|
52
|
+
| ----------------- | --------------------------------------------------------------------------- |
|
|
53
|
+
| `project name` | Folder to create. Defaults to `express-gen-ts` if omitted. |
|
|
54
|
+
| `--use-yarn` | Installs dependencies with Yarn instead of npm. |
|
|
55
|
+
| `--version` | Prints the generator version. |
|
|
56
|
+
|
|
57
|
+
> The historical `--with-auth` switch was removed in v2.5+. For an auth-ready example see the [express-jsonwebtoken-demo](https://github.com/seanpmaxwell/express-jsonwebtoken-demo) project.
|
|
58
|
+
|
|
59
|
+
## Sample Project
|
|
60
|
+
|
|
61
|
+
The generated code includes CRUD routes for illustrative `User` resources to demonstrate controllers, services, and routing patterns in Express + TypeScript. Replace these samples with your own modules after scaffolding.
|
|
62
|
+
|
|
63
|
+
## Available Scripts
|
|
64
|
+
|
|
65
|
+
- `npm run dev` / `npm run dev:hot` – Run the server with live reload.
|
|
66
|
+
- `npm run test` / `npm run test:hot` – Execute all Jest tests, optionally with watch mode.
|
|
67
|
+
- `npm run test -- users.test.ts` – Target a single test file.
|
|
68
|
+
- `npm run lint` – Run ESLint checks.
|
|
69
|
+
- `npm run build` – Compile the project for production.
|
|
70
|
+
- `npm start` – Serve the built project.
|
|
71
|
+
- `npm run type-check` – Run the TypeScript compiler without emitting files.
|
|
72
|
+
|
|
73
|
+
## Debugging
|
|
74
|
+
|
|
75
|
+
Development uses `nodemon` so the server restarts when files change. To enable the Node.js inspector, edit `nodemonConfig` in `package.json` (and `spec/nodemon.json` for tests) and change the `exec` value from `ts-node` to `node --inspect -r ts-node/register`.
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
[MIT](LICENSE)
|
package/README.md
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
<
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img alt="express-generator-typescript" src="https://github.com/seanpmaxwell/express-generator-typescript/raw/master/express-typescript.png" width="420">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/v/express-generator-typescript.svg?style=for-the-badge&logo=npm" alt="NPM Version"></a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/dm/express-generator-typescript.svg?style=for-the-badge" alt="NPM Downloads"></a>
|
|
8
|
+
<a href="https://www.npmjs.com/package/express-generator-typescript"><img src="https://img.shields.io/npm/l/express-generator-typescript.svg?style=for-the-badge" alt="License"></a>
|
|
9
|
+
</p>
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
<a href="https://www.npmjs.com/package/express-generator-typescript" target="_blank"><img src="https://img.shields.io/npm/l/express-generator-typescript.svg" alt="Package License" /></a>
|
|
7
|
-
<a href="https://www.npmjs.com/package/express-generator-typescript" target="_blank"><img src="https://img.shields.io/npm/dm/express-generator-typescript.svg" alt="NPM Downloads" /></a>
|
|
11
|
+
# express-generator-typescript
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
## What is it?
|
|
11
|
-
|
|
12
|
-
Creates a new express application similar to the _express-generator_ module. Except this new application is configured to use TypeScript instead of plain JavaScript.
|
|
13
|
-
|
|
14
|
-
This project complies with Typescript best practices listed <a href="https://github.com/seanpmaxwell/Typescript-Best-Practices/blob/main/README.md">here</a>.
|
|
15
|
-
<br/>
|
|
13
|
+
Production-ready Express application scaffolding with TypeScript baked in. Generate a modern API starter in seconds that follows the [TypeScript best practices](https://github.com/seanpmaxwell/Typescript-Best-Practices/blob/main/README.md) used to build this tool.
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
## Documenation
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"lint": "eslint .",
|
|
10
10
|
"start": "NODE_ENV=production node -r ./config.js ./dist",
|
|
11
11
|
"test": "NODE_ENV=test vitest",
|
|
12
|
-
"type-check": "tsc --noEmit"
|
|
12
|
+
"type-check": "tsc -b --noEmit"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=16.0.0"
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"helmet": "^8.1.0",
|
|
23
23
|
"inserturlparams": "^2.0.5",
|
|
24
24
|
"jet-env": "^1.1.4",
|
|
25
|
-
"jet-logger": "^2.
|
|
26
|
-
"jet-paths": "^
|
|
27
|
-
"jet-validators": "^1.5
|
|
25
|
+
"jet-logger": "^2.1.1",
|
|
26
|
+
"jet-paths": "^2.0.1",
|
|
27
|
+
"jet-validators": "^1.6.5",
|
|
28
28
|
"jsonfile": "^6.1.0",
|
|
29
29
|
"module-alias": "^2.2.3",
|
|
30
30
|
"morgan": "^1.10.0"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import logger from 'jet-logger';
|
|
2
2
|
import { copy, copyFilesRec, exec, remove } from './common/utils';
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
/******************************************************************************
|
|
6
5
|
Run
|
|
7
6
|
******************************************************************************/
|
|
@@ -3,7 +3,6 @@ import logger from 'jet-logger';
|
|
|
3
3
|
import childProcess from 'child_process';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
/******************************************************************************
|
|
8
7
|
Functions
|
|
9
8
|
*******************************************************************************
|
|
@@ -3,7 +3,6 @@ import { isValueOf } from 'jet-validators';
|
|
|
3
3
|
|
|
4
4
|
import { NODE_ENVS } from '.';
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
/******************************************************************************
|
|
8
7
|
Setup
|
|
9
8
|
******************************************************************************/
|
|
@@ -13,7 +12,6 @@ const ENV = jetEnv({
|
|
|
13
12
|
Port: num,
|
|
14
13
|
});
|
|
15
14
|
|
|
16
|
-
|
|
17
15
|
/******************************************************************************
|
|
18
16
|
Export default
|
|
19
17
|
******************************************************************************/
|
|
@@ -73,7 +73,6 @@ const HTTP_STATUS_CODES = {
|
|
|
73
73
|
NetworkAuthenticationRequired: 511,
|
|
74
74
|
} as const;
|
|
75
75
|
|
|
76
|
-
|
|
77
76
|
/******************************************************************************
|
|
78
77
|
Export default
|
|
79
78
|
******************************************************************************/
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import jetPaths from 'jet-paths';
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
const PATHS = {
|
|
4
|
+
_: '/api',
|
|
4
5
|
Users: {
|
|
5
|
-
|
|
6
|
+
_: '/users',
|
|
6
7
|
Get: '/all',
|
|
7
8
|
Add: '/add',
|
|
8
9
|
Update: '/update',
|
|
9
10
|
Delete: '/delete/:id',
|
|
10
11
|
},
|
|
11
12
|
} as const;
|
|
13
|
+
|
|
14
|
+
export const JET_PATHS = jetPaths(PATHS);
|
|
15
|
+
export default PATHS;
|
|
@@ -4,7 +4,6 @@ import HTTP_STATUS_CODES, {
|
|
|
4
4
|
HttpStatusCodes,
|
|
5
5
|
} from '@src/common/constants/HTTP_STATUS_CODES';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
/******************************************************************************
|
|
9
8
|
Classes
|
|
10
9
|
******************************************************************************/
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { isNumber, isDate } from 'jet-validators';
|
|
2
2
|
import { transform } from 'jet-validators/utils';
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
/******************************************************************************
|
|
6
5
|
Functions
|
|
7
6
|
******************************************************************************/
|
|
@@ -4,18 +4,16 @@ import { parseObject, TParseOnError } from 'jet-validators/utils';
|
|
|
4
4
|
import { isRelationalKey, transIsDate } from '@src/common/util/validators';
|
|
5
5
|
import { IModel } from './common/types';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
/******************************************************************************
|
|
9
8
|
Constants
|
|
10
9
|
******************************************************************************/
|
|
11
10
|
|
|
12
|
-
const DEFAULT_USER_VALS
|
|
11
|
+
const DEFAULT_USER_VALS: IUser = {
|
|
13
12
|
id: -1,
|
|
14
13
|
name: '',
|
|
15
14
|
created: new Date(),
|
|
16
15
|
email: '',
|
|
17
|
-
}
|
|
18
|
-
|
|
16
|
+
} as const;
|
|
19
17
|
|
|
20
18
|
/******************************************************************************
|
|
21
19
|
Types
|
|
@@ -26,7 +24,6 @@ export interface IUser extends IModel {
|
|
|
26
24
|
email: string;
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
|
|
30
27
|
/******************************************************************************
|
|
31
28
|
Setup
|
|
32
29
|
******************************************************************************/
|
|
@@ -39,7 +36,6 @@ const parseUser = parseObject<IUser>({
|
|
|
39
36
|
created: transIsDate,
|
|
40
37
|
});
|
|
41
38
|
|
|
42
|
-
|
|
43
39
|
/******************************************************************************
|
|
44
40
|
Functions
|
|
45
41
|
******************************************************************************/
|
|
@@ -48,7 +44,9 @@ const parseUser = parseObject<IUser>({
|
|
|
48
44
|
* New user object.
|
|
49
45
|
*/
|
|
50
46
|
function __new__(user?: Partial<IUser>): IUser {
|
|
51
|
-
const
|
|
47
|
+
const defaults = { ...DEFAULT_USER_VALS };
|
|
48
|
+
defaults.created = new Date();
|
|
49
|
+
const retVal = { ...defaults, ...user };
|
|
52
50
|
return parseUser(retVal, errors => {
|
|
53
51
|
throw new Error('Setup new user failed ' + JSON.stringify(errors, null, 2));
|
|
54
52
|
});
|
|
@@ -61,7 +59,6 @@ function test(arg: unknown, errCb?: TParseOnError): arg is IUser {
|
|
|
61
59
|
return !!parseUser(arg, errCb);
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
|
|
65
62
|
/******************************************************************************
|
|
66
63
|
Export default
|
|
67
64
|
******************************************************************************/
|
|
@@ -3,7 +3,6 @@ import { getRandomInt } from '@src/common/util/misc';
|
|
|
3
3
|
|
|
4
4
|
import orm from './MockOrm';
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
/******************************************************************************
|
|
8
7
|
Functions
|
|
9
8
|
******************************************************************************/
|
|
@@ -88,6 +87,9 @@ async function delete_(id: number): Promise<void> {
|
|
|
88
87
|
|
|
89
88
|
/**
|
|
90
89
|
* Delete every user record.
|
|
90
|
+
*
|
|
91
|
+
* @internal
|
|
92
|
+
* Test-only helper. Do not use in production code.
|
|
91
93
|
*/
|
|
92
94
|
async function deleteAllUsers(): Promise<void> {
|
|
93
95
|
const db = await orm.openDb();
|
|
@@ -98,6 +100,9 @@ async function deleteAllUsers(): Promise<void> {
|
|
|
98
100
|
/**
|
|
99
101
|
* Insert multiple users. Can't do multiple at once cause using a plain file
|
|
100
102
|
* for now.
|
|
103
|
+
*
|
|
104
|
+
* @internal
|
|
105
|
+
* Test-only helper. Do not use in production code.
|
|
101
106
|
*/
|
|
102
107
|
async function insertMult(
|
|
103
108
|
users: IUser[] | readonly IUser[],
|
|
@@ -113,7 +118,6 @@ async function insertMult(
|
|
|
113
118
|
return usersF;
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
|
|
117
121
|
/******************************************************************************
|
|
118
122
|
Export default
|
|
119
123
|
******************************************************************************/
|
|
@@ -128,4 +132,3 @@ export default {
|
|
|
128
132
|
deleteAllUsers,
|
|
129
133
|
insertMult,
|
|
130
134
|
} as const;
|
|
131
|
-
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"users":[{"id":
|
|
1
|
+
{"users":[{"id":967269206317,"name":"Bill","created":"2025-12-17T09:55:17.960Z","email":"sean.maxwell@gmail.com"},{"id":3025435078,"name":"John Smith","created":"2025-12-17T09:55:17.960Z","email":"john.smith@gmail.com"},{"id":786429791732,"name":"Gordan Freeman","created":"2025-12-17T09:55:17.960Z","email":"gordan.freeman@gmail.com"}]}
|
|
@@ -8,7 +8,6 @@ import User from '@src/models/User';
|
|
|
8
8
|
import { IReq, IRes } from './common/types';
|
|
9
9
|
import { parseReq } from './common/util';
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
/******************************************************************************
|
|
13
12
|
Constants
|
|
14
13
|
******************************************************************************/
|
|
@@ -19,7 +18,6 @@ const Validators = {
|
|
|
19
18
|
delete: parseReq({ id: transform(Number, isNumber) }),
|
|
20
19
|
} as const;
|
|
21
20
|
|
|
22
|
-
|
|
23
21
|
/******************************************************************************
|
|
24
22
|
Functions
|
|
25
23
|
******************************************************************************/
|
|
@@ -59,7 +57,6 @@ async function delete_(req: IReq, res: IRes) {
|
|
|
59
57
|
res.status(HTTP_STATUS_CODES.Ok).end();
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
|
|
63
60
|
/******************************************************************************
|
|
64
61
|
Export default
|
|
65
62
|
******************************************************************************/
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { parseObject, TSchema } from 'jet-validators/utils';
|
|
2
|
-
|
|
3
2
|
import { ValidationError } from '@src/common/util/route-errors';
|
|
4
3
|
|
|
5
|
-
|
|
6
4
|
/******************************************************************************
|
|
7
5
|
Functions
|
|
8
6
|
******************************************************************************/
|
|
@@ -3,7 +3,6 @@ import { Router } from 'express';
|
|
|
3
3
|
import PATHS from '@src/common/constants/PATHS';
|
|
4
4
|
import UserRoutes from './UserRoutes';
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
/******************************************************************************
|
|
8
7
|
Setup
|
|
9
8
|
******************************************************************************/
|
|
@@ -23,8 +22,7 @@ userRouter.put(PATHS.Users.Update, UserRoutes.update);
|
|
|
23
22
|
userRouter.delete(PATHS.Users.Delete, UserRoutes.delete);
|
|
24
23
|
|
|
25
24
|
// Add UserRouter
|
|
26
|
-
apiRouter.use(PATHS.Users.
|
|
27
|
-
|
|
25
|
+
apiRouter.use(PATHS.Users._, userRouter);
|
|
28
26
|
|
|
29
27
|
/******************************************************************************
|
|
30
28
|
Export default
|
|
@@ -42,7 +42,7 @@ if (ENV.NodeEnv === NODE_ENVS.Production) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// Add APIs, must be after middleware
|
|
45
|
-
app.use(Paths.
|
|
45
|
+
app.use(Paths._, BaseRouter);
|
|
46
46
|
|
|
47
47
|
// Add error handler
|
|
48
48
|
app.use((err: Error, _: Request, res: Response, next: NextFunction) => {
|
|
@@ -4,14 +4,12 @@ import HTTP_STATUS_CODES from '@src/common/constants/HTTP_STATUS_CODES';
|
|
|
4
4
|
import UserRepo from '@src/repos/UserRepo';
|
|
5
5
|
import { IUser } from '@src/models/User';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
/******************************************************************************
|
|
9
8
|
Constants
|
|
10
9
|
******************************************************************************/
|
|
11
10
|
|
|
12
11
|
export const USER_NOT_FOUND_ERR = 'User not found';
|
|
13
12
|
|
|
14
|
-
|
|
15
13
|
/******************************************************************************
|
|
16
14
|
Functions
|
|
17
15
|
******************************************************************************/
|
|
@@ -60,7 +58,6 @@ async function _delete(id: number): Promise<void> {
|
|
|
60
58
|
return UserRepo.delete(id);
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
|
|
64
61
|
/******************************************************************************
|
|
65
62
|
Export default
|
|
66
63
|
******************************************************************************/
|
|
@@ -7,10 +7,10 @@ import { Response } from 'supertest';
|
|
|
7
7
|
|
|
8
8
|
// Use generics to add properties to 'body'
|
|
9
9
|
export type TRes<T = object> = Omit<Response, 'body'> & {
|
|
10
|
-
body: T & { error?: string |
|
|
10
|
+
body: T & { error?: string | IErrorObject },
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
interface
|
|
13
|
+
interface IErrorObject {
|
|
14
14
|
message: string;
|
|
15
15
|
[key: string]: unknown;
|
|
16
16
|
}
|
|
@@ -1,28 +1,18 @@
|
|
|
1
|
-
import { Response } from 'supertest';
|
|
2
1
|
import { IParseObjectError, parseJson } from 'jet-validators/utils';
|
|
3
2
|
import { isString } from 'jet-validators';
|
|
4
|
-
|
|
3
|
+
import { IUser } from '@src/models/User';
|
|
5
4
|
|
|
6
5
|
/******************************************************************************
|
|
7
6
|
Types
|
|
8
7
|
******************************************************************************/
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
export type TRes<T = object> = Omit<Response, 'body'> & {
|
|
12
|
-
body: T & { error?: string | IErrObj },
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
interface IErrObj {
|
|
16
|
-
message: string;
|
|
17
|
-
[key: string]: unknown;
|
|
18
|
-
}
|
|
9
|
+
type TUserArray = IUser[] | readonly IUser[];
|
|
19
10
|
|
|
20
|
-
interface
|
|
11
|
+
interface IValidationError {
|
|
21
12
|
message: string;
|
|
22
13
|
errors: IParseObjectError[];
|
|
23
14
|
}
|
|
24
15
|
|
|
25
|
-
|
|
26
16
|
/******************************************************************************
|
|
27
17
|
Functions
|
|
28
18
|
******************************************************************************/
|
|
@@ -30,9 +20,36 @@ interface IValidationErr {
|
|
|
30
20
|
/**
|
|
31
21
|
* JSON parse a validation error.
|
|
32
22
|
*/
|
|
33
|
-
export function
|
|
23
|
+
export function parseValidationError(arg: unknown): IValidationError {
|
|
34
24
|
if (!isString(arg)) {
|
|
35
25
|
throw new Error('Not a string');
|
|
36
26
|
}
|
|
37
|
-
return parseJson<
|
|
27
|
+
return parseJson<IValidationError>(arg);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Compare to user arrays. Order does not matter and this assumes the email
|
|
32
|
+
* field is unique
|
|
33
|
+
*/
|
|
34
|
+
export function compareUserArrays(a: TUserArray, b: TUserArray): boolean {
|
|
35
|
+
if (a.length !== b.length) return false;
|
|
36
|
+
const aSorted = sortByEmail(a);
|
|
37
|
+
const bSorted = sortByEmail(b);
|
|
38
|
+
for (let i = 0; i < aSorted.length; i++) {
|
|
39
|
+
const a = aSorted[i],
|
|
40
|
+
b = bSorted[i];
|
|
41
|
+
if (a.email !== b.email || a.name !== b.name) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Sort user array by email.
|
|
50
|
+
*/
|
|
51
|
+
function sortByEmail(arr: TUserArray): IUser[] {
|
|
52
|
+
return [...arr].sort((x, y) => {
|
|
53
|
+
return x.email.localeCompare(y.email);
|
|
54
|
+
});
|
|
38
55
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import insertUrlParams from 'inserturlparams';
|
|
2
|
-
import { customDeepCompare } from 'jet-validators/utils';
|
|
3
2
|
|
|
4
3
|
import UserRepo from '@src/repos/UserRepo';
|
|
5
4
|
import User, { IUser } from '@src/models/User';
|
|
@@ -7,11 +6,11 @@ import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
|
|
|
7
6
|
|
|
8
7
|
import HTTP_STATUS_CODES from '@src/common/constants/HTTP_STATUS_CODES';
|
|
9
8
|
import { ValidationError } from '@src/common/util/route-errors';
|
|
9
|
+
import { JET_PATHS as Paths } from '@src/common/constants/PATHS';
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
import { parseValidationErr, TRes } from './common/util';
|
|
11
|
+
import { compareUserArrays, parseValidationError } from './common/util';
|
|
13
12
|
import { agent } from './support/setup';
|
|
14
|
-
|
|
13
|
+
import { TRes } from './common/types';
|
|
15
14
|
|
|
16
15
|
/******************************************************************************
|
|
17
16
|
Constants
|
|
@@ -24,13 +23,6 @@ const DB_USERS = [
|
|
|
24
23
|
User.new({ name: 'Gordan Freeman', email: 'gordan.freeman@gmail.com' }),
|
|
25
24
|
] as const;
|
|
26
25
|
|
|
27
|
-
// Don't compare "id" and "created" cause those are set dynamically by the
|
|
28
|
-
// database
|
|
29
|
-
const compareUserArrays = customDeepCompare({
|
|
30
|
-
onlyCompareProps: ['name', 'email'],
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
|
|
34
26
|
/******************************************************************************
|
|
35
27
|
Tests
|
|
36
28
|
IMPORTANT: Following TypeScript best practices, we test all scenarios that
|
|
@@ -77,7 +69,7 @@ describe('UserRouter', () => {
|
|
|
77
69
|
'missing.', async () => {
|
|
78
70
|
const res: TRes = await agent.post(Paths.Users.Add).send({ user: null });
|
|
79
71
|
expect(res.status).toBe(HTTP_STATUS_CODES.BadRequest);
|
|
80
|
-
const errorObj =
|
|
72
|
+
const errorObj = parseValidationError(res.body.error);
|
|
81
73
|
expect(errorObj.message).toBe(ValidationError.MESSAGE);
|
|
82
74
|
expect(errorObj.errors[0].prop).toBe('user');
|
|
83
75
|
});
|
|
@@ -103,7 +95,7 @@ describe('UserRouter', () => {
|
|
|
103
95
|
user.id = ('5' as unknown as number);
|
|
104
96
|
const res: TRes = await agent.put(Paths.Users.Update).send({ user });
|
|
105
97
|
expect(res.status).toBe(HTTP_STATUS_CODES.BadRequest);
|
|
106
|
-
const errorObj =
|
|
98
|
+
const errorObj = parseValidationError(res.body.error);
|
|
107
99
|
expect(errorObj.message).toBe(ValidationError.MESSAGE);
|
|
108
100
|
expect(errorObj.errors[0].prop).toBe('user');
|
|
109
101
|
expect(errorObj.errors[0].children?.[0].prop).toBe('id');
|
|
@@ -121,9 +113,9 @@ describe('UserRouter', () => {
|
|
|
121
113
|
});
|
|
122
114
|
|
|
123
115
|
// Delete User
|
|
124
|
-
describe(`"DELETE:${Paths.Users.Delete}"`, () => {
|
|
116
|
+
describe(`"DELETE:${Paths.Users.Delete()}"`, () => {
|
|
125
117
|
|
|
126
|
-
const getPath = (id: number) => insertUrlParams(Paths.Users.Delete,
|
|
118
|
+
const getPath = (id: number) => insertUrlParams(Paths.Users.Delete({ id}),
|
|
127
119
|
{ id });
|
|
128
120
|
|
|
129
121
|
// Success
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-generator-typescript",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.7",
|
|
4
4
|
"description": "Generate new Express applications similar to express-generate which but sets it up to use TypeScript instead",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"express-generator-typescript": "node bin/cli.js",
|
|
@@ -19,27 +19,25 @@
|
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
21
|
"express",
|
|
22
|
-
"
|
|
23
|
-
"generator",
|
|
24
|
-
"generate",
|
|
25
|
-
"gen",
|
|
22
|
+
"express-generator",
|
|
23
|
+
"express-generator-typescript",
|
|
26
24
|
"typescript",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
25
|
+
"typescript-express",
|
|
26
|
+
"api",
|
|
27
|
+
"rest",
|
|
28
|
+
"rest-api",
|
|
29
|
+
"scaffold",
|
|
30
|
+
"scaffolding",
|
|
31
|
+
"cli",
|
|
32
|
+
"generator",
|
|
33
|
+
"nodejs",
|
|
34
|
+
"backend",
|
|
35
|
+
"boilerplate",
|
|
36
|
+
"starter",
|
|
37
|
+
"template",
|
|
35
38
|
"express-ts",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"test",
|
|
39
|
-
"testing",
|
|
40
|
-
"routes",
|
|
41
|
-
"router",
|
|
42
|
-
"routing"
|
|
39
|
+
"server",
|
|
40
|
+
"tooling"
|
|
43
41
|
],
|
|
44
42
|
"author": "sean maxwell",
|
|
45
43
|
"license": "MIT",
|