Skip to content

Instantly share code, notes, and snippets.

@BashkaMen
Created May 23, 2025 21:05
Show Gist options
  • Select an option

  • Save BashkaMen/a1111eef29f2526b926022d83ff0ebad to your computer and use it in GitHub Desktop.

Select an option

Save BashkaMen/a1111eef29f2526b926022d83ff0ebad to your computer and use it in GitHub Desktop.
import { randomUUIDv7 } from 'bun';
import { Map, List, Set, set, } from 'immutable'
import z from 'zod';
type HttpMethod = "GET" | "POST"
type Request = { body: string; url: string; method: HttpMethod; headers: Map<string, string> }
type Response = { body: string; url: string, method: HttpMethod; headers: Map<string, string> }
type Context = {
request: Request;
response: Response;
}
type Middleware = (context: Context) => Promise<Context | null>
export const skip : Middleware = (context) => Promise.resolve(null)
export const next : Middleware = (context) => Promise.resolve(context)
export const map_response = (fn: (response: Response) => Response) : Middleware => async (ctx) => {
const reponse = fn(ctx.response)
return { ...ctx, response: reponse }
}
export const set_header = (name: string, value: string) : Middleware =>
map_response(res => ({ ...res, headers: res.headers.set(name, value) }))
export const set_body = (body: string) : Middleware =>
map_response(res => ({ ...res, body }))
export const path = (path: string) : Middleware => (ctx) => {
if (ctx.request.url === path) {
return next(ctx)
}
return skip(ctx)
}
export const method = (method: HttpMethod) : Middleware => (ctx) => {
if (ctx.request.method === method) {
return next(ctx)
}
return skip(ctx)
}
export const compose = (ms: Middleware[]): Middleware => async (ctx) => {
let current : Context | null = ctx
for (const m of ms) {
current = await m(current)
if (!current) {
return await skip(ctx)
}
}
return await skip(ctx)
}
export const choose = (ms: Middleware[]) : Middleware => async (ctx) => {
for (const m of ms) {
const ctx1 = await m(ctx)
if (ctx1) {
return ctx1
}
}
return ctx
}
export const route = (http_method: HttpMethod, http_path: string) : Middleware => compose([
method(http_method),
path(http_path),
])
export const set_text_body = (text: string) : Middleware => compose([
set_header("Content-Type", "text/plain"),
set_body(text),
])
export const set_json_body = (json: any) : Middleware => set_json_body(JSON.stringify(json))
export const bind_json = <T>(fn: (item: T) => Middleware) : Middleware => async (ctx) => {
const json = JSON.parse(ctx.request.body)
return fn(json)(ctx)
}
export const typed_json_handler =
<TReq, TRes>(opts: {
handler: (req: TReq) => Promise<TRes>,
validate?: (req: TReq) => void
}) : Middleware =>
bind_json((req: TReq) => async (ctx) => {
opts.validate?.(req)
const res = await opts.handler(req)
return set_json_body(res)(ctx)
})
export const authorize = (roles: string[] = []) : Middleware => (ctx) => {
const auth_header = ctx.request.headers.get("Authorization")
// decode jwt
if (roles.length === 0) {
return next(ctx)
}
if (roles.some(role => auth_header?.includes(role))) {
return next(ctx)
}
return skip(ctx)
}
type Endpoint<TReq, TRes> = {
method: HttpMethod,
path: string,
handler: (item: TReq) => Promise<TRes>
validate?: (item: TReq) => Promise<TRes>
}
const schema = z.object({
email: z.string().email(),
})
type ZodEndpoint<TReq, TRes> = {
method: HttpMethod,
path: string,
schema: z.Schema
handler: (item: TReq) => Promise<TRes>
}
export const endpoint = <TReq, TRes>(endpoint: Endpoint<TReq, TRes>) : Middleware => (ctx) => {
const item = JSON.parse(ctx.request.body)
endpoint.validate?.(item)
const res = endpoint.handler(item)
return set_json_body(res)(ctx)
}
type CreateUserReq = { email: string }
export const create_user = compose([
method("POST"),
path("/user/:create"),
bind_json((req: CreateUserReq) => async (ctx) =>{
const user = { id: randomUUIDv7(), email: req.email, created_at: new Date().valueOf() }
//await db.insert(user)
return ctx
})
])
type CreateOrder = { type: string }
type CreateOrderReponse = { type: string }
export const create_order = compose([
method("POST"),
path("/user/:create"),
authorize(),
typed_json_handler<CreateOrder, CreateOrderReponse>({
handler: async (req: CreateOrder) => {
return { type: req.type}
}
})
])
const create_user_endpoint : Endpoint<CreateUserReq, void> = {
method: "POST",
path: "/user/:create",
handler: async (req: CreateUserReq) => {
const user = { id: randomUUIDv7(), email: req.email, created_at: new Date().valueOf() }
//await db.insert(user)
return;
}
}
const app = compose([
set_header("Server", "fs-server"),
choose([
compose([ path("/"), method("GET"), set_header("Content-Type", "text/html"), set_body("<h1>Home</h1>") ]),
compose([route("GET", "/hello"), set_header("Content-Type", "text/html"), set_body("Hello World")]),
create_user,
create_order,
endpoint(create_user_endpoint),
])
])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment