Skip to main content

Benchmark

We compared Cog (1.0.8), Hono (4.7.10) & @hono/node-server (1.14.1) and Express (5.1.0) using autocannon with the following code:

import autocannon from "autocannon";

const PORT = 3000;

const routes = [
"/",
"/very/deeply/nested/route/hello/there/admin/dashboard/ping",
"/hello?name=John",
"/admin",
"/admin/set-token",
"/parse-body"
] as const;

for (const route of routes) {
console.log(`\n--- Benchmarking ${route} ---`);
const result = await autocannon({
url: `http://127.0.0.1:${PORT}${route}`,
connections: 800,
duration: 10,
method: route === "/parse-body" ? "POST" : "GET",
headers:
route === "/parse-body"
? { "Content-Type": "application/json" }
: { cookie: "sessionId=abc123; otherCookie=value" },
body: route === "/parse-body" ? JSON.stringify({ message: "Hello!" }) : undefined
});

console.log(`Completed benchmark for ${route}`);
console.log(`Requests/sec: ${result.requests.average}`);
console.log(`Latency (avg): ${result.latency.average} ms`);
console.log(`Errors: ${result.errors}`);
}

Results

RouteFrameworkRequests/secAvg Latency (ms)Errors
/Hono62738.9112.240
Cog58119.2813.250
Express14930.5539.53221
/very/deeply...Hono60949.8212.670
Cog5406414.240
Express14926.9140.22202
/hello?name=JohnHono61613.112.480
Cog5355214.430
Express14665.8238.37259
/adminHono41214.5518.920
Cog55128.7314.030
Express1439638.57264
/admin/set-tokenHono39992.7319.510
Cog51946.1914.950
Express14106.5538.93269
/parse-bodyHono27158.428.960
Cog50171.6415.420
Express1300439.98291

Summary

The benchmark shows that Cog performs significantly better than Express in all tested routes, with much higher requests per second, lower latency, and zero errors. While Hono outperforms Cog in raw speed and latency, Cog remains a fast and reliable framework considering its simplicity and minimal footprint. However, Cog is still experimental and not recommended for production use. Use it only for learning, experimentation, or lightweight projects, and avoid deploying it in critical production environments.

caution

All benchmarks were run locally with Node.js v22.11.0 on a MacBook Air M1. Also, these tests were conducted by someone whose benchmarking skills are... let’s say, “enthusiastic beginner,” so results may differ if rerun or done by a pro.

Apps code

Cog app

import { Cog } from "cog-http";

const app = new Cog();

app.use("*", (_req, res, next) => {
res.set("X-Powered-By", "Cog");
next();
});

app.get("/", (_req, res) => {
res.send("Hello from Cog!");
});

app.get("/very/deeply/nested/route/hello/there/admin/dashboard/ping", (_req, res) => {
res.send("Pong!");
});

app.get("/hello", (req, res) => {
const { name } = req.query;
res.send(`Hello, ${name || "user"}!`);
});

app.group("/admin", (admin) => {
admin.get("/", (req, res) => {
const { token } = req.cookies;

if (token === "my_token_12345") {
res.clearCookie("token");
return res.send({ message: "Secret info" });
}

res.send("Unauthenticated", 403);
});

admin.get("/set-token", (_req, res) => {
res.setCookie("token", "my_token_12345");
res.send("Token set!");
});
});

app.post("/parse-body", (req, res) => {
const body = req.body;
res.send(body);
});

app.listen(3000, "127.0.0.1", () => {
console.log("Listening on 127.0.0.1:3000");
});

Hono app

import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { getCookie, setCookie, deleteCookie } from "hono/cookie";

const app = new Hono();

app.use("*", async (c, next) => {
c.header("X-Powered-By", "Hono");
await next();
});

app.get("/", (c) => c.text("Hello from Hono!"));

app.get("/very/deeply/nested/route/hello/there/admin/dashboard/ping", (c) => c.text("Pong!"));

app.get("/hello", (c) => {
const name = c.req.query("name") || "user";
return c.text(`Hello, ${name}!`);
});

const admin = new Hono();

admin.get("/", (c) => {
const token = getCookie(c, "token");
if (token === "my_token_12345") {
deleteCookie(c, "token");
return c.json({ message: "Secret info" });
}
return c.text("Unauthenticated", 403);
});

admin.get("/set-token", (c) => {
setCookie(c, "token", "my_token_12345");
return c.text("Token set!");
});

app.route("/admin/*", admin);

app.post("/parse-body", async (c) => {
const body = await c.req.json();
return c.json(body);
});

serve({
fetch: app.fetch,
hostname: "127.0.0.1",
port: 3002
});
console.log("Listening on 127.0.0.1:3002");

Express app

import express from "express";
import cookieParser from "cookie-parser";

const app = express();

app.use(cookieParser());
app.use(express.json() as express.RequestHandler);

app.use((_req, res, next) => {
res.set("X-Powered-By", "Express");
next();
});

app.get("/", (_req, res) => {
res.send("Hello from Express!");
});

app.get("/very/deeply/nested/route/hello/there/admin/dashboard/ping", (_req, res) => {
res.send("Pong!");
});

app.get("/hello", (req, res) => {
const { name } = req.query;
res.send(`Hello, ${name || "user"}!`);
});

const adminRouter = express.Router();

adminRouter.get("/", (req, res) => {
const { token } = req.cookies;
if (token === "my_token_12345") {
res.clearCookie("token");
res.status(200).send({ message: "Secret info" });
return;
}
res.status(403).send("Unauthenticated");
});

adminRouter.get("/set-token", (_req, res) => {
res.cookie("token", "my_token_12345");
res.send("Token set!");
});

app.use("/admin", adminRouter);

app.post("/parse-body", (req, res) => {
res.send(req.body);
});

app.listen(3001, "127.0.0.1", () => {
console.log("Express listening on 127.0.0.1:3001");
});