Benchmarks

Heavy Validation

Performance benchmarks for an Update User endpoint with heavy validation and serialization.

These benchmarks are based on the Fastify benchmarks repo. Our goal is to perform similarly to Fastify as we consider it the industry standard in terms of performance.

Update User benchmark involves heavy validation and serialization on top of routing. It showcases the a performance comparing of frameworks like Zod and TypeBox vs @mionjs/tun-types

What is tested?

The test consists of an updateUser request with a complex User model (~1KB payload) that includes:

export interface User {
  id: number;
  username: string;
  email: string;
  profile: {
    firstName: string;
    lastName: string;
    displayName: string;
    bio?: string;
    avatarUrl?: string;
    dateOfBirth: Date;
  };
  role: "admin" | "user" | "guest" | "moderator";
  status: "active" | "suspended" | "pending_verification" | "deactivated";
  address: Address;
  paymentMethods: PaymentMethod[]; // Discriminated union
  preferences: UserPreferences;
  createdAt: Date;
  updatedAt: Date;
  lastLoginAt?: Date;
  tags: string[];
}

// ### mion ###
// the received user by the route is already validated and deserialized
// all Date fields are already JS Date objects (not strings from JSON.parse)
export const routes: Routes = {
  updateUser: (ctx, user: User): User => {
    user.updatedAt = new Date();
    user.lastLoginAt = new Date();
    user.profile.displayName = `${user.profile.firstName} ${user.profile.lastName.charAt(0)}.`;
    return user;
  },
};

// ### Other frameworks (Express, Fastify, Hono, etc.) ###
// Use Zod schemas for validation and date coercion
const UserSchema = z.object({
  id: z.number(),
  username: z.string(),
  // ... full schema with nested objects, discriminated unions, etc.
  createdAt: z.coerce.date(),
  updatedAt: z.coerce.date(),
  // ...
});

app.post("/updateUser", function (req, res) {
  const user = UserSchema.parse(req.body); // Validates + deserializes dates
  user.updatedAt = new Date();
  user.lastLoginAt = new Date();
  user.profile.displayName = `${user.profile.firstName} ${user.profile.lastName.charAt(0)}.`;
  res.json(user);
});

Notes

In this benchmark the requests take considerably more time compared with the 'hello world' benchmark. This is mostly because each request is spending extra time on validation and serialization.

For this specific test, the performance of the libraries used for validation and serialization might be more important than the routing itself.

We can observe the more validation and serialization is required, the more performant mion becomes, this is due to it's highly optimized JIT RunTypes functions.

Benchmarks

  • Machine: darwin arm64 | 12 vCPUs | 16.0GB Mem
  • Node: v24.13.0
  • Run: Wed Apr 15 2026 22:00:02 GMT+0100 (Irish Standard Time)
  • Method: autocannon -c 100 -d 20.102586 -p 1 localhost:3000 (two rounds; one to warm-up, one to measure)

Req (R/s)

Throughput (Mb/s)

Latency (ms)

Max Memory (Mb)

Memory Series (MB)

Results Table

VersionRouterReq (R/s)Latency (ms)Output (Mb/s)Max Memory (Mb)Max Cpu (%)ValidationDescription
mion.bun0.6.249225.91.9550.7470101mion using bun, automatic validation and serialization
hono.bun3.12.648727.11.9648.0485101hono bun server with Zod validation
elysia.bun1.0.040668.82.3540.10100101Elysia framework with TypeBox validation
mion0.6.237747.52.6040.60152116Automatic validation and serialization out of the box
http-node16.18.036701.82.6838.36144106bare node http server with Zod validation
fastify5.7.432177.03.0533.66269125Fastify with native JSON Schema validation
express5.2.128152.43.5329.42162109Express with Zod validation
hapi21.4.425700.53.8626.86226112Hapi with Zod validation
hono3.12.623521.24.7324.25614120hono node server with Zod validation
Benchmarks Repo