transit-kit 0.8.0 → 0.8.2

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,19 +1,29 @@
1
- # transit-kit
1
+ # Transit-Kit
2
2
 
3
3
  [![CI](https://github.com/D4rkr34lm/transit-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/D4rkr34lm/transit-kit/actions/workflows/ci.yml)
4
4
  [![Coverage Status](https://coveralls.io/repos/github/D4rkr34lm/transit-kit/badge.svg?branch=main)](https://coveralls.io/github/D4rkr34lm/transit-kit?branch=main)
5
+ [![npm version](https://badge.fury.io/js/transit-kit.svg)](https://www.npmjs.com/package/transit-kit)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9+-blue.svg)](https://www.typescriptlang.org/)
8
+ [![Node.js](https://img.shields.io/badge/Node.js-22+-green.svg)](https://nodejs.org/)
5
9
 
6
- A declarative TypeScript framework for building type-safe Express.js APIs with automatic OpenAPI generation.
10
+ A **declarative TypeScript framework** for building end-to-end type-safe REST APIs with Express.js and automatic OpenAPI documentation generation.
7
11
 
8
- ## Features
12
+ ## Why Transit-Kit?
9
13
 
10
- - 🔒 **Type-Safe**: End-to-end type safety with TypeScript and Zod validation
11
- - 📝 **Declarative API Definition**: Define your endpoints with clear, declarative syntax
12
- - 🔄 **Automatic Validation**: Request body and query parameter validation using Zod schemas
13
- - 📚 **OpenAPI Generation**: Automatically generate OpenAPI documentation from your endpoint definitions
14
- - ⚡ **Express.js Powered**: Built on top of the battle-tested Express.js framework
15
- - ðŸŠĩ **Built-in Logging**: Request and response logging out of the box
16
- - ðŸŽŊ **Path Parameters**: Full support for path parameters with type safety
14
+ Transit-Kit brings modern type-safety and developer experience to Express.js without abandoning the familiar, battle-tested foundation you already know. It's built on minimalism:
15
+
16
+ - ðŸŽŊ **Express at its core** — Use the familiar Express API you already know
17
+ - 🔒 **End-to-end type safety** — Full TypeScript inference from request to response
18
+ - 📝 **Auto-generated OpenAPI** — Documentation that stays in sync with your code
19
+ - ✅ **Zod validation** — Runtime type checking with zero boilerplate
20
+ - ðŸŠķ **Minimal overhead** — Thin layer over Express, not a complete rewrite
21
+ - **Declarative definitions** — Define endpoints declaratively with full type info
22
+
23
+ ## Requirements
24
+
25
+ - **Node.js 22+**
26
+ - **TypeScript 5.9+**
17
27
 
18
28
  ## Installation
19
29
 
@@ -21,450 +31,681 @@ A declarative TypeScript framework for building type-safe Express.js APIs with a
21
31
  npm install transit-kit
22
32
  ```
23
33
 
24
- ## Quick Start
34
+ ```bash
35
+ yarn add transit-kit
36
+ ```
25
37
 
26
- ### 1. Create a Server
38
+ ```bash
39
+ pnpm add transit-kit
40
+ ```
41
+
42
+ ## Quick Start
27
43
 
28
44
  ```typescript
29
45
  import { createServer } from "transit-kit/server";
46
+ import { createApiEndpointHandler } from "transit-kit/server";
47
+ import { z } from "zod";
30
48
 
49
+ // Create a server
31
50
  const server = createServer({
32
51
  port: 3000,
33
52
  inDevMode: true,
34
- logger: true, // or pass a custom logger (console-like interface)
53
+ logger: true,
35
54
  });
55
+
56
+ // Define a simple endpoint
57
+ const helloEndpoint = createApiEndpointHandler(
58
+ {
59
+ meta: {
60
+ name: "sayHello",
61
+ group: "Greetings",
62
+ description: "Returns a greeting message",
63
+ },
64
+ path: "/hello/:name",
65
+ method: "get",
66
+ responseSchemas: {
67
+ 200: {
68
+ type: "json",
69
+ schema: z.object({
70
+ message: z.string(),
71
+ }),
72
+ },
73
+ },
74
+ securitySchemes: [],
75
+ },
76
+ async ({ parameters }) => {
77
+ return {
78
+ code: 200,
79
+ data: {
80
+ message: `Hello, ${parameters.name}!`,
81
+ },
82
+ };
83
+ }
84
+ );
85
+
86
+ // Register and start
87
+ server.registerApiEndpoint(helloEndpoint);
88
+ server.start();
36
89
  ```
37
90
 
38
- ### 2. Define an API Endpoint
91
+ ## Core Concepts
92
+
93
+ ### 1. Endpoint Definitions
94
+
95
+ Every API endpoint in Transit-Kit is defined declaratively with full type information:
39
96
 
40
97
  ```typescript
41
- import { createApiEndpointHandler } from "transit-kit/server";
42
- import z from "zod";
98
+ const definition = {
99
+ meta: {
100
+ name: "createUser", // Unique operation ID
101
+ group: "Users", // OpenAPI tag
102
+ description: "Create a user" // Endpoint description
103
+ },
104
+ path: "/users", // Express-style path
105
+ method: "post", // HTTP method
106
+ requestBodySchema: z.object({ // Request validation (optional)
107
+ name: z.string(),
108
+ email: z.string().email(),
109
+ }),
110
+ querySchema: z.object({ // Query params validation (optional)
111
+ sendEmail: z.boolean().optional(),
112
+ }),
113
+ responseSchemas: { // All possible responses
114
+ 201: {
115
+ type: "json",
116
+ schema: z.object({
117
+ id: z.string(),
118
+ name: z.string(),
119
+ email: z.string(),
120
+ }),
121
+ },
122
+ 400: {
123
+ type: "json",
124
+ schema: z.object({
125
+ error: z.string(),
126
+ }),
127
+ },
128
+ },
129
+ securitySchemes: [], // Auth schemes (if any)
130
+ };
131
+ ```
132
+
133
+ ### 2. Type-Safe Handlers
134
+
135
+ Handlers automatically infer types from your endpoint definition:
136
+
137
+ ```typescript
138
+ const handler = async ({ body, query, parameters }) => {
139
+ // body is typed as { name: string, email: string }
140
+ // query is typed as { sendEmail?: boolean }
141
+ // parameters are inferred from the path pattern
142
+
143
+ const user = await createUserInDatabase(body);
144
+
145
+ return {
146
+ code: 201,
147
+ data: user, // Must match the response schema for code 201
148
+ };
149
+ };
150
+ ```
151
+
152
+ ### 3. Server Registration
153
+
154
+ Combine definitions and handlers, then register with your server:
155
+
156
+ ```typescript
157
+ const endpoint = createApiEndpointHandler(definition, handler);
158
+ server.registerApiEndpoint(endpoint);
159
+ ```
160
+
161
+ ## Example: CRUD API
162
+
163
+ Here's a complete example showing a REST API for managing todos:
164
+
165
+ ```typescript
166
+ import { createServer, createApiEndpointHandler } from "transit-kit/server";
167
+ import { z } from "zod";
168
+
169
+ const server = createServer({
170
+ port: 3000,
171
+ inDevMode: true,
172
+ logger: true,
173
+ });
43
174
 
44
- // Define a simple GET endpoint
45
- const getUserEndpoint = createApiEndpointHandler(
175
+ // In-memory storage (use a real database in production)
176
+ const todos: Map<string, { id: string; title: string; completed: boolean }> = new Map();
177
+
178
+ // Schemas
179
+ const TodoSchema = z.object({
180
+ id: z.string(),
181
+ title: z.string(),
182
+ completed: z.boolean(),
183
+ });
184
+
185
+ const CreateTodoSchema = z.object({
186
+ title: z.string().min(1),
187
+ });
188
+
189
+ // CREATE
190
+ const createTodo = createApiEndpointHandler(
46
191
  {
47
192
  meta: {
48
- name: "Get User",
49
- description: "Retrieves a user by ID",
50
- group: "Users",
193
+ name: "createTodo",
194
+ group: "Todos",
195
+ description: "Create a new todo item",
196
+ },
197
+ path: "/todos",
198
+ method: "post",
199
+ requestBodySchema: CreateTodoSchema,
200
+ responseSchemas: {
201
+ 201: {
202
+ type: "json",
203
+ schema: TodoSchema,
204
+ },
205
+ },
206
+ securitySchemes: [],
207
+ },
208
+ async ({ body }) => {
209
+ const id = crypto.randomUUID();
210
+ const todo = {
211
+ id,
212
+ title: body.title,
213
+ completed: false,
214
+ };
215
+ todos.set(id, todo);
216
+
217
+ return {
218
+ code: 201,
219
+ data: todo,
220
+ };
221
+ }
222
+ );
223
+
224
+ // READ (List)
225
+ const listTodos = createApiEndpointHandler(
226
+ {
227
+ meta: {
228
+ name: "listTodos",
229
+ group: "Todos",
230
+ description: "Get all todo items",
51
231
  },
232
+ path: "/todos",
52
233
  method: "get",
53
- path: "/users/:userId",
54
234
  responseSchemas: {
55
235
  200: {
56
- dataType: "application/json",
57
- dataSchema: z.object({
58
- id: z.string(),
59
- name: z.string(),
60
- email: z.string().email(),
236
+ type: "json",
237
+ schema: z.object({
238
+ todos: z.array(TodoSchema),
61
239
  }),
62
240
  },
63
- 404: {}, // Empty response
64
241
  },
242
+ securitySchemes: [],
65
243
  },
66
- async (request) => {
67
- const { userId } = request.params;
68
-
69
- // Simulate fetching user
70
- const user = await fetchUser(userId);
244
+ async () => {
245
+ return {
246
+ code: 200,
247
+ data: {
248
+ todos: Array.from(todos.values()),
249
+ },
250
+ };
251
+ }
252
+ );
71
253
 
72
- if (!user) {
254
+ // READ (Single)
255
+ const getTodo = createApiEndpointHandler(
256
+ {
257
+ meta: {
258
+ name: "getTodo",
259
+ group: "Todos",
260
+ description: "Get a specific todo item",
261
+ },
262
+ path: "/todos/:id",
263
+ method: "get",
264
+ responseSchemas: {
265
+ 200: {
266
+ type: "json",
267
+ schema: TodoSchema,
268
+ },
269
+ 404: {
270
+ type: "json",
271
+ schema: z.object({ error: z.string() }),
272
+ },
273
+ },
274
+ securitySchemes: [],
275
+ },
276
+ async ({ parameters }) => {
277
+ const todo = todos.get(parameters.id);
278
+
279
+ if (!todo) {
73
280
  return {
74
281
  code: 404,
282
+ data: { error: "Todo not found" },
75
283
  };
76
284
  }
77
285
 
78
286
  return {
79
287
  code: 200,
80
- dataType: "application/json",
81
- json: user,
288
+ data: todo,
82
289
  };
83
- },
290
+ }
84
291
  );
85
- ```
86
292
 
87
- ### 3. Define an Endpoint with Request Body and Query Parameters
88
-
89
- ```typescript
90
- const createUserEndpoint = createApiEndpointHandler(
293
+ // UPDATE
294
+ const updateTodo = createApiEndpointHandler(
91
295
  {
92
296
  meta: {
93
- name: "Create User",
94
- description: "Creates a new user",
95
- group: "Users",
297
+ name: "updateTodo",
298
+ group: "Todos",
299
+ description: "Update a todo item",
96
300
  },
97
- method: "post",
98
- path: "/users",
301
+ path: "/todos/:id",
302
+ method: "put",
99
303
  requestBodySchema: z.object({
100
- name: z.string().min(1),
101
- email: z.string().email(),
102
- age: z.number().min(18),
103
- }),
104
- querySchema: z.object({
105
- notify: z.boolean().optional(),
304
+ title: z.string().optional(),
305
+ completed: z.boolean().optional(),
106
306
  }),
107
307
  responseSchemas: {
108
- 201: {
109
- dataType: "application/json",
110
- dataSchema: z.object({
111
- id: z.string(),
112
- name: z.string(),
113
- email: z.string().email(),
114
- }),
308
+ 200: {
309
+ type: "json",
310
+ schema: TodoSchema,
115
311
  },
116
- 400: {
117
- dataType: "application/json",
118
- dataSchema: z.object({
119
- error: z.string(),
120
- }),
312
+ 404: {
313
+ type: "json",
314
+ schema: z.object({ error: z.string() }),
121
315
  },
122
316
  },
317
+ securitySchemes: [],
123
318
  },
124
- async (request) => {
125
- // Request body is automatically validated and typed
126
- const { name, email, age } = request.body;
127
- const { notify } = request.query;
319
+ async ({ parameters, body }) => {
320
+ const todo = todos.get(parameters.id);
321
+
322
+ if (!todo) {
323
+ return {
324
+ code: 404,
325
+ data: { error: "Todo not found" },
326
+ };
327
+ }
128
328
 
129
- try {
130
- const newUser = await createUser({ name, email, age });
329
+ const updated = {
330
+ ...todo,
331
+ ...body,
332
+ };
333
+ todos.set(parameters.id, updated);
131
334
 
132
- if (notify) {
133
- await sendNotification(email);
134
- }
335
+ return {
336
+ code: 200,
337
+ data: updated,
338
+ };
339
+ }
340
+ );
135
341
 
342
+ // DELETE
343
+ const deleteTodo = createApiEndpointHandler(
344
+ {
345
+ meta: {
346
+ name: "deleteTodo",
347
+ group: "Todos",
348
+ description: "Delete a todo item",
349
+ },
350
+ path: "/todos/:id",
351
+ method: "delete",
352
+ responseSchemas: {
353
+ 204: {
354
+ type: "json",
355
+ schema: z.object({}),
356
+ },
357
+ 404: {
358
+ type: "json",
359
+ schema: z.object({ error: z.string() }),
360
+ },
361
+ },
362
+ securitySchemes: [],
363
+ },
364
+ async ({ parameters }) => {
365
+ const exists = todos.has(parameters.id);
366
+
367
+ if (!exists) {
136
368
  return {
137
- code: 201,
138
- dataType: "application/json",
139
- json: newUser,
140
- };
141
- } catch (error) {
142
- return {
143
- code: 400,
144
- dataType: "application/json",
145
- json: { error: error.message },
369
+ code: 404,
370
+ data: { error: "Todo not found" },
146
371
  };
147
372
  }
148
- },
149
- );
150
- ```
151
373
 
152
- ### 4. Register Endpoints and Start Server
374
+ todos.delete(parameters.id);
153
375
 
154
- ```typescript
155
- server.registerApiEndpoint(getUserEndpoint);
156
- server.registerApiEndpoint(createUserEndpoint);
376
+ return {
377
+ code: 204,
378
+ data: {},
379
+ };
380
+ }
381
+ );
382
+
383
+ // Register all endpoints
384
+ server.registerApiEndpoint(createTodo);
385
+ server.registerApiEndpoint(listTodos);
386
+ server.registerApiEndpoint(getTodo);
387
+ server.registerApiEndpoint(updateTodo);
388
+ server.registerApiEndpoint(deleteTodo);
157
389
 
158
390
  server.start();
391
+ console.log("Server running on http://localhost:3000");
159
392
  ```
160
393
 
161
394
  ## Authentication
162
395
 
163
- Transit-kit includes a simple, composable authentication mechanism based on `Authorization` headers. You declare one or more `SecurityScheme`s on an endpoint, and the framework will:
396
+ Transit-Kit supports **Basic** and **Bearer** authentication schemes out of the box.
164
397
 
165
- - Apply an authentication middleware for that endpoint
166
- - Try all declared schemes in order and pick the first successful one
167
- - Return `401 Unauthorized` automatically when authentication fails
168
- - Inject the authenticated `caller` into your endpoint handler
398
+ ### Basic Authentication
169
399
 
170
- ### Basic Auth
400
+ ```typescript
401
+ import { createBasicAuthSchema } from "transit-kit/server";
171
402
 
172
- Supports `Authorization: Basic <base64(username:password)>`.
403
+ // Define your user type
404
+ interface User {
405
+ id: string;
406
+ username: string;
407
+ role: string;
408
+ }
173
409
 
174
- ```typescript
175
- import {
176
- createApiEndpointHandler,
177
- createServer,
178
- } from "transit-kit/server";
179
- import { createBasicAuthSchema } from "transit-kit/server/security/basicAuth";
180
- import z from "zod";
181
-
182
- // Define the shape of the authenticated caller
183
- type Caller = { id: string; role: "admin" | "user" };
184
-
185
- // Your validation logic (DB lookup, etc.)
186
- const basicAuth = createBasicAuthSchema<Caller>(
187
- "basic",
410
+ // Create an auth scheme
411
+ const basicAuth = createBasicAuthSchema<User>(
412
+ "basicAuth",
188
413
  async (username, password) => {
189
- const user = await findUserByUsername(username);
190
- if (!user) return null;
191
- const ok = await verifyPassword(password, user.passwordHash);
192
- return ok ? { id: user.id, role: user.role } : null;
193
- },
194
- );
195
-
196
- const getProfile = createApiEndpointHandler(
197
- {
198
- meta: { name: "Get Profile", description: "Returns caller profile", group: "Auth" },
199
- method: "get",
200
- path: "/me",
201
- responseSchemas: {
202
- 200: {
203
- dataType: "application/json",
204
- dataSchema: z.object({ id: z.string(), role: z.enum(["admin", "user"]) }),
205
- },
206
- 401: {},
207
- },
208
- // Enable authentication for this endpoint
209
- securitySchemes: [basicAuth],
210
- },
211
- async (_req, { caller }) => {
212
- // `caller` is typed from your security scheme
213
- return { code: 200, dataType: "application/json", json: caller };
214
- },
414
+ // Validate credentials (use your database)
415
+ if (username === "admin" && password === "secret") {
416
+ return {
417
+ id: "1",
418
+ username: "admin",
419
+ role: "admin",
420
+ };
421
+ }
422
+ return null; // Invalid credentials
423
+ }
215
424
  );
216
-
217
- const server = createServer({ port: 3000, inDevMode: true, logger: true });
218
- server.registerApiEndpoint(getProfile);
219
- server.start();
220
425
  ```
221
426
 
222
- ### Bearer Token Auth
223
-
224
- Supports `Authorization: Bearer <token>`.
427
+ ### Bearer Authentication
225
428
 
226
429
  ```typescript
227
- import { createBearerAuthSchema } from "transit-kit/server/security/bearerAuth";
228
-
229
- type Caller = { id: string; scopes: string[] };
430
+ import { createBearerAuthSchema } from "transit-kit/server";
230
431
 
231
- const bearerAuth = createBearerAuthSchema<Caller>(
232
- "bearer",
432
+ const bearerAuth = createBearerAuthSchema<User>(
433
+ "bearerAuth",
233
434
  async (token) => {
234
- const payload = await verifyJwt(token); // or call your token introspection service
235
- return payload ? { id: payload.sub, scopes: payload.scopes } : null;
236
- },
435
+ // Validate token (e.g., JWT verification)
436
+ const user = await verifyJWT(token);
437
+ return user; // or null if invalid
438
+ }
237
439
  );
440
+ ```
238
441
 
239
- const getSecret = createApiEndpointHandler(
442
+ ### Protected Endpoints
443
+
444
+ ```typescript
445
+ const protectedEndpoint = createApiEndpointHandler(
240
446
  {
241
- meta: { name: "Get Secret", description: "Protected resource", group: "Auth" },
447
+ meta: {
448
+ name: "getProfile",
449
+ group: "Users",
450
+ description: "Get current user profile",
451
+ },
452
+ path: "/profile",
242
453
  method: "get",
243
- path: "/secret",
244
454
  responseSchemas: {
245
- 200: { dataType: "application/json", dataSchema: z.object({ secret: z.string() }) },
246
- 401: {},
455
+ 200: {
456
+ type: "json",
457
+ schema: z.object({
458
+ id: z.string(),
459
+ username: z.string(),
460
+ role: z.string(),
461
+ }),
462
+ },
463
+ 401: {
464
+ type: "json",
465
+ schema: z.object({ error: z.string() }),
466
+ },
247
467
  },
248
- securitySchemes: [bearerAuth],
249
- },
250
- async (_req, { caller }) => {
251
- // Use `caller.scopes` for authorization decisions
252
- return { code: 200, dataType: "application/json", json: { secret: "shh" } };
468
+ securitySchemes: [basicAuth], // Require authentication
253
469
  },
470
+ async ({ caller }) => {
471
+ // caller is typed as User | null
472
+ if (!caller) {
473
+ return {
474
+ code: 401,
475
+ data: { error: "Unauthorized" },
476
+ };
477
+ }
478
+
479
+ return {
480
+ code: 200,
481
+ data: caller, // Fully typed user object
482
+ };
483
+ }
254
484
  );
485
+
486
+ server.registerApiEndpoint(protectedEndpoint);
255
487
  ```
256
488
 
257
- ### Multiple Schemes per Endpoint
489
+ ### Multiple Auth Schemes
258
490
 
259
- You can allow multiple authentication methods on the same endpoint. The framework tries them in the order you specify and picks the first successful scheme.
491
+ You can support multiple authentication methods:
260
492
 
261
493
  ```typescript
262
494
  const endpoint = createApiEndpointHandler(
263
495
  {
264
- meta: { name: "Hybrid Auth", description: "Basic or Bearer", group: "Auth" },
265
- method: "get",
266
- path: "/hybrid",
267
- responseSchemas: { 200: { dataType: "application/json", dataSchema: z.object({ ok: z.boolean() }) }, 401: {} },
268
- securitySchemes: [basicAuth, bearerAuth],
269
- },
270
- async (_req, { caller }) => {
271
- // `caller` resolves from whichever scheme authenticated successfully
272
- return { code: 200, dataType: "application/json", json: { ok: true } };
496
+ // ... other config
497
+ securitySchemes: [basicAuth, bearerAuth], // Accept either
273
498
  },
499
+ async ({ caller }) => {
500
+ // caller will be set if any scheme succeeds
501
+ // ...
502
+ }
274
503
  );
275
504
  ```
276
505
 
277
- ### How It Works
278
-
279
- - Authentication middleware is built automatically per-endpoint when `securitySchemes` are present.
280
- - Internals use `authenticate()` from [src/server/security/SecuritySchema.ts](src/server/security/SecuritySchema.ts) to try each scheme.
281
- - Basic auth reads and decodes the header in [src/server/security/basicAuth.ts](src/server/security/basicAuth.ts).
282
- - Bearer auth reads the header in [src/server/security/bearerAuth.ts](src/server/security/bearerAuth.ts).
283
- - On success, the authenticated `caller` is stored and passed to your handler as `extractedRequestData.caller`.
284
- - On failure, the framework responds with `401` and a JSON `{ message: "Unauthorized" }` from [src/server/middleware/auth.ts](src/server/middleware/auth.ts).
506
+ ## OpenAPI Documentation
285
507
 
286
- ## Response Types
508
+ Transit-Kit automatically generates OpenAPI 3.0 documentation from your endpoint definitions.
287
509
 
288
- ### JSON Response
510
+ ```typescript
511
+ import { generateOpenApiDoc } from "transit-kit/generator";
512
+
513
+ const openApiDoc = await generateOpenApiDoc(server, {
514
+ title: "My API",
515
+ version: "1.0.0",
516
+ description: "A type-safe REST API built with Transit-Kit",
517
+ servers: [
518
+ {
519
+ url: "http://localhost:3000",
520
+ description: "Development server",
521
+ },
522
+ {
523
+ url: "https://api.example.com",
524
+ description: "Production server",
525
+ },
526
+ ],
527
+ contact: {
528
+ name: "API Support",
529
+ email: "support@example.com",
530
+ },
531
+ license: {
532
+ name: "MIT",
533
+ url: "https://opensource.org/licenses/MIT",
534
+ },
535
+ });
289
536
 
290
- For endpoints that return JSON data:
537
+ // Serve the OpenAPI spec
538
+ server.expressApp.get("/openapi.json", (req, res) => {
539
+ res.json(openApiDoc);
540
+ });
291
541
 
292
- ```typescript
293
- return {
294
- code: 200,
295
- dataType: "application/json",
296
- json: { message: "Success", data: myData },
297
- };
542
+ // Or write to file
543
+ import { writeFileSync } from "fs";
544
+ writeFileSync("./openapi.json", JSON.stringify(openApiDoc, null, 2));
298
545
  ```
299
546
 
300
- ### Empty Response
547
+ The generated OpenAPI document includes:
548
+ - All endpoints with request/response schemas
549
+ - Path and query parameters
550
+ - Request body validation schemas
551
+ - Security requirements
552
+ - Response schemas for all status codes
301
553
 
302
- For endpoints that return no content (e.g., 204 No Content):
554
+ You can use this with tools like:
555
+ - **Swagger UI** for interactive documentation
556
+ - **Postman** for API testing
557
+ - **OpenAPI Generator** for client SDK generation
303
558
 
304
- ```typescript
305
- return {
306
- code: 204,
307
- };
308
- ```
559
+ ## API Reference
309
560
 
310
- ## OpenAPI Generation
561
+ ### Server
311
562
 
312
- Transit-kit can automatically generate OpenAPI documentation from your endpoint definitions.
563
+ #### `createServer(config: ServerConfig): Server`
313
564
 
314
- ### Using the CLI
565
+ Creates a new Transit-Kit server instance.
315
566
 
316
- ```bash
317
- npx transit-kit generate-openapi --output openapi.json --target ./src
318
- ```
567
+ **Config Options:**
568
+ - `port: number` — Port to listen on
569
+ - `inDevMode: boolean` — Enable development mode (detailed logging)
570
+ - `logger: Logger | boolean` — Custom logger or true/false for console/no logging
319
571
 
320
- Options:
572
+ **Returns:** Server instance with:
573
+ - `expressApp: Application` — Underlying Express app
574
+ - `registerApiEndpoint(endpoint)` — Register an API endpoint
575
+ - `start()` — Start the server
576
+ - `endpointDefinitions` — Array of registered endpoint definitions
321
577
 
322
- - `-o, --output <path>`: Output path for the generated OpenAPI document (default: `openapi.json`)
323
- - `-t, --target <path>`: Target path to search for endpoint definitions (default: `.`)
578
+ ### Endpoint Creation
324
579
 
325
- ### Programmatic Usage
580
+ #### `createApiEndpointHandler(definition, handler)`
326
581
 
327
- ```typescript
328
- import { generateOpenApiDoc } from "transit-kit/cli";
582
+ Creates a type-safe API endpoint.
329
583
 
330
- const openApiDoc = await generateOpenApiDoc("./src");
331
- ```
584
+ **Parameters:**
585
+ - `definition` — Endpoint definition object
586
+ - `handler` — Async function handling the request
332
587
 
333
- The generated OpenAPI document will include:
588
+ **Handler receives:**
589
+ - `request` — Express Request object
590
+ - `response` — Express Response object
591
+ - `parameters` — Type-safe path parameters
592
+ - `query` — Type-safe query parameters
593
+ - `body` — Type-safe request body
594
+ - `caller` — Authenticated user (if auth is enabled)
334
595
 
335
- - All registered endpoints
336
- - Request/response schemas
337
- - Path parameters
338
- - Query parameters
339
- - Request body schemas
340
- - Response schemas for all status codes
596
+ ### Authentication
341
597
 
342
- ## Configuration
598
+ #### `createBasicAuthSchema<Caller>(name, validateCaller)`
343
599
 
344
- ### Server Configuration
600
+ Creates a Basic authentication scheme.
345
601
 
346
- ```typescript
347
- interface ServerConfig {
348
- inDevMode: boolean; // Enable development mode features
349
- port: number; // Port to listen on
350
- logger: Logger | boolean; // Logger instance or boolean to enable/disable
351
- }
352
- ```
602
+ **Parameters:**
603
+ - `name: string` — Unique name for the scheme
604
+ - `validateCaller: (username, password) => Promise<Caller | null>` — Validation function
353
605
 
354
- ### Custom Logger
606
+ #### `createBearerAuthSchema<Caller>(name, validateCaller)`
355
607
 
356
- You can provide a custom logger with a console-like interface:
608
+ Creates a Bearer token authentication scheme.
357
609
 
358
- ```typescript
359
- const customLogger = {
360
- log: (message: string) => {
361
- /* custom logging */
362
- },
363
- error: (message: string) => {
364
- /* custom error logging */
365
- },
366
- // ... other console methods
367
- };
610
+ **Parameters:**
611
+ - `name: string` — Unique name for the scheme
612
+ - `validateCaller: (token) => Promise<Caller | null>` — Validation function
368
613
 
369
- const server = createServer({
370
- port: 3000,
371
- inDevMode: false,
372
- logger: customLogger,
373
- });
374
- ```
614
+ ### OpenAPI Generation
375
615
 
376
- ## API Reference
616
+ #### `generateOpenApiDoc(server, options): Promise<OpenAPIV3.Document>`
377
617
 
378
- ### `createServer(config: ServerConfig): Server`
618
+ Generates OpenAPI 3.0 documentation.
379
619
 
380
- Creates a new server instance with the specified configuration.
620
+ **Parameters:**
621
+ - `server: Server` — Your Transit-Kit server instance
622
+ - `options: GeneratorOptions` — OpenAPI metadata
381
623
 
382
- ### `createApiEndpointHandler(definition, handler)`
624
+ **Options:**
625
+ - `title: string` — API title
626
+ - `version: string` — API version
627
+ - `description?: string` — API description
628
+ - `servers?: ServerObject[]` — Server URLs
629
+ - `contact?: ContactObject` — Contact information
630
+ - `license?: LicenseObject` — License information
383
631
 
384
- Creates an API endpoint handler with type-safe request/response handling.
632
+ ## Response Types
385
633
 
386
- **Definition properties:**
634
+ Transit-Kit currently supports JSON responses:
387
635
 
388
- - `meta`: Metadata about the endpoint (name, description, group)
389
- - `method`: HTTP method (`get`, `post`, `put`, `patch`, `delete`)
390
- - `path`: Express-style path with optional parameters (e.g., `/users/:userId`)
391
- - `requestBodySchema`: (Optional) Zod schema for request body validation
392
- - `querySchema`: (Optional) Zod schema for query parameter validation
393
- - `responseSchemas`: Map of status codes to response schemas
636
+ ```typescript
637
+ {
638
+ type: "json",
639
+ schema: z.object({ /* your schema */ }),
640
+ headers?: ["X-Custom-Header"], // Optional custom headers
641
+ }
642
+ ```
394
643
 
395
- ### `server.registerApiEndpoint(endpoint)`
644
+ When using custom headers in your response schema, you must include them in the handler response:
396
645
 
397
- Registers an endpoint with the server.
646
+ ```typescript
647
+ return {
648
+ code: 200,
649
+ data: { /* ... */ },
650
+ headers: {
651
+ "X-Custom-Header": "value",
652
+ },
653
+ };
654
+ ```
398
655
 
399
- ### `server.start()`
656
+ ## Accessing Express Features
400
657
 
401
- Starts the Express server on the configured port.
658
+ Since Transit-Kit is built on Express, you can access the underlying Express app:
402
659
 
403
- ## Examples
660
+ ```typescript
661
+ const server = createServer({ /* ... */ });
404
662
 
405
- ### Complete Example
663
+ // Add custom middleware
664
+ server.expressApp.use(cors());
665
+ server.expressApp.use(helmet());
406
666
 
407
- ```typescript
408
- import { createServer, createApiEndpointHandler } from "transit-kit/server";
409
- import z from "zod";
667
+ // Static files
668
+ server.expressApp.use(express.static("public"));
410
669
 
411
- // Create server
412
- const server = createServer({
413
- port: 3000,
414
- inDevMode: true,
415
- logger: true,
670
+ // Custom routes
671
+ server.expressApp.get("/health", (req, res) => {
672
+ res.json({ status: "ok" });
416
673
  });
674
+ ```
417
675
 
418
- // Define endpoints
419
- const listUsersEndpoint = createApiEndpointHandler(
420
- {
421
- meta: {
422
- name: "List Users",
423
- description: "Get a list of all users",
424
- group: "Users",
425
- },
426
- method: "get",
427
- path: "/users",
428
- querySchema: z.object({
429
- page: z.number().optional(),
430
- limit: z.number().optional(),
431
- }),
432
- responseSchemas: {
433
- 200: {
434
- dataType: "application/json",
435
- dataSchema: z.array(
436
- z.object({
437
- id: z.string(),
438
- name: z.string(),
439
- }),
440
- ),
441
- },
442
- },
443
- },
444
- async (request) => {
445
- const { page = 1, limit = 10 } = request.query;
446
- const users = await fetchUsers(page, limit);
676
+ ## Project Structure
447
677
 
448
- return {
449
- code: 200,
450
- dataType: "application/json",
451
- json: users,
452
- };
453
- },
454
- );
678
+ Transit-Kit exports two main modules:
455
679
 
456
- server.registerApiEndpoint(listUsersEndpoint);
457
- server.start();
680
+ - **`transit-kit/server`** — Server creation, endpoint handlers, and authentication
681
+ - **`transit-kit/generator`** — OpenAPI documentation generation
682
+
683
+ ## TypeScript Configuration
684
+
685
+ For the best experience, ensure your `tsconfig.json` includes:
686
+
687
+ ```json
688
+ {
689
+ "compilerOptions": {
690
+ "strict": true,
691
+ "esModuleInterop": true,
692
+ "moduleResolution": "node",
693
+ "target": "ES2022",
694
+ "module": "ES2022"
695
+ }
696
+ }
458
697
  ```
459
698
 
460
699
  ## License
461
700
 
462
- MIT
701
+ MIT ÂĐ [D4rkr34lm](https://github.com/D4rkr34lm)
463
702
 
464
703
  ## Contributing
465
704
 
466
705
  Contributions are welcome! Please feel free to submit a Pull Request.
467
706
 
468
- ## Author
707
+ ## Links
469
708
 
470
- D4rkr34lm
709
+ - [GitHub Repository](https://github.com/D4rkr34lm/transit-kit)
710
+ - [npm Package](https://www.npmjs.com/package/transit-kit)
711
+ - [Report Issues](https://github.com/D4rkr34lm/transit-kit/issues)
@@ -17,7 +17,10 @@ function extractPathAndParameters(path) {
17
17
  return { openApiPath, parameters };
18
18
  }
19
19
  function extractQueryParameters(querySchema) {
20
- const querySchemaObject = z.toJSONSchema(querySchema, { io: "input" });
20
+ const querySchemaObject = z.toJSONSchema(querySchema, {
21
+ io: "input",
22
+ target: "openapi-3.0",
23
+ });
21
24
  if (querySchemaObject.properties) {
22
25
  return Object.entries(querySchemaObject.properties).map(([name, schema]) => ({
23
26
  name: name,
@@ -65,6 +68,7 @@ function translateToOpenAPIPathItem(definition) {
65
68
  "application/json": {
66
69
  schema: z.toJSONSchema(requestBodySchema, {
67
70
  io: "input",
71
+ target: "openapi-3.0",
68
72
  }), // Type assertion
69
73
  },
70
74
  },
@@ -76,7 +80,10 @@ function translateToOpenAPIPathItem(definition) {
76
80
  .map(([statusCode, responseDef]) => {
77
81
  if (isJsonResponseSchema(responseDef)) {
78
82
  const zodSchema = responseDef.schema;
79
- const responseSchema = z.toJSONSchema(zodSchema, { io: "input" });
83
+ const responseSchema = z.toJSONSchema(zodSchema, {
84
+ io: "input",
85
+ target: "openapi-3.0",
86
+ });
80
87
  return {
81
88
  [statusCode]: {
82
89
  description: `Response for status code ${statusCode}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "transit-kit",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "A declarative TypeScript framework for building type-safe Express.js APIs with automatic OpenAPI generation",
5
5
  "keywords": [
6
6
  "express",