ts-typed-api 0.2.12 → 0.2.14

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/dist/handler.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { ApiDefinitionSchema } from "./definition";
2
2
  import { createRouteHandler } from "./router";
3
+ import { MiddlewareResponse } from "./object-handlers";
3
4
  import express from "express";
4
5
  export type SpecificRouteHandler<TDef extends ApiDefinitionSchema> = {
5
6
  [TDomain_ in keyof TDef['endpoints']]: {
6
7
  [TRouteKey_ in keyof TDef['endpoints'][TDomain_]]: ReturnType<typeof createRouteHandler<TDef, TDomain_, TRouteKey_>>;
7
8
  }[keyof TDef['endpoints'][TDomain_]];
8
9
  }[keyof TDef['endpoints']];
9
- export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (req: express.Request, res: express.Response, next: express.NextFunction, endpointInfo: {
10
+ export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (req: express.Request, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: {
10
11
  [TDomain in keyof TDef['endpoints']]: {
11
12
  domain: TDomain;
12
13
  routeKey: keyof TDef['endpoints'][TDomain];
package/dist/handler.js CHANGED
@@ -55,6 +55,50 @@ function preprocessQueryParams(query, querySchema) {
55
55
  }
56
56
  return processedQuery;
57
57
  }
58
+ // Helper function to create respond method for middleware compatibility
59
+ function createRespondFunction(routeDefinition, responseSetter) {
60
+ return (status, data) => {
61
+ const responseSchema = routeDefinition.responses[status];
62
+ if (!responseSchema) {
63
+ console.error(`No response schema defined for status ${status}`);
64
+ responseSetter(500, {
65
+ data: null,
66
+ error: [{ field: "general", type: "general", message: "Internal server error: Undefined response schema for status." }]
67
+ });
68
+ return;
69
+ }
70
+ let responseBody;
71
+ if (status === 422) {
72
+ responseBody = {
73
+ data: null,
74
+ error: data
75
+ };
76
+ }
77
+ else {
78
+ responseBody = {
79
+ data: data,
80
+ error: null
81
+ };
82
+ }
83
+ const validationResult = responseSchema.safeParse(responseBody);
84
+ if (validationResult.success) {
85
+ // Handle 204 responses specially - they must not have a body
86
+ if (status === 204) {
87
+ responseSetter(status, null);
88
+ }
89
+ else {
90
+ responseSetter(status, validationResult.data);
91
+ }
92
+ }
93
+ else {
94
+ console.error(`FATAL: Constructed response body failed Zod validation for status ${status}.`, validationResult.error.issues, 'Provided data:', data, 'Constructed response body:', responseBody);
95
+ responseSetter(500, {
96
+ data: null,
97
+ error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
98
+ });
99
+ }
100
+ };
101
+ }
58
102
  // Helper function to create multer middleware based on file upload configuration
59
103
  function createFileUploadMiddleware(config) {
60
104
  // Default multer configuration
@@ -367,7 +411,12 @@ middlewares) {
367
411
  middlewares.forEach(middleware => {
368
412
  const wrappedMiddleware = async (req, res, next) => {
369
413
  try {
370
- await middleware(req, res, next, { domain: currentDomain, routeKey: currentRouteKey });
414
+ // Add respond method to res for middleware compatibility
415
+ const middlewareRes = res;
416
+ middlewareRes.respond = createRespondFunction(routeDefinition, (status, data) => {
417
+ res.status(status).json(data);
418
+ });
419
+ await middleware(req, middlewareRes, next, { domain: currentDomain, routeKey: currentRouteKey });
371
420
  }
372
421
  catch (error) {
373
422
  next(error);
@@ -415,10 +415,70 @@ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middleware
415
415
  path: c.req.path,
416
416
  originalUrl: c.req.url
417
417
  };
418
- // Create minimal res object (middleware shouldn't use it)
419
- const fakeRes = {};
418
+ // Create minimal res object with respond method for middleware compatibility
419
+ const fakeRes = {
420
+ respond: (status, data) => {
421
+ const responseSchema = routeDefinition.responses[status];
422
+ if (!responseSchema) {
423
+ console.error(`No response schema defined for status ${status}`);
424
+ c.json({
425
+ data: null,
426
+ error: [{ field: "general", type: "general", message: "Internal server error: Undefined response schema for status." }]
427
+ }, 500);
428
+ return;
429
+ }
430
+ let responseBody;
431
+ if (status === 422) {
432
+ responseBody = {
433
+ data: null,
434
+ error: data
435
+ };
436
+ }
437
+ else {
438
+ responseBody = {
439
+ data: data,
440
+ error: null
441
+ };
442
+ }
443
+ const validationResult = responseSchema.safeParse(responseBody);
444
+ if (validationResult.success) {
445
+ // Handle 204 responses specially - they must not have a body
446
+ if (status === 204) {
447
+ c.__response = new Response(null, { status: status });
448
+ }
449
+ else {
450
+ c.__response = c.json(validationResult.data, status);
451
+ }
452
+ }
453
+ else {
454
+ console.error(`FATAL: Constructed response body failed Zod validation for status ${status}.`, validationResult.error.issues, 'Provided data:', data, 'Constructed response body:', responseBody);
455
+ c.__response = c.json({
456
+ data: null,
457
+ error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
458
+ }, 500);
459
+ }
460
+ },
461
+ status: (status) => {
462
+ // For middleware that might use status, but since not used, stub
463
+ c.__response = new Response(null, { status: status });
464
+ },
465
+ json: (data) => {
466
+ // Send json response
467
+ c.__response = c.json(data);
468
+ },
469
+ setHeader: (name, value) => {
470
+ c.header(name, value);
471
+ },
472
+ end: () => {
473
+ // Perhaps do nothing or set response
474
+ }
475
+ };
420
476
  // Call Express-style middleware
421
477
  await middleware(fakeReq, fakeRes, next, { domain: currentDomain, routeKey: currentRouteKey });
478
+ // Check if middleware responded directly
479
+ if (c.__response) {
480
+ return c.__response;
481
+ }
422
482
  }
423
483
  catch (error) {
424
484
  console.error('Middleware error:', error);
@@ -7,15 +7,22 @@ export type EndpointInfo<TDef extends ApiDefinitionSchema = ApiDefinitionSchema>
7
7
  routeKey: keyof TDef['endpoints'][TDomain];
8
8
  };
9
9
  }[keyof TDef['endpoints']];
10
- export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (req: express.Request, res: express.Response, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>;
11
- export type SimpleMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction) => void | Promise<void>;
12
- export type UniversalEndpointMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction, endpointInfo: {
10
+ export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (req: express.Request, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>;
11
+ export type SimpleMiddleware = (req: express.Request, res: MiddlewareResponse, next: express.NextFunction) => void | Promise<void>;
12
+ export type UniversalEndpointMiddleware = (req: express.Request, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: {
13
13
  domain: string;
14
14
  routeKey: string;
15
15
  }) => void | Promise<void>;
16
+ export interface MiddlewareResponse {
17
+ respond(status: number, data: any): void;
18
+ status(code: number): this;
19
+ json(data: any): void;
20
+ setHeader(name: string, value: string): void;
21
+ end(): void;
22
+ }
16
23
  export type EndpointMiddlewareCtx<Ctx extends Record<string, any> = Record<string, any>, TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = ((req: express.Request & {
17
24
  ctx?: Ctx;
18
- }, res: express.Response, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>) | ((c: any, next: any) => void | Promise<void>);
25
+ }, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>) | ((c: any, next: any) => void | Promise<void>);
19
26
  export type AnyMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = EndpointMiddleware<TDef> | UniversalEndpointMiddleware | SimpleMiddleware;
20
27
  type HandlerFunction<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain], Ctx extends Record<string, any> = Record<string, any>> = (req: TypedRequest<TDef, TDomain, TRouteKey, ApiParams<TDef, TDomain, TRouteKey>, ApiBody<TDef, TDomain, TRouteKey>, ApiQuery<TDef, TDomain, TRouteKey>, Record<string, any>, Ctx>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void;
21
28
  export type ObjectHandlers<TDef extends ApiDefinitionSchema, Ctx extends Record<string, any> = Record<string, any>> = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-typed-api",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "A lightweight, type-safe RPC library for TypeScript with Zod validation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/handler.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { ApiDefinitionSchema, RouteSchema, UnifiedError, FileUploadConfig } from "./definition";
3
3
  import { createRouteHandler, TypedRequest, TypedResponse } from "./router";
4
+ import { MiddlewareResponse } from "./object-handlers";
4
5
  import express from "express";
5
6
  import multer from "multer";
6
7
 
@@ -16,7 +17,7 @@ export type SpecificRouteHandler<TDef extends ApiDefinitionSchema> = {
16
17
  // Type for middleware function that receives endpoint information with type safety
17
18
  export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (
18
19
  req: express.Request,
19
- res: express.Response,
20
+ res: MiddlewareResponse,
20
21
  next: express.NextFunction,
21
22
  endpointInfo: {
22
23
  [TDomain in keyof TDef['endpoints']]: {
@@ -79,6 +80,61 @@ function preprocessQueryParams(query: any, querySchema?: z.ZodTypeAny): any {
79
80
  return processedQuery;
80
81
  }
81
82
 
83
+ // Helper function to create respond method for middleware compatibility
84
+ function createRespondFunction(
85
+ routeDefinition: RouteSchema,
86
+ responseSetter: (status: number, data: any) => void
87
+ ) {
88
+ return (status: number, data: any) => {
89
+ const responseSchema = routeDefinition.responses[status];
90
+
91
+ if (!responseSchema) {
92
+ console.error(`No response schema defined for status ${status}`);
93
+ responseSetter(500, {
94
+ data: null,
95
+ error: [{ field: "general", type: "general", message: "Internal server error: Undefined response schema for status." }]
96
+ });
97
+ return;
98
+ }
99
+
100
+ let responseBody: any;
101
+
102
+ if (status === 422) {
103
+ responseBody = {
104
+ data: null,
105
+ error: data
106
+ };
107
+ } else {
108
+ responseBody = {
109
+ data: data,
110
+ error: null
111
+ };
112
+ }
113
+
114
+ const validationResult = responseSchema.safeParse(responseBody);
115
+
116
+ if (validationResult.success) {
117
+ // Handle 204 responses specially - they must not have a body
118
+ if (status === 204) {
119
+ responseSetter(status, null);
120
+ } else {
121
+ responseSetter(status, validationResult.data);
122
+ }
123
+ } else {
124
+ console.error(
125
+ `FATAL: Constructed response body failed Zod validation for status ${status}.`,
126
+ validationResult.error.issues,
127
+ 'Provided data:', data,
128
+ 'Constructed response body:', responseBody
129
+ );
130
+ responseSetter(500, {
131
+ data: null,
132
+ error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
133
+ });
134
+ }
135
+ };
136
+ }
137
+
82
138
  // Helper function to create multer middleware based on file upload configuration
83
139
  function createFileUploadMiddleware(config: FileUploadConfig): express.RequestHandler {
84
140
  // Default multer configuration
@@ -418,7 +474,12 @@ export function registerRouteHandlers<TDef extends ApiDefinitionSchema>(
418
474
  middlewares.forEach(middleware => {
419
475
  const wrappedMiddleware: express.RequestHandler = async (req, res, next) => {
420
476
  try {
421
- await middleware(req, res, next, { domain: currentDomain, routeKey: currentRouteKey } as any);
477
+ // Add respond method to res for middleware compatibility
478
+ const middlewareRes = res as any;
479
+ middlewareRes.respond = createRespondFunction(routeDefinition, (status, data) => {
480
+ res.status(status).json(data);
481
+ });
482
+ await middleware(req, middlewareRes as MiddlewareResponse, next, { domain: currentDomain, routeKey: currentRouteKey } as any);
422
483
  } catch (error) {
423
484
  next(error);
424
485
  }
@@ -497,12 +497,80 @@ export function registerHonoRouteHandlers<
497
497
  originalUrl: c.req.url
498
498
  };
499
499
 
500
- // Create minimal res object (middleware shouldn't use it)
501
- const fakeRes = {};
500
+ // Create minimal res object with respond method for middleware compatibility
501
+ const fakeRes = {
502
+ respond: (status: number, data: any) => {
503
+ const responseSchema = routeDefinition.responses[status];
504
+
505
+ if (!responseSchema) {
506
+ console.error(`No response schema defined for status ${status}`);
507
+ c.json({
508
+ data: null,
509
+ error: [{ field: "general", type: "general", message: "Internal server error: Undefined response schema for status." }]
510
+ }, 500);
511
+ return;
512
+ }
513
+
514
+ let responseBody: any;
515
+
516
+ if (status === 422) {
517
+ responseBody = {
518
+ data: null,
519
+ error: data
520
+ };
521
+ } else {
522
+ responseBody = {
523
+ data: data,
524
+ error: null
525
+ };
526
+ }
527
+
528
+ const validationResult = responseSchema.safeParse(responseBody);
529
+
530
+ if (validationResult.success) {
531
+ // Handle 204 responses specially - they must not have a body
532
+ if (status === 204) {
533
+ (c as any).__response = new Response(null, { status: status as any });
534
+ } else {
535
+ (c as any).__response = c.json(validationResult.data, status as any);
536
+ }
537
+ } else {
538
+ console.error(
539
+ `FATAL: Constructed response body failed Zod validation for status ${status}.`,
540
+ validationResult.error.issues,
541
+ 'Provided data:', data,
542
+ 'Constructed response body:', responseBody
543
+ );
544
+ (c as any).__response = c.json({
545
+ data: null,
546
+ error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
547
+ }, 500);
548
+ }
549
+ },
550
+ status: (status: number) => {
551
+ // For middleware that might use status, but since not used, stub
552
+ (c as any).__response = new Response(null, { status: status });
553
+ },
554
+ json: (data: any) => {
555
+ // Send json response
556
+ (c as any).__response = c.json(data);
557
+ },
558
+ setHeader: (name: string, value: string) => {
559
+ c.header(name, value);
560
+ },
561
+ end: () => {
562
+ // Perhaps do nothing or set response
563
+ }
564
+ };
502
565
 
503
566
  // Call Express-style middleware
504
567
  await middleware(fakeReq as any, fakeRes as any, next, { domain: currentDomain, routeKey: currentRouteKey });
505
568
 
569
+ // Check if middleware responded directly
570
+ if ((c as any).__response) {
571
+ return (c as any).__response;
572
+ }
573
+
506
574
  } catch (error) {
507
575
  console.error('Middleware error:', error);
508
576
  await next();
@@ -13,7 +13,7 @@ export type EndpointInfo<TDef extends ApiDefinitionSchema = ApiDefinitionSchema>
13
13
  // Type for middleware function that receives endpoint information
14
14
  export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = (
15
15
  req: express.Request,
16
- res: express.Response,
16
+ res: MiddlewareResponse,
17
17
  next: express.NextFunction,
18
18
  endpointInfo: EndpointInfo<TDef>
19
19
  ) => void | Promise<void>;
@@ -21,14 +21,14 @@ export type EndpointMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionS
21
21
  // Type for simple middleware that doesn't need endpoint information
22
22
  export type SimpleMiddleware = (
23
23
  req: express.Request,
24
- res: express.Response,
24
+ res: MiddlewareResponse,
25
25
  next: express.NextFunction
26
26
  ) => void | Promise<void>;
27
27
 
28
28
  // Type for middleware that can work with any API definition
29
29
  export type UniversalEndpointMiddleware = (
30
30
  req: express.Request,
31
- res: express.Response,
31
+ res: MiddlewareResponse,
32
32
  next: express.NextFunction,
33
33
  endpointInfo: {
34
34
  domain: string;
@@ -36,13 +36,22 @@ export type UniversalEndpointMiddleware = (
36
36
  }
37
37
  ) => void | Promise<void>;
38
38
 
39
+ // Custom response interface for framework-agnostic middleware
40
+ export interface MiddlewareResponse {
41
+ respond(status: number, data: any): void;
42
+ status(code: number): this;
43
+ json(data: any): void;
44
+ setHeader(name: string, value: string): void;
45
+ end(): void;
46
+ }
47
+
39
48
  // Unified middleware type that works for both Express and Hono with context typing
40
49
  export type EndpointMiddlewareCtx<
41
50
  Ctx extends Record<string, any> = Record<string, any>,
42
51
  TDef extends ApiDefinitionSchema = ApiDefinitionSchema
43
52
  > =
44
53
  // Express version: (req, res, next, endpointInfo)
45
- ((req: express.Request & { ctx?: Ctx }, res: express.Response, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>) |
54
+ ((req: express.Request & { ctx?: Ctx }, res: MiddlewareResponse, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>) |
46
55
  // Hono version: (c, next) - context is passed via c.req.ctx -> c.ctx copying
47
56
  ((c: any, next: any) => void | Promise<void>);
48
57
 
@@ -58,6 +58,9 @@ describe.each([
58
58
  401: ({ data }) => {
59
59
  throw new Error(`Authentication failed: ${data.error}`);
60
60
  },
61
+ 403: ({ data }) => {
62
+ throw new Error(`Forbidden: ${data.error}`);
63
+ },
61
64
  422: ({ error }) => {
62
65
  throw new Error(`Validation error: ${JSON.stringify(error)}`);
63
66
  }
@@ -74,11 +77,32 @@ describe.each([
74
77
  expect(data.error).toBe('No authorization header');
75
78
  throw new Error('Authentication failed as expected');
76
79
  },
80
+ 403: ({ data }) => {
81
+ throw new Error(`Unexpected forbidden: ${data.error}`);
82
+ },
77
83
  422: ({ error }) => {
78
84
  throw new Error(`Validation error: ${JSON.stringify(error)}`);
79
85
  }
80
86
  })
81
87
  ).rejects.toThrow('Authentication failed as expected');
82
88
  });
89
+
90
+ test('should deny access with invalid auth header', async () => {
91
+ await expect(
92
+ client.callApi('public', 'protected', { headers: { Authorization: 'Bearer invalid-token' } }, {
93
+ 200: ({ data }) => data,
94
+ 401: ({ data }) => {
95
+ throw new Error(`Unexpected auth failed: ${data.error}`);
96
+ },
97
+ 403: ({ data }) => {
98
+ expect(data.error).toBe('Forbidden');
99
+ throw new Error('Forbidden as expected');
100
+ },
101
+ 422: ({ error }) => {
102
+ throw new Error(`Validation error: ${JSON.stringify(error)}`);
103
+ }
104
+ })
105
+ ).rejects.toThrow('Forbidden as expected');
106
+ });
83
107
  });
84
108
  });
package/tests/setup.ts CHANGED
@@ -219,7 +219,8 @@ export const MiddlewareTestApiDefinition = CreateApiDefinition({
219
219
  path: '/protected',
220
220
  responses: CreateResponses({
221
221
  200: z.object({ message: z.string(), user: z.string() }),
222
- 401: z.object({ error: z.string() })
222
+ 401: z.object({ error: z.string() }),
223
+ 403: z.object({ error: z.string() })
223
224
  })
224
225
  },
225
226
  context: {
@@ -444,7 +445,74 @@ async function startHonoServer(): Promise<void> {
444
445
  });
445
446
  }
446
447
 
447
- type Ctx = { user: string }
448
+ type Ctx = { user?: string; noAuth?: boolean; forbidden?: boolean, middlewareData?: string }
449
+
450
+ // Shared handlers for middleware tests
451
+ const middlewareTestHandlers = {
452
+ public: {
453
+ ping: async (req: any, res: any) => {
454
+ res.respond(200, { message: "pong" });
455
+ },
456
+ protected: async (req: any, res: any) => {
457
+ // Middleware has already validated auth, so we only handle success case
458
+ res.respond(200, {
459
+ message: "protected content",
460
+ user: req.ctx?.user || "unknown"
461
+ });
462
+ },
463
+ context: async (req: any, res: any) => {
464
+ res.respond(200, {
465
+ message: "context test",
466
+ contextData: req.ctx?.middlewareData || "default"
467
+ });
468
+ }
469
+ }
470
+ };
471
+
472
+ // Generic middleware setup function
473
+ function setupMiddlewareApp(app: any, isHono: boolean) {
474
+ // Define middleware functions
475
+ const loggingMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next, endpointInfo) => {
476
+ console.log(`[Test] ${req.method} ${req.path} - Domain: ${endpointInfo.domain}, Route: ${endpointInfo.routeKey}`);
477
+ await next();
478
+ }
479
+
480
+ const contextMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next) => {
481
+ req.ctx = { ...req.ctx, middlewareData: "middleware-added-data" };
482
+ await next();
483
+ }
484
+
485
+ const authMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next, endpointInfo) => {
486
+ // Only apply auth checks to protected routes
487
+ if (endpointInfo.domain === 'public' && endpointInfo.routeKey === 'protected') {
488
+ const authHeader = req.headers?.authorization;
489
+ if (!authHeader) {
490
+ res.respond(401, { error: "No authorization header" });
491
+ } else if (authHeader === 'Bearer valid-token') {
492
+ req.ctx = { ...req.ctx, user: 'testuser' };
493
+ await next();
494
+ } else {
495
+ res.respond(403, { error: "Forbidden" });
496
+ }
497
+ } else {
498
+ await next();
499
+ }
500
+ }
501
+
502
+ const middlewares = [
503
+ loggingMiddleware,
504
+ contextMiddleware,
505
+ authMiddleware
506
+ ]
507
+
508
+ // Register handlers with middleware
509
+ if (isHono) {
510
+ const hndl = CreateTypedHonoHandlerWithContext<Ctx>();
511
+ hndl(app, MiddlewareTestApiDefinition, middlewareTestHandlers, middlewares);
512
+ } else {
513
+ RegisterHandlers(app, MiddlewareTestApiDefinition, middlewareTestHandlers, middlewares);
514
+ }
515
+ }
448
516
 
449
517
  // Middleware test servers
450
518
  async function startMiddlewareExpressServer(): Promise<void> {
@@ -452,54 +520,7 @@ async function startMiddlewareExpressServer(): Promise<void> {
452
520
  const app = express();
453
521
  app.use(express.json());
454
522
 
455
- // Define middleware functions
456
- const loggingMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction, endpointInfo: any) => {
457
- console.log(`[Test] ${req.method} ${req.path} - Domain: ${endpointInfo.domain}, Route: ${endpointInfo.routeKey}`);
458
- next();
459
- };
460
-
461
- const contextMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction) => {
462
- (req as any).ctx = { middlewareData: "middleware-added-data" };
463
- next();
464
- };
465
-
466
- const authMiddleware: EndpointMiddlewareCtx<Ctx> = (req, res, next) => {
467
- const authHeader = req.headers.authorization;
468
- if (authHeader === 'Bearer valid-token') {
469
- req.ctx = { user: 'testuser' };
470
- }
471
- next();
472
- };
473
-
474
- // Register handlers with middleware
475
- RegisterHandlers(app, MiddlewareTestApiDefinition, {
476
- public: {
477
- ping: async (req: any, res: any) => {
478
- res.respond(200, { message: "pong" });
479
- },
480
- protected: async (req, res) => {
481
- // Check if user is authenticated via context
482
- if (req.ctx && req.ctx.user) {
483
- res.respond(200, {
484
- message: "protected content",
485
- user: req.ctx.user
486
- });
487
- } else {
488
- res.respond(401, { error: "No authorization header" });
489
- }
490
- },
491
- context: async (req: any, res: any) => {
492
- res.respond(200, {
493
- message: "context test",
494
- contextData: req.ctx?.middlewareData || "default"
495
- });
496
- }
497
- }
498
- }, [
499
- loggingMiddleware,
500
- contextMiddleware,
501
- authMiddleware
502
- ]);
523
+ setupMiddlewareApp(app, false);
503
524
 
504
525
  middlewareExpressServer = app.listen(MIDDLEWARE_EXPRESS_PORT, () => {
505
526
  resolve();
@@ -511,55 +532,7 @@ async function startMiddlewareHonoServer(): Promise<void> {
511
532
  return new Promise((resolve) => {
512
533
  const app = new Hono();
513
534
 
514
- // Define middleware functions
515
- const loggingMiddleware = async (req: any, res: any, next: any, endpointInfo: any) => {
516
- console.log(`[Test Hono] ${req.method} ${req.path} - Domain: ${endpointInfo.domain}, Route: ${endpointInfo.routeKey}`);
517
- await next();
518
- };
519
-
520
- const contextMiddleware = async (req: any, res: any, next: any) => {
521
- req.ctx = { ...req.ctx, middlewareData: "middleware-added-data" };
522
- await next();
523
- };
524
-
525
- const authMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next) => {
526
- const authHeader = req.headers?.authorization;
527
- if (authHeader === 'Bearer valid-token') {
528
- req.ctx = { ...req.ctx, user: 'testuser' };
529
- }
530
- await next();
531
- };
532
-
533
- const hdnl = CreateTypedHonoHandlerWithContext<Ctx>()
534
- // Register handlers with middleware
535
- hdnl(app, MiddlewareTestApiDefinition, {
536
- public: {
537
- ping: async (req: any, res: any) => {
538
- res.respond(200, { message: "pong" });
539
- },
540
- protected: async (req, res) => {
541
- // Check if user is authenticated via context
542
- if (req.ctx && req.ctx.user) {
543
- res.respond(200, {
544
- message: "protected content",
545
- user: req.ctx.user
546
- });
547
- } else {
548
- res.respond(401, { error: "No authorization header" });
549
- }
550
- },
551
- context: async (req: any, res: any) => {
552
- res.respond(200, {
553
- message: "context test",
554
- contextData: req.ctx?.middlewareData || "default"
555
- });
556
- }
557
- }
558
- }, [
559
- loggingMiddleware,
560
- contextMiddleware,
561
- authMiddleware
562
- ]);
535
+ setupMiddlewareApp(app, true);
563
536
 
564
537
  // Create HTTP server from Hono app
565
538
  const server = app.fetch;