ts-typed-api 0.2.13 → 0.2.15
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 +2 -1
- package/dist/handler.js +9 -3
- package/dist/hono-cloudflare-workers.js +19 -1
- package/dist/object-handlers.d.ts +11 -4
- package/dist/router.d.ts +1 -0
- package/examples/simple/definitions.ts +9 -0
- package/package.json +1 -1
- package/src/handler.ts +12 -4
- package/src/hono-cloudflare-workers.ts +19 -1
- package/src/object-handlers.ts +13 -4
- package/src/router.ts +1 -0
- package/tests/setup.ts +38 -61
- package/tests/simple-api.test.ts +33 -0
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:
|
|
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
|
@@ -302,7 +302,7 @@ middlewares) {
|
|
|
302
302
|
baseUrl: expressReq.baseUrl,
|
|
303
303
|
url: expressReq.url,
|
|
304
304
|
};
|
|
305
|
-
// Augment expressRes with the .respond
|
|
305
|
+
// Augment expressRes with the .respond and .setHeader methods, using TDef
|
|
306
306
|
const typedExpressRes = expressRes;
|
|
307
307
|
typedExpressRes.respond = (status, dataForResponse) => {
|
|
308
308
|
// Use the passed apiDefinition object
|
|
@@ -347,6 +347,11 @@ middlewares) {
|
|
|
347
347
|
});
|
|
348
348
|
}
|
|
349
349
|
};
|
|
350
|
+
typedExpressRes.setHeader = (name, value) => {
|
|
351
|
+
// Call the original Express setHeader method to avoid recursion
|
|
352
|
+
Object.getPrototypeOf(expressRes).setHeader.call(expressRes, name, value);
|
|
353
|
+
return typedExpressRes;
|
|
354
|
+
};
|
|
350
355
|
const specificHandlerFn = handler;
|
|
351
356
|
await specificHandlerFn(finalTypedReq, typedExpressRes);
|
|
352
357
|
}
|
|
@@ -412,10 +417,11 @@ middlewares) {
|
|
|
412
417
|
const wrappedMiddleware = async (req, res, next) => {
|
|
413
418
|
try {
|
|
414
419
|
// Add respond method to res for middleware compatibility
|
|
415
|
-
|
|
420
|
+
const middlewareRes = res;
|
|
421
|
+
middlewareRes.respond = createRespondFunction(routeDefinition, (status, data) => {
|
|
416
422
|
res.status(status).json(data);
|
|
417
423
|
});
|
|
418
|
-
await middleware(req,
|
|
424
|
+
await middleware(req, middlewareRes, next, { domain: currentDomain, routeKey: currentRouteKey });
|
|
419
425
|
}
|
|
420
426
|
catch (error) {
|
|
421
427
|
next(error);
|
|
@@ -329,7 +329,11 @@ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middleware
|
|
|
329
329
|
originalUrl: c.req.url
|
|
330
330
|
};
|
|
331
331
|
const fakeRes = {
|
|
332
|
-
respond: c.respond
|
|
332
|
+
respond: c.respond,
|
|
333
|
+
setHeader: (name, value) => {
|
|
334
|
+
c.header(name, value);
|
|
335
|
+
return fakeRes;
|
|
336
|
+
}
|
|
333
337
|
};
|
|
334
338
|
const specificHandlerFn = handler;
|
|
335
339
|
await specificHandlerFn(fakeReq, fakeRes);
|
|
@@ -457,6 +461,20 @@ function registerHonoRouteHandlers(app, apiDefinition, routeHandlers, middleware
|
|
|
457
461
|
error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
|
|
458
462
|
}, 500);
|
|
459
463
|
}
|
|
464
|
+
},
|
|
465
|
+
status: (status) => {
|
|
466
|
+
// For middleware that might use status, but since not used, stub
|
|
467
|
+
c.__response = new Response(null, { status: status });
|
|
468
|
+
},
|
|
469
|
+
json: (data) => {
|
|
470
|
+
// Send json response
|
|
471
|
+
c.__response = c.json(data);
|
|
472
|
+
},
|
|
473
|
+
setHeader: (name, value) => {
|
|
474
|
+
c.header(name, value);
|
|
475
|
+
},
|
|
476
|
+
end: () => {
|
|
477
|
+
// Perhaps do nothing or set response
|
|
460
478
|
}
|
|
461
479
|
};
|
|
462
480
|
// Call Express-style middleware
|
|
@@ -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:
|
|
11
|
-
export type SimpleMiddleware = (req: express.Request, res:
|
|
12
|
-
export type UniversalEndpointMiddleware = (req: express.Request, res:
|
|
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:
|
|
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/dist/router.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ type ResponseDataForStatus<TDef extends ApiDefinitionSchema, TDomain extends key
|
|
|
14
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
15
|
export interface TypedResponse<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteName extends keyof TDef['endpoints'][TDomain], L extends Record<string, any> = Record<string, any>> extends express.Response<any, L> {
|
|
16
16
|
respond: RespondFunction<TDef, TDomain, TRouteName>;
|
|
17
|
+
setHeader: (name: string, value: string) => this;
|
|
17
18
|
json: <B = any>(body: B) => this;
|
|
18
19
|
}
|
|
19
20
|
export declare function createRouteHandler<TDef extends ApiDefinitionSchema, TDomain extends keyof TDef['endpoints'], TRouteKey extends keyof TDef['endpoints'][TDomain], // Using direct keyof for simplicity
|
|
@@ -34,6 +34,15 @@ export const PublicApiDefinition = CreateApiDefinition({
|
|
|
34
34
|
200: z.enum(["pong"]),
|
|
35
35
|
})
|
|
36
36
|
},
|
|
37
|
+
customHeaders: {
|
|
38
|
+
method: 'GET',
|
|
39
|
+
path: '/custom-headers',
|
|
40
|
+
responses: CreateResponses({
|
|
41
|
+
200: z.object({
|
|
42
|
+
message: z.string()
|
|
43
|
+
}),
|
|
44
|
+
})
|
|
45
|
+
},
|
|
37
46
|
}
|
|
38
47
|
}
|
|
39
48
|
})
|
package/package.json
CHANGED
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:
|
|
20
|
+
res: MiddlewareResponse,
|
|
20
21
|
next: express.NextFunction,
|
|
21
22
|
endpointInfo: {
|
|
22
23
|
[TDomain in keyof TDef['endpoints']]: {
|
|
@@ -354,7 +355,7 @@ export function registerRouteHandlers<TDef extends ApiDefinitionSchema>(
|
|
|
354
355
|
url: expressReq.url,
|
|
355
356
|
} as TypedRequest<TDef, typeof currentDomain, typeof currentRouteKey>;
|
|
356
357
|
|
|
357
|
-
// Augment expressRes with the .respond
|
|
358
|
+
// Augment expressRes with the .respond and .setHeader methods, using TDef
|
|
358
359
|
const typedExpressRes = expressRes as TypedResponse<TDef, typeof currentDomain, typeof currentRouteKey>;
|
|
359
360
|
|
|
360
361
|
typedExpressRes.respond = (status, dataForResponse) => {
|
|
@@ -407,6 +408,12 @@ export function registerRouteHandlers<TDef extends ApiDefinitionSchema>(
|
|
|
407
408
|
}
|
|
408
409
|
};
|
|
409
410
|
|
|
411
|
+
typedExpressRes.setHeader = (name: string, value: string) => {
|
|
412
|
+
// Call the original Express setHeader method to avoid recursion
|
|
413
|
+
Object.getPrototypeOf(expressRes).setHeader.call(expressRes, name, value);
|
|
414
|
+
return typedExpressRes;
|
|
415
|
+
};
|
|
416
|
+
|
|
410
417
|
const specificHandlerFn = handler as (
|
|
411
418
|
req: TypedRequest<TDef, typeof currentDomain, typeof currentRouteKey>,
|
|
412
419
|
res: TypedResponse<TDef, typeof currentDomain, typeof currentRouteKey>
|
|
@@ -474,10 +481,11 @@ export function registerRouteHandlers<TDef extends ApiDefinitionSchema>(
|
|
|
474
481
|
const wrappedMiddleware: express.RequestHandler = async (req, res, next) => {
|
|
475
482
|
try {
|
|
476
483
|
// Add respond method to res for middleware compatibility
|
|
477
|
-
|
|
484
|
+
const middlewareRes = res as any;
|
|
485
|
+
middlewareRes.respond = createRespondFunction(routeDefinition, (status, data) => {
|
|
478
486
|
res.status(status).json(data);
|
|
479
487
|
});
|
|
480
|
-
await middleware(req,
|
|
488
|
+
await middleware(req, middlewareRes as MiddlewareResponse, next, { domain: currentDomain, routeKey: currentRouteKey } as any);
|
|
481
489
|
} catch (error) {
|
|
482
490
|
next(error);
|
|
483
491
|
}
|
|
@@ -407,7 +407,11 @@ export function registerHonoRouteHandlers<
|
|
|
407
407
|
} as TypedRequest<TDef, typeof currentDomain, typeof currentRouteKey>;
|
|
408
408
|
|
|
409
409
|
const fakeRes = {
|
|
410
|
-
respond: (c as any).respond
|
|
410
|
+
respond: (c as any).respond,
|
|
411
|
+
setHeader: (name: string, value: string) => {
|
|
412
|
+
c.header(name, value);
|
|
413
|
+
return fakeRes;
|
|
414
|
+
}
|
|
411
415
|
} as TypedResponse<TDef, typeof currentDomain, typeof currentRouteKey>;
|
|
412
416
|
|
|
413
417
|
const specificHandlerFn = handler as (
|
|
@@ -546,6 +550,20 @@ export function registerHonoRouteHandlers<
|
|
|
546
550
|
error: [{ field: "general", type: "general", message: "Internal server error: Constructed response failed validation." }]
|
|
547
551
|
}, 500);
|
|
548
552
|
}
|
|
553
|
+
},
|
|
554
|
+
status: (status: number) => {
|
|
555
|
+
// For middleware that might use status, but since not used, stub
|
|
556
|
+
(c as any).__response = new Response(null, { status: status });
|
|
557
|
+
},
|
|
558
|
+
json: (data: any) => {
|
|
559
|
+
// Send json response
|
|
560
|
+
(c as any).__response = c.json(data);
|
|
561
|
+
},
|
|
562
|
+
setHeader: (name: string, value: string) => {
|
|
563
|
+
c.header(name, value);
|
|
564
|
+
},
|
|
565
|
+
end: () => {
|
|
566
|
+
// Perhaps do nothing or set response
|
|
549
567
|
}
|
|
550
568
|
};
|
|
551
569
|
|
package/src/object-handlers.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
package/src/router.ts
CHANGED
|
@@ -59,6 +59,7 @@ export interface TypedResponse<
|
|
|
59
59
|
L extends Record<string, any> = Record<string, any>
|
|
60
60
|
> extends express.Response<any, L> {
|
|
61
61
|
respond: RespondFunction<TDef, TDomain, TRouteName>;
|
|
62
|
+
setHeader: (name: string, value: string) => this;
|
|
62
63
|
json: <B = any>(body: B) => this; // Keep original json
|
|
63
64
|
}
|
|
64
65
|
|
package/tests/setup.ts
CHANGED
|
@@ -7,12 +7,18 @@ import { PublicApiDefinition as SimplePublicApiDefinition, PrivateApiDefinition
|
|
|
7
7
|
import { PublicApiDefinition as AdvancedPublicApiDefinition, PrivateApiDefinition as AdvancedPrivateApiDefinition } from '../examples/advanced/definitions';
|
|
8
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 = {
|
|
13
14
|
common: {
|
|
14
15
|
ping: async (req: any, res: any) => {
|
|
15
16
|
res.respond(200, "pong");
|
|
17
|
+
},
|
|
18
|
+
customHeaders: async (req: any, res: any) => {
|
|
19
|
+
res.setHeader('X-Custom-Test', 'test-value');
|
|
20
|
+
res.setHeader('X-Another-Header', 'another-value');
|
|
21
|
+
res.respond(200, { message: "headers set" });
|
|
16
22
|
}
|
|
17
23
|
},
|
|
18
24
|
status: {
|
|
@@ -444,7 +450,7 @@ async function startHonoServer(): Promise<void> {
|
|
|
444
450
|
});
|
|
445
451
|
}
|
|
446
452
|
|
|
447
|
-
type Ctx = { user?: string; noAuth?: boolean; forbidden?: boolean }
|
|
453
|
+
type Ctx = { user?: string; noAuth?: boolean; forbidden?: boolean, middlewareData?: string }
|
|
448
454
|
|
|
449
455
|
// Shared handlers for middleware tests
|
|
450
456
|
const middlewareTestHandlers = {
|
|
@@ -471,74 +477,45 @@ const middlewareTestHandlers = {
|
|
|
471
477
|
// Generic middleware setup function
|
|
472
478
|
function setupMiddlewareApp(app: any, isHono: boolean) {
|
|
473
479
|
// Define middleware functions
|
|
474
|
-
const loggingMiddleware =
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
const authMiddleware = isHono ?
|
|
495
|
-
async (req: any, res: any, next: any, endpointInfo: any) => {
|
|
496
|
-
// Only apply auth checks to protected routes
|
|
497
|
-
if (endpointInfo.domain === 'public' && endpointInfo.routeKey === 'protected') {
|
|
498
|
-
const authHeader = req.headers?.authorization;
|
|
499
|
-
if (!authHeader) {
|
|
500
|
-
(res as any).respond(401, { error: "No authorization header" });
|
|
501
|
-
} else if (authHeader === 'Bearer valid-token') {
|
|
502
|
-
req.ctx = { ...req.ctx, user: 'testuser' };
|
|
503
|
-
await next();
|
|
504
|
-
} else {
|
|
505
|
-
(res as any).respond(403, { error: "Forbidden" });
|
|
506
|
-
}
|
|
507
|
-
} else {
|
|
480
|
+
const loggingMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next, endpointInfo) => {
|
|
481
|
+
console.log(`[Test] ${req.method} ${req.path} - Domain: ${endpointInfo.domain}, Route: ${endpointInfo.routeKey}`);
|
|
482
|
+
await next();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const contextMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next) => {
|
|
486
|
+
req.ctx = { ...req.ctx, middlewareData: "middleware-added-data" };
|
|
487
|
+
await next();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const authMiddleware: EndpointMiddlewareCtx<Ctx> = async (req, res, next, endpointInfo) => {
|
|
491
|
+
// Only apply auth checks to protected routes
|
|
492
|
+
if (endpointInfo.domain === 'public' && endpointInfo.routeKey === 'protected') {
|
|
493
|
+
const authHeader = req.headers?.authorization;
|
|
494
|
+
if (!authHeader) {
|
|
495
|
+
res.respond(401, { error: "No authorization header" });
|
|
496
|
+
} else if (authHeader === 'Bearer valid-token') {
|
|
497
|
+
req.ctx = { ...req.ctx, user: 'testuser' };
|
|
508
498
|
await next();
|
|
509
|
-
}
|
|
510
|
-
} :
|
|
511
|
-
(req: any, res: any, next: any, endpointInfo: any) => {
|
|
512
|
-
// Only apply auth checks to protected routes
|
|
513
|
-
if (endpointInfo.domain === 'public' && endpointInfo.routeKey === 'protected') {
|
|
514
|
-
const authHeader = req.headers?.authorization;
|
|
515
|
-
if (!authHeader) {
|
|
516
|
-
(res as any).respond(401, { error: "No authorization header" });
|
|
517
|
-
} else if (authHeader === 'Bearer valid-token') {
|
|
518
|
-
req.ctx = { ...req.ctx, user: 'testuser' };
|
|
519
|
-
next();
|
|
520
|
-
} else {
|
|
521
|
-
(res as any).respond(403, { error: "Forbidden" });
|
|
522
|
-
}
|
|
523
499
|
} else {
|
|
524
|
-
|
|
500
|
+
res.respond(403, { error: "Forbidden" });
|
|
525
501
|
}
|
|
526
|
-
}
|
|
502
|
+
} else {
|
|
503
|
+
await next();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const middlewares = [
|
|
508
|
+
loggingMiddleware,
|
|
509
|
+
contextMiddleware,
|
|
510
|
+
authMiddleware
|
|
511
|
+
]
|
|
527
512
|
|
|
528
513
|
// Register handlers with middleware
|
|
529
514
|
if (isHono) {
|
|
530
515
|
const hndl = CreateTypedHonoHandlerWithContext<Ctx>();
|
|
531
|
-
hndl(app, MiddlewareTestApiDefinition, middlewareTestHandlers,
|
|
532
|
-
loggingMiddleware,
|
|
533
|
-
contextMiddleware,
|
|
534
|
-
authMiddleware
|
|
535
|
-
]);
|
|
516
|
+
hndl(app, MiddlewareTestApiDefinition, middlewareTestHandlers, middlewares);
|
|
536
517
|
} else {
|
|
537
|
-
RegisterHandlers(app, MiddlewareTestApiDefinition, middlewareTestHandlers,
|
|
538
|
-
loggingMiddleware,
|
|
539
|
-
contextMiddleware,
|
|
540
|
-
authMiddleware
|
|
541
|
-
]);
|
|
518
|
+
RegisterHandlers(app, MiddlewareTestApiDefinition, middlewareTestHandlers, middlewares);
|
|
542
519
|
}
|
|
543
520
|
}
|
|
544
521
|
|
package/tests/simple-api.test.ts
CHANGED
|
@@ -74,6 +74,39 @@ describe.each([
|
|
|
74
74
|
|
|
75
75
|
expect(result).toBe('pong');
|
|
76
76
|
});
|
|
77
|
+
|
|
78
|
+
test('should set response headers', async () => {
|
|
79
|
+
// Direct HTTP call to test header setting
|
|
80
|
+
const response = await fetch(`${baseUrl}/api/v1/public/ping`);
|
|
81
|
+
const contentType = response.headers.get('content-type');
|
|
82
|
+
|
|
83
|
+
// Check that standard headers are set
|
|
84
|
+
if (serverName === 'Express') {
|
|
85
|
+
expect(contentType).toBe('application/json; charset=utf-8');
|
|
86
|
+
} else {
|
|
87
|
+
// Hono sets content-type differently
|
|
88
|
+
expect(contentType).toBe('application/json');
|
|
89
|
+
}
|
|
90
|
+
expect(response.status).toBe(200);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('should set custom headers', async () => {
|
|
94
|
+
// Direct HTTP call to test custom header setting
|
|
95
|
+
const response = await fetch(`${baseUrl}/api/v1/public/custom-headers`);
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
|
|
98
|
+
// Check response data (unified response format)
|
|
99
|
+
expect(data.data).toEqual({ message: "headers set" });
|
|
100
|
+
expect(data).not.toHaveProperty('error'); // Error should not be present for success responses
|
|
101
|
+
expect(response.status).toBe(200);
|
|
102
|
+
|
|
103
|
+
// Check custom headers
|
|
104
|
+
const customHeader = response.headers.get('x-custom-test');
|
|
105
|
+
const anotherHeader = response.headers.get('x-another-header');
|
|
106
|
+
|
|
107
|
+
expect(customHeader).toBe('test-value');
|
|
108
|
+
expect(anotherHeader).toBe('another-value');
|
|
109
|
+
});
|
|
77
110
|
});
|
|
78
111
|
|
|
79
112
|
describe('Private API', () => {
|