ts-openapi-express 1.0.3 → 1.0.5

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
@@ -1,3 +1,266 @@
1
+ **[Documentation](https://jacobshirley.github.io/ts-openapi-express/v1)**
2
+
1
3
  # ts-openapi-express
2
4
 
3
- For detailed documentation, examples, and API reference, see the [main README](../../README.md).
5
+ An opinionated, type-safe Express.js integration library for OpenAPI specifications. This package enables you to build type-safe REST APIs with full request/response validation using OpenAPI schemas.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/ts-openapi-express.svg)](https://www.npmjs.com/package/ts-openapi-express)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ ## Features
11
+
12
+ - **Type-Safe Routes**: Leverage TypeScript to ensure your route handlers match your OpenAPI spec
13
+ - **Request/Response Validation**: Built-in middleware to validate incoming requests and outgoing responses against your OpenAPI schema
14
+ - **OpenAPI Schema Support**: Load OpenAPI specifications (YAML/JSON) and use them to drive your API implementation
15
+ - **Zero Configuration**: Minimal setup required - just provide your spec and route handlers
16
+ - **Middleware Support**: Integrate custom Express middleware alongside OpenAPI validation
17
+ - **Streaming Support**: Handle streaming responses with native Node.js streams
18
+
19
+ ## Getting Started
20
+
21
+ See [examples/](./examples/blog-api) for a complete working example.
22
+
23
+ ### 1. Generate TypeScript Types from Your OpenAPI Spec
24
+
25
+ The first step is to generate TypeScript type definitions from your OpenAPI specification using [openapi-typescript](https://openapi-ts.pages.dev/).
26
+
27
+ ```bash
28
+ npm install --save-dev openapi-typescript
29
+ ```
30
+
31
+ Add this script to your `package.json`:
32
+
33
+ ```json
34
+ {
35
+ "scripts": {
36
+ "generate:types": "openapi-typescript path/to/your/openapi.yaml --output path/to/generated/types.ts"
37
+ }
38
+ }
39
+ ```
40
+
41
+ Then run it to generate the types:
42
+
43
+ ```bash
44
+ npm run generate:types
45
+ ```
46
+
47
+ It's also recommended to add it to your `compile/build` script to ensure types are always up to date.
48
+
49
+ ```json
50
+ {
51
+ "scripts": {
52
+ "build": "npm run generate:types && tsc"
53
+ }
54
+ }
55
+ ```
56
+
57
+ This command generates a `types.ts` file with path types that look like:
58
+
59
+ ```typescript
60
+ export interface paths {
61
+ '/users': {
62
+ get: {
63
+ responses: {
64
+ '200': {
65
+ content: {
66
+ 'application/json': {
67
+ id: string
68
+ name: string
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ // ... more paths
76
+ }
77
+ ```
78
+
79
+ ### 2. Install ts-openapi-express
80
+
81
+ ```bash
82
+ npm install ts-openapi-express
83
+ yarn add ts-openapi-express
84
+ pnpm add ts-openapi-express
85
+ ```
86
+
87
+ ### 3. Define Your Routes with Type Safety
88
+
89
+ ```typescript
90
+ import { openapiExpress } from 'ts-openapi-express'
91
+ import { paths } from './generated/types' // Generated from step 1
92
+
93
+ const app = openapiExpress<paths>({
94
+ specPath: './openapi.yaml',
95
+ routes: {
96
+ '/users': {
97
+ get: {
98
+ handler: async (req) => ({
99
+ 200: {
100
+ headers: {},
101
+ body: [
102
+ { id: '1', name: 'John' },
103
+ { id: '2', name: 'Jane' },
104
+ ],
105
+ },
106
+ }),
107
+ },
108
+ post: {
109
+ handler: async (req) => ({
110
+ 201: {
111
+ headers: { 'content-type': 'application/json' },
112
+ body: { id: '3', name: req.body.name },
113
+ },
114
+ }),
115
+ },
116
+ },
117
+ '/users/{id}': {
118
+ get: {
119
+ handler: async (req) => ({
120
+ 200: {
121
+ headers: {},
122
+ body: { id: req.params.id, name: 'John' },
123
+ },
124
+ }),
125
+ },
126
+ },
127
+ },
128
+ })
129
+
130
+ app.listen(3000, () => {
131
+ console.log('API running on http://localhost:3000')
132
+ console.log('OpenAPI spec available at http://localhost:3000/openapi.json')
133
+ })
134
+ ```
135
+
136
+ ## Examples
137
+
138
+ See the [examples/](./examples/) directory for complete working examples, including a simple blog API.
139
+
140
+ ## API Options
141
+
142
+ ```typescript
143
+ interface OpenapiExpressOptions<Spec> {
144
+ /**
145
+ * Path to your OpenAPI specification file (YAML or JSON)
146
+ */
147
+ specPath: string
148
+
149
+ /**
150
+ * Route handlers object mapping paths to HTTP method handlers
151
+ */
152
+ routes: Routes<Spec>
153
+
154
+ /**
155
+ * Optional Express middleware to use
156
+ */
157
+ middleware?: ExpressMiddleware[]
158
+
159
+ /**
160
+ * Enable request validation against OpenAPI spec (default: true)
161
+ */
162
+ validateRequest?: boolean
163
+
164
+ /**
165
+ * Enable response validation against OpenAPI spec (default: true)
166
+ */
167
+ validateResponse?: boolean
168
+
169
+ /**
170
+ * Automatically parse JSON request bodies (default: true)
171
+ */
172
+ decodeJsonBody?: boolean
173
+
174
+ /**
175
+ * Provide an existing Express app instance (optional)
176
+ */
177
+ app?: Application
178
+
179
+ /**
180
+ * Disable the 'X-Powered-By' header (default: true)
181
+ */
182
+ disableXPoweredBy?: boolean
183
+ }
184
+ ```
185
+
186
+ ## Handler Signature
187
+
188
+ Route handlers receive type-safe request objects with:
189
+
190
+ - `req.params`: Path parameters (fully typed)
191
+ - `req.query`: Query parameters (fully typed)
192
+ - `req.body`: Request body (fully typed)
193
+ - `req.headers`: HTTP headers
194
+
195
+ Handlers must return a response object with a single status code:
196
+
197
+ ```typescript
198
+ {
199
+ 200: {
200
+ headers: { 'content-type': 'application/json' },
201
+ body: { /* response data */ }
202
+ }
203
+ }
204
+ ```
205
+
206
+ Or, if no body is required:
207
+
208
+ ```typescript
209
+ {
210
+ 204: {
211
+ headers: {}
212
+ }
213
+ }
214
+ ```
215
+
216
+ Streaming responses are supported by passing a Node.js `Readable`:
217
+
218
+ ```typescript
219
+ {
220
+ 200: {
221
+ headers: { 'content-type': 'text/csv' },
222
+ body: fs.createReadStream('data.csv')
223
+ }
224
+ }
225
+ ```
226
+
227
+ ## Examples
228
+
229
+ ### Running the Test
230
+
231
+ This repository includes a comprehensive test suite that demonstrates usage. To run it:
232
+
233
+ ```bash
234
+ # This will:
235
+ # 1. Generate types from the OpenAPI spec
236
+ # 2. Run the test suite with validation
237
+ npm test
238
+ ```
239
+
240
+ The test file at `packages/ts-openapi-express/test/unit/openapi.test.ts` shows:
241
+
242
+ - Type-safe route definition
243
+ - Request/response validation
244
+ - Error handling
245
+ - Path parameter usage
246
+ - Query parameter handling
247
+
248
+ ### Regenerating Test Types
249
+
250
+ If you modify the test OpenAPI spec, regenerate the types:
251
+
252
+ ```bash
253
+ npm run test:compile:spec
254
+ ```
255
+
256
+ ## Architecture
257
+
258
+ The library works in three main steps:
259
+
260
+ 1. **Type Generation** (via openapi-typescript): Convert your OpenAPI spec into TypeScript types
261
+ 2. **Route Definition**: Define your Express handlers using the generated types
262
+ 3. **Validation**: Automatic request/response validation ensures your implementation matches the spec
263
+
264
+ ## License
265
+
266
+ MIT
package/dist/errors.d.ts CHANGED
@@ -1,12 +1,7 @@
1
- import { error as openapiValidatorErrors } from 'express-openapi-validator';
2
- export type OpenapiValidatorError = (typeof openapiValidatorErrors)[keyof typeof openapiValidatorErrors];
3
- declare const isExpressOpenAPIValidatorError: (err: any) => err is Error & {
4
- status: number;
5
- };
1
+ import { error as OpenapiValidationErrors } from 'express-openapi-validator';
6
2
  declare class HttpError extends Error {
7
3
  readonly status: number;
8
4
  constructor(message: string, status: number);
9
- type(): string;
10
5
  }
11
6
  declare class UnauthorizedError extends HttpError {
12
7
  constructor();
@@ -18,4 +13,4 @@ declare class InvalidJsonError extends HttpError {
18
13
  readonly jsonString: string;
19
14
  constructor(jsonString: string);
20
15
  }
21
- export { isExpressOpenAPIValidatorError, HttpError, RequestBodyTooLargeError, InvalidJsonError, UnauthorizedError, };
16
+ export { OpenapiValidationErrors, HttpError, RequestBodyTooLargeError, InvalidJsonError, UnauthorizedError, };
package/dist/errors.js CHANGED
@@ -1,22 +1,10 @@
1
- import { error as openapiValidatorErrors } from 'express-openapi-validator';
2
- const isExpressOpenAPIValidatorError = (err) => {
3
- for (const key in openapiValidatorErrors) {
4
- if (err instanceof
5
- openapiValidatorErrors[key]) {
6
- return true;
7
- }
8
- }
9
- return false;
10
- };
1
+ import { error as OpenapiValidationErrors } from 'express-openapi-validator';
11
2
  class HttpError extends Error {
12
3
  status;
13
4
  constructor(message, status) {
14
5
  super(message);
15
6
  this.status = status;
16
7
  }
17
- type() {
18
- return this.constructor.name;
19
- }
20
8
  }
21
9
  class UnauthorizedError extends HttpError {
22
10
  constructor() {
@@ -35,4 +23,4 @@ class InvalidJsonError extends HttpError {
35
23
  this.jsonString = jsonString;
36
24
  }
37
25
  }
38
- export { isExpressOpenAPIValidatorError, HttpError, RequestBodyTooLargeError, InvalidJsonError, UnauthorizedError, };
26
+ export { OpenapiValidationErrors, HttpError, RequestBodyTooLargeError, InvalidJsonError, UnauthorizedError, };
@@ -15,7 +15,7 @@ function openapiExpress(options) {
15
15
  if (decodeJsonBody) {
16
16
  app.use(json());
17
17
  }
18
- if (validateRequest || validateResponse)
18
+ if (validateRequest || validateResponse) {
19
19
  app.use(...requestResponseValidatorMiddleware({
20
20
  spec,
21
21
  validation: {
@@ -23,6 +23,7 @@ function openapiExpress(options) {
23
23
  responses: validateResponse,
24
24
  },
25
25
  }));
26
+ }
26
27
  for (const m of middleware) {
27
28
  app.use(m);
28
29
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ts-openapi-express",
3
3
  "type": "module",
4
- "version": "1.0.3",
4
+ "version": "1.0.5",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "directories": {
@@ -10,6 +10,9 @@
10
10
  "repository": {
11
11
  "url": "https://github.com/jacobshirley/ts-openapi-express"
12
12
  },
13
+ "bugs": {
14
+ "url": "https://github.com/jacobshirley/ts-openapi-express/issues"
15
+ },
13
16
  "homepage": "https://jacobshirley.github.io/ts-openapi-express/v1",
14
17
  "keywords": [
15
18
  "openapi",
@@ -28,13 +31,23 @@
28
31
  "author": "",
29
32
  "license": "ISC",
30
33
  "dependencies": {
31
- "express": "^5.0.0",
32
- "express-openapi-validator": "^5.3.9",
33
34
  "lodash-es": "^4.17.23",
34
- "yaml": "^2.8.1"
35
+ "yaml": "^2.8.1",
36
+ "express-openapi-validator": "^5.0.0"
37
+ },
38
+ "peerDependencies": {
39
+ "express": "^5.0.0"
40
+ },
41
+ "peerDependenciesMeta": {
42
+ "express": {
43
+ "optional": false
44
+ },
45
+ "express-openapi-validator": {
46
+ "optional": false
47
+ }
35
48
  },
36
49
  "devDependencies": {
37
- "@types/express": "^5.0.0",
50
+ "@types/express": "^5.0.6",
38
51
  "@types/lodash-es": "^4.17.12",
39
52
  "@types/node": "^22.0.0",
40
53
  "@types/supertest": "^6.0.2",
@@ -50,9 +63,9 @@
50
63
  "README.md"
51
64
  ],
52
65
  "scripts": {
53
- "test": "pnpm test:unit",
54
- "test:unit": "pnpm test:compile:spec && vitest run",
55
- "test:compile:spec": "openapi-typescript test/unit/openapi.test.yaml --output test/unit/test-schema.ts",
66
+ "test": "vitest run",
67
+ "test:unit": "pnpm pretest && vitest run",
68
+ "pretest": "openapi-typescript test/unit/openapi.test.yaml --output test/unit/test-schema.gen.ts",
56
69
  "compile": "tsc -p tsconfig.prod.json",
57
70
  "watch": "tsc -p tsconfig.prod.json --watch"
58
71
  }