Skip to content

Commit

Permalink
Add wrapper middlewares
Browse files Browse the repository at this point in the history
  • Loading branch information
ragokan committed Sep 25, 2024
1 parent 8141f7e commit 50edf34
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 79 deletions.
1 change: 1 addition & 0 deletions packages/server/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ await Bun.build({
"./src/matchers/constants.ts",
"./src/handlers/cors.ts",
"./src/handlers/static.ts",
"./src/middleware/cacheMiddleware.ts",
],
outdir: "./dist",
minify: true,
Expand Down
15 changes: 7 additions & 8 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,15 @@
"require": "./dist/handlers/static.js",
"default": "./dist/handlers/static.js",
"types": "./dist/handlers/static.d.ts"
},
"./cacheMiddleware": {
"import": "./dist/middleware/cacheMiddleware.js",
"require": "./dist/middleware/cacheMiddleware.js",
"default": "./dist/middleware/cacheMiddleware.js",
"types": "./dist/middleware/cacheMiddleware.d.ts"
}
},
"keywords": [
"bunicorn",
"server",
"fast",
"edge",
"bun",
"node"
],
"keywords": ["bunicorn", "server", "fast", "edge", "bun", "node"],
"peerDependencies": {
"valibot": "^0.41.0",
"zod": "^3.23.8"
Expand Down
70 changes: 53 additions & 17 deletions packages/server/src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,45 @@ export class BunicornApp<
>;
}

public async executeMiddlewaresAndHandler(
context: BunicornContext,
middlewares: Array<
(
context: BunicornContext,
next: () => Promise<Response>,
) => Promise<Response | void | Record<string, unknown>>
>,
handler: (context: BunicornContext) => Promise<Response | void>,
): Promise<Response> {
let index = 0;

async function next(): Promise<Response> {
if (index < middlewares.length) {
const middleware = middlewares[index]!;
index++;
const result = await middleware(context, next);
if (result instanceof Response) {
return result;
}
if (result && typeof result === "object") {
Object.assign(context, result);
}
return await next();
}
const result = await handler(context);
if (result instanceof Response) {
return result;
}

return Response.json(
{ message: "No Response is returned from handlers.", status: 500 },
{ status: 500 },
);
}

return await next();
}

protected async useRoute(
request: Request,
url: string,
Expand All @@ -97,28 +136,25 @@ export class BunicornApp<
route,
);

const { middlewares, handler } = route;
const middlewaresLength = middlewares.length;
const middlewares = route.middlewares;

for (let i = 0; i < middlewaresLength; i++) {
const result = await middlewares[i]!(context);
if (result instanceof Response) {
return result;
}
if (result) {
Object.assign(context, result);
// Ensure that the handler is only called once
let handlerCalled: boolean | undefined;
let handlerResult: Response | undefined;
async function handler(context: BunicornContext) {
if (handlerCalled) {
return handlerResult;
}
handlerCalled = true;
return (handlerResult = await route.handler(context));
}

const result = await handler(context);
if (result instanceof Response) {
return result;
}

return Response.json(
{ message: "Internal Server Error", status: 500 },
{ status: 500 },
const response = await this.executeMiddlewaresAndHandler(
context,
middlewares,
handler,
);
return response;
} catch (error) {
if (error instanceof BunicornError) {
return new Response(error.toString(), {
Expand Down
43 changes: 26 additions & 17 deletions packages/server/src/development.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
import { z } from "zod";
import { BunicornApp } from "./index.ts";
import { BunicornApp, createMiddleware } from "./index.ts";
import { cacheMiddleware } from "./middleware/cacheMiddleware.ts";
import { Router } from "./router/base.ts";

const rb = new Router().output(z.object({ message: z.string() }));
const loggerMiddleware = createMiddleware(async (_, next) => {
console.log("Request received.");
const response = await next();
console.log("Response sent with status:", response.status);
return response;
});

const countMiddleware = createMiddleware(async () => {
console.log("Count middleware called.");
return { count: 3 };
});

rb.input(z.object({ message: z.string() })).post("/hey", (ctx) => {
return ctx.json({ message: "" });
const ageMiddleware = createMiddleware(async () => {
console.log("Age middleware called.");
return { age: 5 };
});

const getHelloMessage = new Router()
.output(z.object({ msg: z.string() }))
.get("/:id", async (ctx) => {
return ctx.json({
msg: `Hello ${ctx.params.id}!`,
});
const route = new Router()
.use(loggerMiddleware)
.use(cacheMiddleware({ by: (ctx) => ctx.req.url }))
.use(countMiddleware)
.use(ageMiddleware)
.get("/", (ctx) => {
console.log("Handler called.");
return ctx.json({ message: `Hello World! ${ctx.count} ${ctx.age}` });
});

const app = new BunicornApp().addRoute(getHelloMessage).addRoute(
new Router().get("/", (ctx) => {
return ctx.json({ message: "Hello World!" });
}),
);
const app = new BunicornApp().addRoute(route);

app.serve({
port: 8080,
});
console.log("Server started on port 8080");
console.log("Server started on http://localhost:8080/");
2 changes: 1 addition & 1 deletion packages/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export * from "./app/index.ts";
export * from "./error/index.ts";
export * from "./handlers/index.ts";
export * from "./context/base.ts";
export * from "./middleware.ts";
export * from "./middleware/index.ts";
export * from "./router/base.ts";
export * from "./router/group.ts";
export * from "./router/types.ts";
Expand Down
12 changes: 0 additions & 12 deletions packages/server/src/middleware.ts

This file was deleted.

13 changes: 13 additions & 0 deletions packages/server/src/middleware/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { BunicornContext } from "../context/base.ts";
import type { BasePath } from "../router/types.ts";

export type BaseMiddleware<TPath extends BasePath = BasePath, TResult = any> = (
ctx: BunicornContext<TPath>,
next: () => Promise<Response>,
) => TResult | Promise<TResult>;

export function createMiddleware<TResult>(
cb: (ctx: BunicornContext, next: () => Promise<Response>) => TResult,
) {
return cb;
}
25 changes: 25 additions & 0 deletions packages/server/src/middleware/cacheMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { BunicornContext } from "../context/base.ts";
import { createMiddleware } from "./base.ts";

interface Args<MergeContext extends object> {
by: (ctx: BunicornContext & MergeContext) => string | void;
}

export function cacheMiddleware<MergeContext extends object = {}>({
by,
}: Args<MergeContext>) {
const cache = new Map<unknown, Response>();
return createMiddleware(async (ctx, next) => {
const key = by(ctx as BunicornContext & MergeContext);
if (!key) {
// No key given, just continue;
return;
}
if (cache.has(key)) {
return cache.get(key)!.clone();
}
const response = await next();
cache.set(key, response.clone());
return response.clone();
});
}
1 change: 1 addition & 0 deletions packages/server/src/middleware/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./base.ts";
2 changes: 1 addition & 1 deletion packages/server/src/router/base.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it } from "bun:test";
import * as v from "valibot";
import type { BunicornContext } from "../context/base.ts";
import { createMiddleware } from "../middleware.ts";
import { createMiddleware } from "../middleware/index.ts";
import type { BunicornSchema } from "../validation/types.ts";
import { Router } from "./base.ts";

Expand Down
24 changes: 6 additions & 18 deletions packages/server/src/router/base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { BaseSchema as vBaseSchema } from "valibot";
import type { BunicornContext } from "../context/base.ts";
import type { BaseMiddleware } from "../middleware.ts";
import type { BaseMiddleware } from "../middleware/index.ts";
import type {
BunicornSchema,
__InferBunicornInput,
Expand Down Expand Up @@ -35,12 +35,15 @@ export class Router<
}

public use<TResult extends object | void | Promise<object> | Promise<void>>(
cb: (context: BunicornContext & TContextResults) => TResult,
cb: (
context: BunicornContext & TContextResults,
next: () => Promise<Response>,
) => TResult,
) {
const newBuilder = this.copy();
newBuilder.route.middlewares!.push(cb as unknown as BaseMiddleware);
type Result = Awaited<TResult>;
type NewContext = Result extends void
type NewContext = Result extends void | Response
? TContextResults
: TContextResults & Result;
return newBuilder as unknown as Router<NewContext, TInput, TOutput>;
Expand Down Expand Up @@ -199,18 +202,3 @@ export class Router<
}

export { Router as RB };

// add global parsers
// add cache to getText -> getJson
// then create middleware like
/*
cache(fn, { by: (ctx) => [ctx.params.id], ttl: 1000 })
cacheAsync(fn, { by: (ctx) => [ctx.params.id], ttl: 1000 })
*/
// function wrap<TContext extends BunicornContext<any, any>, Out>(
// cb: (ctx: TContext) => Out
// ) {
// return (ctx: TContext): Out => {
// return cb(ctx);
// };
// }
2 changes: 1 addition & 1 deletion packages/server/src/router/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BaseMiddleware } from "../middleware.ts";
import type { BaseMiddleware } from "../middleware/index.ts";
import type { BunicornSchema, __ValidateOptions } from "../validation/types.ts";
import type { BaseMethod, BasePath, __MergePaths } from "./types.ts";

Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/router/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BaseMiddleware } from "../middleware.ts";
import type { BaseMiddleware } from "../middleware/index.ts";
import type { Route } from "./route.ts";

export type BasePath = `/${string}`;
Expand Down
11 changes: 8 additions & 3 deletions packages/server/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["./src"],
"exclude": ["node_modules", "./node_modules"]
}
"include": [
"./src",
],
"exclude": [
"node_modules",
"./node_modules"
]
}

0 comments on commit 50edf34

Please sign in to comment.