Build HTTP with Bun
Build HTTP with Bun
Kacper Walczak · 25-10-2023
Learn how to build HTTP server with Bun.
It's very easy and fast.
You can use it in your projects.
Very fast HTTP server library based on Bun. Feel free to use it in your projects. Just copy the lib/src/http.
Bun-http GutHub repo (opens in a new tab)
Final usage
This is how you can use it in your project.
servemethod should be last method in chain.
import { http, Res } from "./lib/src/http";
const str = (val: any) => JSON.stringify(val);
http.get("/", (req) => {
return new Res(`Hello from home`);
});
http.get("/product/:productId", (req) => {
return new Res(`Product (id: ${req.params.productId})`);
});
http.post("/blog/create", (req) => {
return new Res(`
params: ${str(req.params)},
query: ${str(req.query)},
body: ${str(req.body)}
`);
});
const server = http.serve({ port: 3000 });
console.log(`Listening on port ${server.port}...`);
/* Example output
bun main.ts
[0.90ms] ".env"
Listening on port 3000...
8:27:48 PM: 200 GET /
8:27:52 PM: 405 GET /blog/123
8:27:56 PM: 200 POST /blog/123
8:27:58 PM: 200 GET /__endpoints
*/How to build it
- Create library folder
mkdir lib - Create
srcfoldermkdir lib/src - Create
http.tsfiletouch lib/src/http.ts - Create
index.tsfiletouch lib/index.ts - Create
tsconfig.jsonfiletouch lib/tsconfig.json - Create
package.jsonfiletouch lib/package.json - Create
README.mdfiletouch lib/README.md
Final folder structure
- package.json
- tsconfig.json
- index.ts
- README.md
- http.ts
Build http object
Http object will be our main object to build http server. It will have methods like get, post, put, etc. and serve method to start server.
router shouldn't be public. Consider change to class with private router.
// lib/src/http.ts
export type ServeOptions = { port?: number };
const defaultServeOptions: ServeOptions = {
port: 3000,
};
export const http = {
router: new Router(),
get(path: string, handler: (req: Req) => Res) {
this.router.endpoints.push({ path, handler, method: "GET" });
},
post(path: string, handler: (req: Req) => Res) {
this.router.endpoints.push({ path, handler, method: "POST" });
},
put(path: string, handler: (req: Req) => Res) {
this.router.endpoints.push({ path, handler, method: "PUT" });
},
delete(path: string, handler: (req: Req) => Res) {
this.router.endpoints.push({ path, handler, method: "DELETE" });
},
patch(path: string, handler: (req: Req) => Res) {
this.router.endpoints.push({ path, handler, method: "PATCH" });
},
serve(opts = defaultServeOptions) {
return Bun.serve({
port: opts.port,
fetch(req) {
return http.router.route(req);
},
error(err) {
return new Res(`<p>${err}\n${err.stack}</pre>`, {
headers: {
"Content-Type": "text/html",
},
});
},
});
},
};Build router class
Router should be able to handle request and route to proper endpoint. For example:
const router = new Router();
router.endpoints.push({ path: "/blog/:id", handler: (req) => {...} });
// request GET /blog/123
router.route(request); // { path: "/blog/:id", handler: (req) => {...} }export class Router {
endpoints: Endpoints = new Endpoints();
constructor() {
if (Bun.env.PRODUCTION == "false") {
// [DEV ONLY] Adds showcase page with all endpoints
this.endpoints.push(AdminEndpoints.showcase(this.endpoints));
}
}
// Request init from Bun
async route(req: Request) {
const url = new URL(req.url);
const path = url.pathname;
const query = url.searchParams;
const pathArr = path.split("/");
const log = (status: number) =>
reqLog({ status, method: req.method, path });
const endpoint = this.endpoints.getByPath(path);
if (!endpoint) {
log(404);
return new Res("Not found", { status: 404 });
}
if (req.method !== endpoint.method) {
log(405);
return new Res("Method not allowed", { status: 405 });
}
const request = new Req(req);
request.params = Params.fromPathArray(endpoint.pathArr ?? [], pathArr);
request.query = Object.fromEntries(query.entries());
if (req.method !== "GET" && req.method !== "HEAD") {
request.body = await req.json();
}
const response = endpoint.handler(request);
log(response.status);
return response;
}
}Create Endpoints
Endpoints should be able to find by path. For example:
const endpoints = new Endpoints();
endpoints.push({ path: "/blog/:id", handler: (req) => {...} });
endpoints.getByPath("/blog/123"); // { path: "/blog/:id", handler: (req) => {...} }export type Endpoint = {
path: string; // /blog/:id
handler: (req: Req) => Res;
pathArr?: (string | any)[]; // ['blog', ':id']
method?: string;
};
export class Endpoints extends Array<Endpoint> {
override push(...endpoints: Endpoint[]) {
endpoints.forEach((e) => (e.pathArr = e.path.split("/")));
return super.push(...endpoints);
}
override unshift(...endpoints: Endpoint[]) {
endpoints.forEach((e) => (e.pathArr = e.path.split("/")));
return super.unshift(...endpoints);
}
getByPath(path: string) {
const endpoint = this.find((e) => {
const pathArr = path.split("/");
if (e.path === path) return true;
if (!e.pathArr) return false;
if (e.pathArr.length !== pathArr.length) return false;
return e.pathArr.every((p, i) => {
if (p.startsWith(":")) {
return true;
}
return p === pathArr[i];
});
});
return endpoint;
}
}Flavor with Req, Res and Params
export class Req extends Request {
private _body: any = {};
public params: { [key: string]: string } = {};
public query: { [key: string]: string } = {};
get body(): any {
return this._body;
}
set body(val: any) {
this._body = val;
}
}
export class Res extends Response {}
const Params = {
fromPathArray(endpointPath: string[], requestPath: string[]) {
const params: { [key: string]: string } = {};
endpointPath.forEach((p, i) => {
if (!p.startsWith(":")) return;
params[p.slice(1)] = requestPath[i];
});
return params;
},
};Create showcase page
/**
* [SHOULD BE AVAILABLE DEV ONLY] Adds showcase page with all endpoints
*/
class AdminEndpoints {
static showcase(endpoints: Endpoint[]): Endpoint {
return {
path: "/__endpoints",
method: "GET",
handler: (req) =>
new Response(
"<h1>Endpoints:</h1>\n<ol>\n" +
endpoints
.filter((e) => !e.path.startsWith("/__"))
.map((e) => " <li>" + e.path + "</li>")
.sort()
.join("\n") +
"\n</ol>",
{
headers: {
"Content-Type": "text/html",
},
}
),
};
}
}Endpoints showcase
This endpoints list was generated for Final usage section.
When user writes http.get, http.post, etc. then should be able to visit localhost:3000/__endpoints to see all endpoints.
This page will look like this:

That's all. Now you can use your http library.
Consider to add few more features like:
- Add
http.headmethod - Add
middlewares - etc...
READ
Latest readings
Readings are sites which will help you with detailed
information about given topic. Read latest ones from Learn.
06-03-2026
Build your own local voice assistant powered by Ollama.
06-03-2026
Generate YouTube thumbnails with FastAPI and Ollama.
05-09-2024
Compare Neo4j and Tigergraph databases, which is easier to work with, etc.