ts-typed-api 0.2.1 → 0.2.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/dist/handler.js CHANGED
@@ -241,6 +241,7 @@ middlewares) {
241
241
  params: parsedParams,
242
242
  query: parsedQuery,
243
243
  body: parsedBody,
244
+ ctx: expressReq.ctx,
244
245
  headers: expressReq.headers,
245
246
  cookies: expressReq.cookies,
246
247
  ip: expressReq.ip,
@@ -1,4 +1,4 @@
1
- import { Hono, Context } from 'hono';
1
+ import { Hono, Context, Env } from 'hono';
2
2
  import { z } from 'zod';
3
3
  import { ApiDefinitionSchema } from './definition';
4
4
  import { TypedRequest, TypedResponse } from './router';
@@ -19,7 +19,7 @@ export declare const honoFileSchema: z.ZodObject<{
19
19
  stream: z.ZodOptional<z.ZodAny>;
20
20
  }, z.core.$strip>;
21
21
  export type HonoFileType = z.infer<typeof honoFileSchema>;
22
- export type HonoTypedContext<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain]> = Context & {
22
+ export type HonoTypedContext<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain], Ctx extends Record<string, any> = Record<string, any>> = Context & {
23
23
  params: TypedRequest<TDef, TDomain, TRouteKey>['params'];
24
24
  query: TypedRequest<TDef, TDomain, TRouteKey>['query'];
25
25
  body: TypedRequest<TDef, TDomain, TRouteKey>['body'];
@@ -27,7 +27,9 @@ export type HonoTypedContext<TDef extends ApiDefinitionSchema, TDomain extends k
27
27
  files?: HonoFile[] | {
28
28
  [fieldname: string]: HonoFile[];
29
29
  };
30
+ ctx?: Ctx;
30
31
  respond: TypedResponse<TDef, TDomain, TRouteKey>['respond'];
31
32
  };
32
- export declare function registerHonoRouteHandlers<TDef extends ApiDefinitionSchema>(app: Hono, apiDefinition: TDef, routeHandlers: Array<SpecificRouteHandler<TDef>>, middlewares?: EndpointMiddleware<TDef>[]): void;
33
- export declare function RegisterHonoHandlers<TDef extends ApiDefinitionSchema>(app: Hono, apiDefinition: TDef, objectHandlers: ObjectHandlers<TDef>, middlewares?: AnyMiddleware<TDef>[]): void;
33
+ export declare function registerHonoRouteHandlers<TDef extends ApiDefinitionSchema, TBindings extends Env = Env, TVariables extends Record<string, never> = Record<string, never>, TPath extends string = "/">(app: Hono<TBindings, TVariables, TPath>, apiDefinition: TDef, routeHandlers: Array<SpecificRouteHandler<TDef>>, middlewares?: EndpointMiddleware<TDef>[]): void;
34
+ export declare function RegisterHonoHandlers<TDef extends ApiDefinitionSchema, Ctx extends Record<string, any> = Record<string, any>, TBindings extends Env = Env, TVariables extends Record<string, never> = Record<string, never>, TPath extends string = "/">(app: Hono<TBindings, TVariables, TPath>, apiDefinition: TDef, objectHandlers: ObjectHandlers<TDef, Ctx>, middlewares?: AnyMiddleware<TDef>[]): void;
35
+ export declare function CreateTypedHonoHandlerWithContext<Ctx extends Record<string, any>>(): <TDef extends ApiDefinitionSchema>(app: Hono, apiDefinition: TDef, objectHandlers: ObjectHandlers<TDef, Ctx>, middlewares?: AnyMiddleware<TDef>[]) => void;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.honoFileSchema = void 0;
4
4
  exports.registerHonoRouteHandlers = registerHonoRouteHandlers;
5
5
  exports.RegisterHonoHandlers = RegisterHonoHandlers;
6
+ exports.CreateTypedHonoHandlerWithContext = CreateTypedHonoHandlerWithContext;
6
7
  const zod_1 = require("zod");
7
8
  // Hono-compatible file schema for Workers environment
8
9
  exports.honoFileSchema = zod_1.z.object({
@@ -268,6 +269,8 @@ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middleware
268
269
  c.params = parsedParams;
269
270
  c.query = parsedQuery;
270
271
  c.body = parsedBody;
272
+ // Get context from Hono's context system
273
+ c.ctx = c.get('ctx') || {};
271
274
  // Add respond method to context
272
275
  c.respond = (status, data) => {
273
276
  const responseSchema = routeDefinition.responses[status];
@@ -318,6 +321,7 @@ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middleware
318
321
  body: parsedBody,
319
322
  file: c.file,
320
323
  files: c.files,
324
+ ctx: c.ctx,
321
325
  headers: c.req.header(),
322
326
  ip: c.req.header('CF-Connecting-IP') || '127.0.0.1',
323
327
  method: c.req.method,
@@ -402,11 +406,23 @@ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middleware
402
406
  middlewares.forEach(middleware => {
403
407
  const wrappedMiddleware = async (c, next) => {
404
408
  try {
405
- await middleware(c.req, c.res, next, { domain: currentDomain, routeKey: currentRouteKey });
409
+ // Create Express-like req object for middleware compatibility
410
+ const fakeReq = {
411
+ headers: c.req.header(),
412
+ get ctx() { return c.get('ctx') || {}; },
413
+ set ctx(value) { c.set('ctx', value); },
414
+ method: c.req.method,
415
+ path: c.req.path,
416
+ originalUrl: c.req.url
417
+ };
418
+ // Create minimal res object (middleware shouldn't use it)
419
+ const fakeRes = {};
420
+ // Call Express-style middleware
421
+ await middleware(fakeReq, fakeRes, next, { domain: currentDomain, routeKey: currentRouteKey });
406
422
  }
407
423
  catch (error) {
408
424
  console.error('Middleware error:', error);
409
- return next();
425
+ await next();
410
426
  }
411
427
  };
412
428
  middlewareWrappers.push(wrappedMiddleware);
@@ -472,3 +488,8 @@ function RegisterHonoHandlers(app, apiDefinition, objectHandlers, middlewares) {
472
488
  }) || [];
473
489
  registerHonoRouteHandlers(app, apiDefinition, handlerArray, endpointMiddlewares);
474
490
  }
491
+ function CreateTypedHonoHandlerWithContext() {
492
+ return function (app, apiDefinition, objectHandlers, middlewares) {
493
+ return RegisterHonoHandlers(app, apiDefinition, objectHandlers, middlewares);
494
+ };
495
+ }
@@ -1,6 +1,6 @@
1
1
  export { CreateApiDefinition, CreateResponses } from './definition';
2
2
  export { z as ZodSchema } from 'zod';
3
3
  export { EndpointMiddleware } from './object-handlers';
4
- export { RegisterHonoHandlers } from './hono-cloudflare-workers';
4
+ export { RegisterHonoHandlers, CreateTypedHonoHandlerWithContext } from './hono-cloudflare-workers';
5
5
  export type { ApiDefinitionSchema, RouteSchema, UnifiedError, FileUploadConfig, HttpSuccessStatusCode, HttpClientErrorStatusCode, HttpServerErrorStatusCode, AllowedInputStatusCode, AllowedResponseStatusCode } from './definition';
6
6
  export type { HonoFile, HonoFileType, HonoTypedContext } from './hono-cloudflare-workers';
package/dist/hono-only.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RegisterHonoHandlers = exports.ZodSchema = exports.CreateResponses = exports.CreateApiDefinition = void 0;
3
+ exports.CreateTypedHonoHandlerWithContext = exports.RegisterHonoHandlers = exports.ZodSchema = exports.CreateResponses = exports.CreateApiDefinition = void 0;
4
4
  // Hono-only exports - for Cloudflare Workers and other Hono environments
5
5
  // Excludes Express dependencies like multer, busboy, etc.
6
6
  var definition_1 = require("./definition");
@@ -11,3 +11,4 @@ Object.defineProperty(exports, "ZodSchema", { enumerable: true, get: function ()
11
11
  // Hono adapter for Cloudflare Workers
12
12
  var hono_cloudflare_workers_1 = require("./hono-cloudflare-workers");
13
13
  Object.defineProperty(exports, "RegisterHonoHandlers", { enumerable: true, get: function () { return hono_cloudflare_workers_1.RegisterHonoHandlers; } });
14
+ Object.defineProperty(exports, "CreateTypedHonoHandlerWithContext", { enumerable: true, get: function () { return hono_cloudflare_workers_1.CreateTypedHonoHandlerWithContext; } });
package/dist/index.d.ts CHANGED
@@ -5,4 +5,4 @@ export { CreateApiDefinition, CreateResponses, ApiDefinitionSchema } from './def
5
5
  export { RegisterHandlers, EndpointMiddleware, UniversalEndpointMiddleware, SimpleMiddleware, EndpointInfo } from './object-handlers';
6
6
  export { File as UploadedFile } from './router';
7
7
  export { z as ZodSchema } from 'zod';
8
- export { RegisterHonoHandlers, registerHonoRouteHandlers, HonoFile, HonoFileType, honoFileSchema, HonoTypedContext } from './hono-cloudflare-workers';
8
+ export { RegisterHonoHandlers, registerHonoRouteHandlers, HonoFile, HonoFileType, honoFileSchema, HonoTypedContext, CreateTypedHonoHandlerWithContext } from './hono-cloudflare-workers';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.honoFileSchema = exports.registerHonoRouteHandlers = exports.RegisterHonoHandlers = exports.ZodSchema = exports.RegisterHandlers = exports.CreateResponses = exports.CreateApiDefinition = exports.generateOpenApiSpec2 = exports.generateOpenApiSpec = exports.FetchHttpClientAdapter = exports.ApiClient = void 0;
3
+ exports.CreateTypedHonoHandlerWithContext = exports.honoFileSchema = exports.registerHonoRouteHandlers = exports.RegisterHonoHandlers = exports.ZodSchema = exports.RegisterHandlers = exports.CreateResponses = exports.CreateApiDefinition = exports.generateOpenApiSpec2 = exports.generateOpenApiSpec = exports.FetchHttpClientAdapter = exports.ApiClient = void 0;
4
4
  var client_1 = require("./client");
5
5
  Object.defineProperty(exports, "ApiClient", { enumerable: true, get: function () { return client_1.ApiClient; } });
6
6
  Object.defineProperty(exports, "FetchHttpClientAdapter", { enumerable: true, get: function () { return client_1.FetchHttpClientAdapter; } });
@@ -20,3 +20,4 @@ var hono_cloudflare_workers_1 = require("./hono-cloudflare-workers");
20
20
  Object.defineProperty(exports, "RegisterHonoHandlers", { enumerable: true, get: function () { return hono_cloudflare_workers_1.RegisterHonoHandlers; } });
21
21
  Object.defineProperty(exports, "registerHonoRouteHandlers", { enumerable: true, get: function () { return hono_cloudflare_workers_1.registerHonoRouteHandlers; } });
22
22
  Object.defineProperty(exports, "honoFileSchema", { enumerable: true, get: function () { return hono_cloudflare_workers_1.honoFileSchema; } });
23
+ Object.defineProperty(exports, "CreateTypedHonoHandlerWithContext", { enumerable: true, get: function () { return hono_cloudflare_workers_1.CreateTypedHonoHandlerWithContext; } });
@@ -13,13 +13,16 @@ export type UniversalEndpointMiddleware = (req: express.Request, res: express.Re
13
13
  domain: string;
14
14
  routeKey: string;
15
15
  }) => void | Promise<void>;
16
+ export type EndpointMiddlewareCtx<Ctx extends Record<string, any> = Record<string, any>, TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = ((req: express.Request & {
17
+ ctx?: Ctx;
18
+ }, res: express.Response, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>) | ((c: any, next: any) => void | Promise<void>);
16
19
  export type AnyMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> = EndpointMiddleware<TDef> | UniversalEndpointMiddleware | SimpleMiddleware;
17
- type HandlerFunction<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain]> = (req: TypedRequest<TDef, TDomain, TRouteKey>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void;
18
- export type ObjectHandlers<TDef extends ApiDefinitionSchema> = {
20
+ 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, any, any, any, any, Ctx>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void;
21
+ export type ObjectHandlers<TDef extends ApiDefinitionSchema, Ctx extends Record<string, any> = Record<string, any>> = {
19
22
  [TDomain in keyof TDef['endpoints']]: {
20
- [TRouteKey in keyof TDef['endpoints'][TDomain]]: HandlerFunction<TDef, TDomain, TRouteKey>;
23
+ [TRouteKey in keyof TDef['endpoints'][TDomain]]: HandlerFunction<TDef, TDomain, TRouteKey, Ctx>;
21
24
  };
22
25
  };
23
- export declare function RegisterHandlers<TDef extends ApiDefinitionSchema>(app: express.Express, apiDefinition: TDef, objectHandlers: ObjectHandlers<TDef>, middlewares?: AnyMiddleware<TDef>[]): void;
26
+ export declare function RegisterHandlers<TDef extends ApiDefinitionSchema, Ctx extends Record<string, any> = Record<string, any>>(app: express.Express, apiDefinition: TDef, objectHandlers: ObjectHandlers<TDef, Ctx>, middlewares?: AnyMiddleware<TDef>[]): void;
24
27
  export declare function makeObjectHandlerRegistrar<TDef extends ApiDefinitionSchema>(apiDefinition: TDef): (app: express.Express, objectHandlers: ObjectHandlers<TDef>, middlewares?: EndpointMiddleware<TDef>[]) => void;
25
28
  export {};
package/dist/router.d.ts CHANGED
@@ -3,11 +3,12 @@ import { ApiDefinitionSchema, // Changed from ApiDefinition
3
3
  ApiBody, ApiParams, ApiQuery, InferDataFromUnifiedResponse } from './definition';
4
4
  export type File = Express.Multer.File;
5
5
  export type TypedRequest<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain], // Using direct keyof for simplicity here
6
- P extends ApiParams<TDef, TDomain, TRouteKey> = ApiParams<TDef, TDomain, TRouteKey>, ReqBody extends ApiBody<TDef, TDomain, TRouteKey> = ApiBody<TDef, TDomain, TRouteKey>, Q extends ApiQuery<TDef, TDomain, TRouteKey> = ApiQuery<TDef, TDomain, TRouteKey>, L extends Record<string, any> = Record<string, any>> = express.Request<P, any, ReqBody, Q, L> & {
6
+ P extends ApiParams<TDef, TDomain, TRouteKey> = ApiParams<TDef, TDomain, TRouteKey>, ReqBody extends ApiBody<TDef, TDomain, TRouteKey> = ApiBody<TDef, TDomain, TRouteKey>, Q extends ApiQuery<TDef, TDomain, TRouteKey> = ApiQuery<TDef, TDomain, TRouteKey>, L extends Record<string, any> = Record<string, any>, Ctx extends Record<string, any> = Record<string, any>> = express.Request<P, any, ReqBody, Q, L> & {
7
7
  file?: File;
8
8
  files?: File[] | {
9
9
  [fieldname: string]: File[];
10
10
  };
11
+ ctx?: Ctx;
11
12
  };
12
13
  type ResponseDataForStatus<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteName extends keyof TDef['endpoints'][TDomain], TStatus extends keyof TDef['endpoints'][TDomain][TRouteName]['responses'] & number> = InferDataFromUnifiedResponse<TDef['endpoints'][TDomain][TRouteName]['responses'][TStatus]>;
13
14
  type RespondFunction<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteName extends keyof TDef['endpoints'][TDomain]> = <TStatusLocal extends keyof TDef['endpoints'][TDomain][TRouteName]['responses'] & number>(status: TStatusLocal, data: ResponseDataForStatus<TDef, TDomain, TRouteName, TStatusLocal>) => void;
@@ -15,10 +16,11 @@ export interface TypedResponse<TDef extends ApiDefinitionSchema, TDomain extends
15
16
  respond: RespondFunction<TDef, TDomain, TRouteName>;
16
17
  json: <B = any>(body: B) => this;
17
18
  }
18
- export declare function createRouteHandler<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain]>(domain: TDomain, routeKey: TRouteKey, handler: (req: TypedRequest<TDef, TDomain, TRouteKey>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void): {
19
+ export declare function createRouteHandler<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain], // Using direct keyof for simplicity
20
+ Ctx extends Record<string, any> = Record<string, any>>(domain: TDomain, routeKey: TRouteKey, handler: (req: TypedRequest<TDef, TDomain, TRouteKey, any, any, any, any, Ctx>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void): {
19
21
  domain: TDomain;
20
22
  routeKey: TRouteKey;
21
- handler: (req: TypedRequest<TDef, TDomain, TRouteKey>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void;
23
+ handler: (req: TypedRequest<TDef, TDomain, TRouteKey, any, any, any, any, Ctx>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void;
22
24
  };
23
25
  export declare function makeRouteHandlerCreator<TDef extends ApiDefinitionSchema>(): <TDomain extends keyof TDef["endpoints"], TRouteKey extends keyof TDef["endpoints"][TDomain]>(domain: TDomain, routeKey: TRouteKey, handler: (req: TypedRequest<TDef, TDomain, TRouteKey>, res: TypedResponse<TDef, TDomain, TRouteKey>) => Promise<void> | void) => ReturnType<typeof createRouteHandler<TDef, TDomain, TRouteKey>>;
24
26
  export {};
package/dist/router.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createRouteHandler = createRouteHandler;
4
4
  exports.makeRouteHandlerCreator = makeRouteHandlerCreator;
5
- // Type-safe route handler creation function, now generic over TDef
5
+ // Type-safe route handler creation function, now generic over TDef and Ctx
6
6
  // This function is called within a context where TDef is known (e.g. specific handlers file)
7
7
  function createRouteHandler(domain, routeKey, handler) {
8
8
  // The returned object includes enough type information (domain, routeKey)
@@ -18,7 +18,7 @@ const loggingMiddlewareTyped: EndpointMiddleware<typeof PrivateApiDefinition> =
18
18
  };
19
19
 
20
20
  // Universal auth middleware that doesn't use endpointInfo
21
- const authMiddleware = async (req, res, next) => {
21
+ const authMiddleware: EndpointMiddleware = async (req, res, next) => {
22
22
  // Example auth logic
23
23
  const authHeader = req.headers.authorization;
24
24
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
@@ -1,7 +1,8 @@
1
1
  import { Hono } from 'hono';
2
2
  import http from 'http';
3
- import { PublicApiDefinition as SimplePublicApiDefinition, PrivateApiDefinition as SimplePrivateApiDefinition } from './examples/simple/definitions';
4
- import { RegisterHonoHandlers } from './src';
3
+ import { PublicApiDefinition as SimplePublicApiDefinition, PrivateApiDefinition as SimplePrivateApiDefinition } from './simple/definitions';
4
+ import { CreateTypedHonoHandlerWithContext, RegisterHonoHandlers } from '../src';
5
+ import { EndpointMiddlewareCtx } from '../src/object-handlers';
5
6
 
6
7
  const HONO_PORT = 3004;
7
8
 
@@ -12,11 +13,18 @@ async function startHonoServer() {
12
13
 
13
14
  console.log('Registering handlers...');
14
15
 
16
+ type Ctx = { foo: string, blah: () => string }
17
+
18
+ const loggingMiddleware: EndpointMiddlewareCtx<Ctx> = (req, res, next, endpointInfo) => {
19
+ console.log(`[${new Date().toISOString()}] ${req.method} ${req.path} - Endpoint: ${endpointInfo.domain}.${endpointInfo.routeKey}`);
20
+ next();
21
+ };
22
+
23
+ const registerWithContext = CreateTypedHonoHandlerWithContext<Ctx>()
15
24
  // Register public handlers using Hono
16
- RegisterHonoHandlers(app, SimplePublicApiDefinition, {
25
+ registerWithContext(app, SimplePublicApiDefinition, {
17
26
  common: {
18
27
  ping: async (req, res) => {
19
- console.log('Handling ping request');
20
28
  res.respond(200, "pong");
21
29
  }
22
30
  },
@@ -33,7 +41,7 @@ async function startHonoServer() {
33
41
  res.respond(200, "pong");
34
42
  }
35
43
  }
36
- });
44
+ }, [loggingMiddleware]);
37
45
 
38
46
  // Register private handlers using Hono
39
47
  RegisterHonoHandlers(app, SimplePrivateApiDefinition, {
@@ -52,19 +60,15 @@ async function startHonoServer() {
52
60
 
53
61
  // Create a simple HTTP server wrapper for Hono
54
62
  const honoServer = http.createServer(async (req: any, res: any) => {
55
- console.log(`Incoming request: ${req.method} ${req.url}`);
56
-
57
63
  try {
58
64
  // Read the request body for non-GET/HEAD methods
59
65
  let body: ReadableStream | undefined;
60
66
  if (req.method !== 'GET' && req.method !== 'HEAD') {
61
- console.log('Reading request body...');
62
67
  const chunks: Buffer[] = [];
63
68
  for await (const chunk of req) {
64
69
  chunks.push(chunk);
65
70
  }
66
71
  const buffer = Buffer.concat(chunks);
67
- console.log('Request body length:', buffer.length);
68
72
  body = new ReadableStream({
69
73
  start(controller) {
70
74
  controller.enqueue(buffer);
@@ -73,18 +77,12 @@ async function startHonoServer() {
73
77
  });
74
78
  }
75
79
 
76
- console.log('Creating Web API Request...');
77
- const request = new Request(`http://localhost:${HONO_PORT}${req.url}`, {
80
+ const response = await server(new Request(`http://localhost:${HONO_PORT}${req.url}`, {
78
81
  method: req.method,
79
82
  headers: req.headers,
80
- body: body
81
- });
82
-
83
- console.log('Calling Hono app.fetch...');
84
- const response = await server(request);
85
-
86
- console.log('Response status:', response.status);
87
- console.log('Response headers:', Object.fromEntries(response.headers.entries()));
83
+ body: body,
84
+ duplex: body ? 'half' : undefined
85
+ } as any));
88
86
 
89
87
  res.statusCode = response.status;
90
88
  for (const [key, value] of response.headers) {
@@ -92,10 +90,8 @@ async function startHonoServer() {
92
90
  }
93
91
 
94
92
  const responseBody = await response.text();
95
- console.log('Response body:', responseBody);
96
93
  res.end(responseBody);
97
94
  } catch (error) {
98
- console.error('Hono server error:', error);
99
95
  res.statusCode = 500;
100
96
  res.end('Internal Server Error');
101
97
  }
@@ -115,10 +111,7 @@ async function startHonoServer() {
115
111
  // Graceful shutdown
116
112
  process.on('SIGINT', () => {
117
113
  console.log('Shutting down server...');
118
- honoServer.close(() => {
119
- console.log('Server closed');
120
- process.exit(0);
121
- });
114
+ process.exit(0);
122
115
  });
123
116
  }
124
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-typed-api",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
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
@@ -281,6 +281,7 @@ export function registerRouteHandlers<TDef extends ApiDefinitionSchema>(
281
281
  params: parsedParams,
282
282
  query: parsedQuery,
283
283
  body: parsedBody,
284
+ ctx: (expressReq as any).ctx,
284
285
  headers: expressReq.headers,
285
286
  cookies: expressReq.cookies,
286
287
  ip: expressReq.ip,
@@ -1,4 +1,4 @@
1
- import { Hono, Context, MiddlewareHandler } from 'hono';
1
+ import { Hono, Context, MiddlewareHandler, Env } from 'hono';
2
2
  import { z } from 'zod';
3
3
  import { ApiDefinitionSchema, RouteSchema, UnifiedError, FileUploadConfig } from './definition';
4
4
  import { TypedRequest, TypedResponse } from './router';
@@ -29,7 +29,8 @@ export type HonoFileType = z.infer<typeof honoFileSchema>;
29
29
  export type HonoTypedContext<
30
30
  TDef extends ApiDefinitionSchema,
31
31
  TDomain extends keyof TDef['endpoints'],
32
- TRouteKey extends keyof TDef['endpoints'][TDomain]
32
+ TRouteKey extends keyof TDef['endpoints'][TDomain],
33
+ Ctx extends Record<string, any> = Record<string, any>
33
34
  > = Context & {
34
35
  // Add typed request properties
35
36
  params: TypedRequest<TDef, TDomain, TRouteKey>['params'];
@@ -37,6 +38,8 @@ export type HonoTypedContext<
37
38
  body: TypedRequest<TDef, TDomain, TRouteKey>['body'];
38
39
  file?: HonoFile;
39
40
  files?: HonoFile[] | { [fieldname: string]: HonoFile[] };
41
+ // Add typed context object
42
+ ctx?: Ctx;
40
43
 
41
44
  // Add typed response method
42
45
  respond: TypedResponse<TDef, TDomain, TRouteKey>['respond'];
@@ -265,8 +268,13 @@ function createHonoFileUploadMiddleware(config: FileUploadConfig): MiddlewareHan
265
268
  }
266
269
 
267
270
  // Register route handlers with Hono, now generic over TDef
268
- export function registerHonoRouteHandlers<TDef extends ApiDefinitionSchema>(
269
- app: Hono,
271
+ export function registerHonoRouteHandlers<
272
+ TDef extends ApiDefinitionSchema,
273
+ TBindings extends Env = Env,
274
+ TVariables extends Record<string, never> = Record<string, never>,
275
+ TPath extends string = "/"
276
+ >(
277
+ app: Hono<TBindings, TVariables, TPath>,
270
278
  apiDefinition: TDef,
271
279
  routeHandlers: Array<SpecificRouteHandler<TDef>>,
272
280
  middlewares?: EndpointMiddleware<TDef>[]
@@ -328,6 +336,9 @@ export function registerHonoRouteHandlers<TDef extends ApiDefinitionSchema>(
328
336
  (c as any).query = parsedQuery;
329
337
  (c as any).body = parsedBody;
330
338
 
339
+ // Get context from Hono's context system
340
+ (c as any).ctx = c.get('ctx') || {};
341
+
331
342
  // Add respond method to context
332
343
  (c as any).respond = (status: number, data: any) => {
333
344
  const responseSchema = routeDefinition.responses[status];
@@ -387,6 +398,7 @@ export function registerHonoRouteHandlers<TDef extends ApiDefinitionSchema>(
387
398
  body: parsedBody,
388
399
  file: (c as any).file,
389
400
  files: (c as any).files,
401
+ ctx: (c as any).ctx,
390
402
  headers: c.req.header(),
391
403
  ip: c.req.header('CF-Connecting-IP') || '127.0.0.1',
392
404
  method: c.req.method,
@@ -475,10 +487,25 @@ export function registerHonoRouteHandlers<TDef extends ApiDefinitionSchema>(
475
487
  middlewares.forEach(middleware => {
476
488
  const wrappedMiddleware: MiddlewareHandler = async (c: any, next: any) => {
477
489
  try {
478
- await middleware(c.req as any, c.res as any, next, { domain: currentDomain, routeKey: currentRouteKey } as any);
490
+ // Create Express-like req object for middleware compatibility
491
+ const fakeReq = {
492
+ headers: c.req.header(),
493
+ get ctx() { return c.get('ctx') || {}; },
494
+ set ctx(value) { c.set('ctx', value); },
495
+ method: c.req.method,
496
+ path: c.req.path,
497
+ originalUrl: c.req.url
498
+ };
499
+
500
+ // Create minimal res object (middleware shouldn't use it)
501
+ const fakeRes = {};
502
+
503
+ // Call Express-style middleware
504
+ await middleware(fakeReq as any, fakeRes as any, next, { domain: currentDomain, routeKey: currentRouteKey });
505
+
479
506
  } catch (error) {
480
507
  console.error('Middleware error:', error);
481
- return next();
508
+ await next();
482
509
  }
483
510
  };
484
511
  middlewareWrappers.push(wrappedMiddleware);
@@ -502,8 +529,11 @@ export function registerHonoRouteHandlers<TDef extends ApiDefinitionSchema>(
502
529
  }
503
530
 
504
531
  // Transform object-based handlers to array format
505
- function transformObjectHandlersToArray<TDef extends ApiDefinitionSchema>(
506
- objectHandlers: ObjectHandlers<TDef>
532
+ function transformObjectHandlersToArray<
533
+ TDef extends ApiDefinitionSchema,
534
+ Ctx extends Record<string, any> = Record<string, any>
535
+ >(
536
+ objectHandlers: ObjectHandlers<TDef, Ctx>
507
537
  ): Array<SpecificRouteHandler<TDef>> {
508
538
  const handlerArray: Array<SpecificRouteHandler<TDef>> = [];
509
539
 
@@ -529,10 +559,16 @@ function transformObjectHandlersToArray<TDef extends ApiDefinitionSchema>(
529
559
  }
530
560
 
531
561
  // Main utility function that registers object-based handlers with Hono
532
- export function RegisterHonoHandlers<TDef extends ApiDefinitionSchema>(
533
- app: Hono,
562
+ export function RegisterHonoHandlers<
563
+ TDef extends ApiDefinitionSchema,
564
+ Ctx extends Record<string, any> = Record<string, any>,
565
+ TBindings extends Env = Env,
566
+ TVariables extends Record<string, never> = Record<string, never>,
567
+ TPath extends string = "/"
568
+ >(
569
+ app: Hono<TBindings, TVariables, TPath>,
534
570
  apiDefinition: TDef,
535
- objectHandlers: ObjectHandlers<TDef>,
571
+ objectHandlers: ObjectHandlers<TDef, Ctx>,
536
572
  middlewares?: AnyMiddleware<TDef>[]
537
573
  ): void {
538
574
  const handlerArray = transformObjectHandlersToArray(objectHandlers);
@@ -550,3 +586,14 @@ export function RegisterHonoHandlers<TDef extends ApiDefinitionSchema>(
550
586
 
551
587
  registerHonoRouteHandlers(app, apiDefinition, handlerArray, endpointMiddlewares);
552
588
  }
589
+
590
+ export function CreateTypedHonoHandlerWithContext<Ctx extends Record<string, any>>() {
591
+ return function <TDef extends ApiDefinitionSchema>(
592
+ app: Hono,
593
+ apiDefinition: TDef,
594
+ objectHandlers: ObjectHandlers<TDef, Ctx>,
595
+ middlewares?: AnyMiddleware<TDef>[]
596
+ ) {
597
+ return RegisterHonoHandlers(app, apiDefinition, objectHandlers, middlewares);
598
+ };
599
+ }
package/src/hono-only.ts CHANGED
@@ -5,7 +5,7 @@ export { z as ZodSchema } from 'zod';
5
5
  export { EndpointMiddleware } from './object-handlers'
6
6
 
7
7
  // Hono adapter for Cloudflare Workers
8
- export { RegisterHonoHandlers } from './hono-cloudflare-workers';
8
+ export { RegisterHonoHandlers, CreateTypedHonoHandlerWithContext } from './hono-cloudflare-workers';
9
9
 
10
10
  // Re-export types that are needed for Hono development
11
11
  export type {
package/src/index.ts CHANGED
@@ -7,4 +7,4 @@ export { File as UploadedFile } from './router';
7
7
  export { z as ZodSchema } from 'zod';
8
8
 
9
9
  // Hono adapter for Cloudflare Workers
10
- export { RegisterHonoHandlers, registerHonoRouteHandlers, HonoFile, HonoFileType, honoFileSchema, HonoTypedContext } from './hono-cloudflare-workers';
10
+ export { RegisterHonoHandlers, registerHonoRouteHandlers, HonoFile, HonoFileType, honoFileSchema, HonoTypedContext, CreateTypedHonoHandlerWithContext } from './hono-cloudflare-workers';
@@ -36,6 +36,16 @@ export type UniversalEndpointMiddleware = (
36
36
  }
37
37
  ) => void | Promise<void>;
38
38
 
39
+ // Unified middleware type that works for both Express and Hono with context typing
40
+ export type EndpointMiddlewareCtx<
41
+ Ctx extends Record<string, any> = Record<string, any>,
42
+ TDef extends ApiDefinitionSchema = ApiDefinitionSchema
43
+ > =
44
+ // Express version: (req, res, next, endpointInfo)
45
+ ((req: express.Request & { ctx?: Ctx }, res: express.Response, next: express.NextFunction, endpointInfo: EndpointInfo<TDef>) => void | Promise<void>) |
46
+ // Hono version: (c, next) - context is passed via c.req.ctx -> c.ctx copying
47
+ ((c: any, next: any) => void | Promise<void>);
48
+
39
49
  // Union type that accepts endpoint-aware, universal, and simple middleware
40
50
  export type AnyMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema> =
41
51
  | EndpointMiddleware<TDef>
@@ -46,23 +56,30 @@ export type AnyMiddleware<TDef extends ApiDefinitionSchema = ApiDefinitionSchema
46
56
  type HandlerFunction<
47
57
  TDef extends ApiDefinitionSchema,
48
58
  TDomain extends keyof TDef['endpoints'],
49
- TRouteKey extends keyof TDef['endpoints'][TDomain]
59
+ TRouteKey extends keyof TDef['endpoints'][TDomain],
60
+ Ctx extends Record<string, any> = Record<string, any>
50
61
  > = (
51
- req: TypedRequest<TDef, TDomain, TRouteKey>,
62
+ req: TypedRequest<TDef, TDomain, TRouteKey, any, any, any, any, Ctx>,
52
63
  res: TypedResponse<TDef, TDomain, TRouteKey>
53
64
  ) => Promise<void> | void;
54
65
 
55
66
  // Type for the object-based handler definition
56
67
  // This ensures all domains and routes are required
57
- export type ObjectHandlers<TDef extends ApiDefinitionSchema> = {
58
- [TDomain in keyof TDef['endpoints']]: {
59
- [TRouteKey in keyof TDef['endpoints'][TDomain]]: HandlerFunction<TDef, TDomain, TRouteKey>;
68
+ export type ObjectHandlers<
69
+ TDef extends ApiDefinitionSchema,
70
+ Ctx extends Record<string, any> = Record<string, any>
71
+ > = {
72
+ [TDomain in keyof TDef['endpoints']]: {
73
+ [TRouteKey in keyof TDef['endpoints'][TDomain]]: HandlerFunction<TDef, TDomain, TRouteKey, Ctx>;
74
+ };
60
75
  };
61
- };
62
76
 
63
77
  // Transform object-based handlers to array format
64
- function transformObjectHandlersToArray<TDef extends ApiDefinitionSchema>(
65
- objectHandlers: ObjectHandlers<TDef>
78
+ function transformObjectHandlersToArray<
79
+ TDef extends ApiDefinitionSchema,
80
+ Ctx extends Record<string, any> = Record<string, any>
81
+ >(
82
+ objectHandlers: ObjectHandlers<TDef, Ctx>
66
83
  ): Array<SpecificRouteHandler<TDef>> {
67
84
  const handlerArray: Array<SpecificRouteHandler<TDef>> = [];
68
85
 
@@ -91,10 +108,13 @@ function transformObjectHandlersToArray<TDef extends ApiDefinitionSchema>(
91
108
  }
92
109
 
93
110
  // Main utility function that registers object-based handlers
94
- export function RegisterHandlers<TDef extends ApiDefinitionSchema>(
111
+ export function RegisterHandlers<
112
+ TDef extends ApiDefinitionSchema,
113
+ Ctx extends Record<string, any> = Record<string, any>
114
+ >(
95
115
  app: express.Express,
96
116
  apiDefinition: TDef,
97
- objectHandlers: ObjectHandlers<TDef>,
117
+ objectHandlers: ObjectHandlers<TDef, Ctx>,
98
118
  middlewares?: AnyMiddleware<TDef>[]
99
119
  ): void {
100
120
  const handlerArray = transformObjectHandlersToArray(objectHandlers);
package/src/router.ts CHANGED
@@ -19,11 +19,14 @@ export type TypedRequest<
19
19
  P extends ApiParams<TDef, TDomain, TRouteKey> = ApiParams<TDef, TDomain, TRouteKey>,
20
20
  ReqBody extends ApiBody<TDef, TDomain, TRouteKey> = ApiBody<TDef, TDomain, TRouteKey>,
21
21
  Q extends ApiQuery<TDef, TDomain, TRouteKey> = ApiQuery<TDef, TDomain, TRouteKey>,
22
- L extends Record<string, any> = Record<string, any>
22
+ L extends Record<string, any> = Record<string, any>,
23
+ Ctx extends Record<string, any> = Record<string, any>
23
24
  > = express.Request<P, any, ReqBody, Q, L> & {
24
25
  // Add file upload support
25
26
  file?: File;
26
27
  files?: File[] | { [fieldname: string]: File[] };
28
+ // Add typed context object for carrying data between middlewares and handlers
29
+ ctx?: Ctx;
27
30
  }
28
31
 
29
32
  // --- Enhanced TypedResponse with res.respond, now generic over TDef ---
@@ -59,18 +62,19 @@ export interface TypedResponse<
59
62
  json: <B = any>(body: B) => this; // Keep original json
60
63
  }
61
64
 
62
- // Type-safe route handler creation function, now generic over TDef
65
+ // Type-safe route handler creation function, now generic over TDef and Ctx
63
66
  // This function is called within a context where TDef is known (e.g. specific handlers file)
64
67
  export function createRouteHandler<
65
68
  TDef extends ApiDefinitionSchema,
66
69
  TDomain extends keyof TDef['endpoints'],
67
- TRouteKey extends keyof TDef['endpoints'][TDomain] // Using direct keyof for simplicity
70
+ TRouteKey extends keyof TDef['endpoints'][TDomain], // Using direct keyof for simplicity
71
+ Ctx extends Record<string, any> = Record<string, any>
68
72
  >(
69
73
  domain: TDomain,
70
74
  routeKey: TRouteKey,
71
75
  handler: (
72
- // Use the TDef generic for TypedRequest and TypedResponse
73
- req: TypedRequest<TDef, TDomain, TRouteKey>,
76
+ // Use the TDef generic for TypedRequest and TypedResponse with Ctx
77
+ req: TypedRequest<TDef, TDomain, TRouteKey, any, any, any, any, Ctx>,
74
78
  res: TypedResponse<TDef, TDomain, TRouteKey>
75
79
  ) => Promise<void> | void
76
80
  ) {
@@ -0,0 +1,84 @@
1
+ import { describe, test, expect } from '@jest/globals';
2
+ import { ApiClient } from '../src';
3
+ import { MiddlewareTestApiDefinition } from './setup';
4
+
5
+ // Test servers with middleware (will be defined in setup.ts)
6
+ export const MIDDLEWARE_EXPRESS_PORT = 3007;
7
+ export const MIDDLEWARE_HONO_PORT = 3008;
8
+
9
+ describe.each([
10
+ ['Express', MIDDLEWARE_EXPRESS_PORT],
11
+ ['Hono', MIDDLEWARE_HONO_PORT]
12
+ ])('Middleware Tests - %s', (serverName, port) => {
13
+ const baseUrl = `http://localhost:${port}`;
14
+ const client = new ApiClient(baseUrl, MiddlewareTestApiDefinition);
15
+
16
+ describe('Basic Middleware Functionality', () => {
17
+ test('should execute middleware and allow request to proceed', async () => {
18
+ const result = await client.callApi('public', 'ping', {}, {
19
+ 200: ({ data }) => {
20
+ expect(data.message).toBe('pong');
21
+ return data;
22
+ },
23
+ 422: ({ error }) => {
24
+ throw new Error(`Validation error: ${JSON.stringify(error)}`);
25
+ }
26
+ });
27
+
28
+ expect(result.message).toBe('pong');
29
+ });
30
+ });
31
+
32
+ describe('Context Modification Middleware', () => {
33
+ test('should have access to context data set by middleware', async () => {
34
+ const result = await client.callApi('public', 'context', {}, {
35
+ 200: ({ data }) => {
36
+ expect(data.message).toBe('context test');
37
+ expect(data.contextData).toBe('middleware-added-data');
38
+ return data;
39
+ },
40
+ 422: ({ error }) => {
41
+ throw new Error(`Validation error: ${JSON.stringify(error)}`);
42
+ }
43
+ });
44
+
45
+ expect(result.contextData).toBe('middleware-added-data');
46
+ });
47
+ });
48
+
49
+ describe('Authentication Middleware', () => {
50
+ test('should allow access with valid auth header', async () => {
51
+ // This test assumes the middleware checks for an 'authorization' header
52
+ const result = await client.callApi('public', 'protected', { headers: { Authorization: 'Bearer valid-token' } }, {
53
+ 200: ({ data }) => {
54
+ expect(data.message).toBe('protected content');
55
+ expect(data.user).toBe('testuser');
56
+ return data;
57
+ },
58
+ 401: ({ data }) => {
59
+ throw new Error(`Authentication failed: ${data.error}`);
60
+ },
61
+ 422: ({ error }) => {
62
+ throw new Error(`Validation error: ${JSON.stringify(error)}`);
63
+ }
64
+ });
65
+
66
+ expect(result.user).toBe('testuser');
67
+ });
68
+
69
+ test('should deny access without auth header', async () => {
70
+ await expect(
71
+ client.callApi('public', 'protected', {}, {
72
+ 200: ({ data }) => data,
73
+ 401: ({ data }) => {
74
+ expect(data.error).toBe('No authorization header');
75
+ throw new Error('Authentication failed as expected');
76
+ },
77
+ 422: ({ error }) => {
78
+ throw new Error(`Validation error: ${JSON.stringify(error)}`);
79
+ }
80
+ })
81
+ ).rejects.toThrow('Authentication failed as expected');
82
+ });
83
+ });
84
+ });
package/tests/setup.ts CHANGED
@@ -5,8 +5,9 @@ import http from 'http';
5
5
  import { Hono } from 'hono';
6
6
  import { PublicApiDefinition as SimplePublicApiDefinition, PrivateApiDefinition as SimplePrivateApiDefinition } from '../examples/simple/definitions';
7
7
  import { PublicApiDefinition as AdvancedPublicApiDefinition, PrivateApiDefinition as AdvancedPrivateApiDefinition } from '../examples/advanced/definitions';
8
- import { RegisterHandlers, RegisterHonoHandlers, CreateApiDefinition, CreateResponses } from '../src';
8
+ import { RegisterHandlers, RegisterHonoHandlers, CreateApiDefinition, CreateResponses, CreateTypedHonoHandlerWithContext } from '../src';
9
9
  import { z } from 'zod';
10
+ import { EndpointMiddlewareCtx } from '../src/object-handlers';
10
11
 
11
12
  // Shared handler definitions for simple API
12
13
  const simplePublicHandlers = {
@@ -202,6 +203,36 @@ const fileUploadHandlers = {
202
203
  }
203
204
  };
204
205
 
206
+ export const MiddlewareTestApiDefinition = CreateApiDefinition({
207
+ prefix: '/api/v1',
208
+ endpoints: {
209
+ public: {
210
+ ping: {
211
+ method: 'GET' as const,
212
+ path: '/ping',
213
+ responses: CreateResponses({
214
+ 200: z.object({ message: z.string() })
215
+ })
216
+ },
217
+ protected: {
218
+ method: 'GET' as const,
219
+ path: '/protected',
220
+ responses: CreateResponses({
221
+ 200: z.object({ message: z.string(), user: z.string() }),
222
+ 401: z.object({ error: z.string() })
223
+ })
224
+ },
225
+ context: {
226
+ method: 'GET' as const,
227
+ path: '/context',
228
+ responses: CreateResponses({
229
+ 200: z.object({ message: z.string(), contextData: z.string() })
230
+ })
231
+ }
232
+ }
233
+ }
234
+ });
235
+
205
236
  // Global test server instances
206
237
  export let simpleServer: Server;
207
238
  export let advancedServer: Server;
@@ -209,6 +240,8 @@ export let fileUploadServer: Server;
209
240
  export let honoServer: Server;
210
241
  export let advancedHonoServer: Server;
211
242
  export let fileUploadHonoServer: Server;
243
+ export let middlewareExpressServer: Server;
244
+ export let middlewareHonoServer: Server;
212
245
 
213
246
  export const SIMPLE_PORT = 3001;
214
247
  export const ADVANCED_PORT = 3002;
@@ -216,6 +249,8 @@ export const FILE_UPLOAD_PORT = 3003;
216
249
  export const HONO_PORT = 3004;
217
250
  export const ADVANCED_HONO_PORT = 3005;
218
251
  export const FILE_UPLOAD_HONO_PORT = 3006;
252
+ export const MIDDLEWARE_EXPRESS_PORT = 3007;
253
+ export const MIDDLEWARE_HONO_PORT = 3008;
219
254
 
220
255
  // Helper function to create HTTP server wrapper for Hono apps
221
256
  function createHonoHttpServer(server: any, port: number, errorPrefix: string): Server {
@@ -267,6 +302,8 @@ beforeAll(async () => {
267
302
  await startHonoServer();
268
303
  await startAdvancedHonoServer();
269
304
  await startFileUploadHonoServer();
305
+ await startMiddlewareExpressServer();
306
+ await startMiddlewareHonoServer();
270
307
  });
271
308
 
272
309
  afterAll(async () => {
@@ -289,6 +326,12 @@ afterAll(async () => {
289
326
  if (fileUploadHonoServer) {
290
327
  fileUploadHonoServer.close();
291
328
  }
329
+ if (middlewareExpressServer) {
330
+ middlewareExpressServer.close();
331
+ }
332
+ if (middlewareHonoServer) {
333
+ middlewareHonoServer.close();
334
+ }
292
335
  });
293
336
 
294
337
  async function startSimpleServer(): Promise<void> {
@@ -399,3 +442,132 @@ async function startHonoServer(): Promise<void> {
399
442
  });
400
443
  });
401
444
  }
445
+
446
+ type Ctx = { user: string }
447
+
448
+ // Middleware test servers
449
+ async function startMiddlewareExpressServer(): Promise<void> {
450
+ return new Promise((resolve) => {
451
+ const app = express();
452
+ app.use(express.json());
453
+
454
+ // Define middleware functions
455
+ const loggingMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction, endpointInfo: any) => {
456
+ console.log(`[Test] ${req.method} ${req.path} - Domain: ${endpointInfo.domain}, Route: ${endpointInfo.routeKey}`);
457
+ next();
458
+ };
459
+
460
+ const contextMiddleware = (req: express.Request, res: express.Response, next: express.NextFunction) => {
461
+ (req as any).ctx = { middlewareData: "middleware-added-data" };
462
+ next();
463
+ };
464
+
465
+ const authMiddleware: EndpointMiddlewareCtx<Ctx> = (req, res, next) => {
466
+ const authHeader = req.headers.authorization;
467
+ if (authHeader === 'Bearer valid-token') {
468
+ req.ctx = { user: 'testuser' };
469
+ }
470
+ next();
471
+ };
472
+
473
+ // Register handlers with middleware
474
+ RegisterHandlers(app, MiddlewareTestApiDefinition, {
475
+ public: {
476
+ ping: async (req: any, res: any) => {
477
+ res.respond(200, { message: "pong" });
478
+ },
479
+ protected: async (req, res) => {
480
+ // Check if user is authenticated via context
481
+ if (req.ctx && req.ctx.user) {
482
+ res.respond(200, {
483
+ message: "protected content",
484
+ user: req.ctx.user
485
+ });
486
+ } else {
487
+ res.respond(401, { error: "No authorization header" });
488
+ }
489
+ },
490
+ context: async (req: any, res: any) => {
491
+ res.respond(200, {
492
+ message: "context test",
493
+ contextData: req.ctx?.middlewareData || "default"
494
+ });
495
+ }
496
+ }
497
+ }, [
498
+ loggingMiddleware,
499
+ contextMiddleware,
500
+ authMiddleware
501
+ ]);
502
+
503
+ middlewareExpressServer = app.listen(MIDDLEWARE_EXPRESS_PORT, () => {
504
+ resolve();
505
+ });
506
+ });
507
+ }
508
+
509
+ async function startMiddlewareHonoServer(): Promise<void> {
510
+ return new Promise((resolve) => {
511
+ const app = new Hono();
512
+
513
+ // Define middleware functions
514
+ const loggingMiddleware = async (req: any, res: any, next: any, endpointInfo: any) => {
515
+ console.log(`[Test Hono] ${req.method} ${req.path} - Domain: ${endpointInfo.domain}, Route: ${endpointInfo.routeKey}`);
516
+ await next();
517
+ };
518
+
519
+ const contextMiddleware = async (req: any, res: any, next: any) => {
520
+ req.ctx = { ...req.ctx, middlewareData: "middleware-added-data" };
521
+ await next();
522
+ };
523
+
524
+ const authMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next) => {
525
+ const authHeader = req.headers?.authorization;
526
+ if (authHeader === 'Bearer valid-token') {
527
+ req.ctx = { ...req.ctx, user: 'testuser' };
528
+ }
529
+ await next();
530
+ };
531
+
532
+ const hdnl = CreateTypedHonoHandlerWithContext<Ctx>()
533
+ // Register handlers with middleware
534
+ hdnl(app, MiddlewareTestApiDefinition, {
535
+ public: {
536
+ ping: async (req: any, res: any) => {
537
+ res.respond(200, { message: "pong" });
538
+ },
539
+ protected: async (req, res) => {
540
+ // Check if user is authenticated via context
541
+ if (req.ctx && req.ctx.user) {
542
+ res.respond(200, {
543
+ message: "protected content",
544
+ user: req.ctx.user
545
+ });
546
+ } else {
547
+ res.respond(401, { error: "No authorization header" });
548
+ }
549
+ },
550
+ context: async (req: any, res: any) => {
551
+ res.respond(200, {
552
+ message: "context test",
553
+ contextData: req.ctx?.middlewareData || "default"
554
+ });
555
+ }
556
+ }
557
+ }, [
558
+ loggingMiddleware,
559
+ contextMiddleware,
560
+ authMiddleware
561
+ ]);
562
+
563
+ // Create HTTP server from Hono app
564
+ const server = app.fetch;
565
+
566
+ // Create a simple HTTP server wrapper for Hono
567
+ middlewareHonoServer = createHonoHttpServer(server, MIDDLEWARE_HONO_PORT, 'Middleware Hono server');
568
+
569
+ middlewareHonoServer.listen(MIDDLEWARE_HONO_PORT, () => {
570
+ resolve();
571
+ });
572
+ });
573
+ }