RPC

Heavy Validation

Benchmarks for an Update User endpoint with heavy validation and JSON round trip.

A ~1 KB user update: heavy validation and the JSON round trip, on top of routing. It compares Zod and TypeBox with RunTypes.

See the code

Requests per Second

$ loading benchmark…

Throughput

$ loading benchmark…

Latency

$ loading benchmark…

Max Memory

$ loading benchmark…

Max CPU

$ loading benchmark…

Benchmark Code

Each request is an updateUser call with a complex User model.

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 ###
// user arrives validated and deserialized: Date fields are Date objects, not JSON strings
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, Hapi, Hono) ###
// Zod validates and coerces dates, compiled for speed
const UserSchema = z.compile(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);
  user.updatedAt = new Date();
  user.lastLoginAt = new Date();
  user.profile.displayName = `${user.profile.firstName} ${user.profile.lastName.charAt(0)}.`;
  res.json(user);
});

Reading the Results

Requests take much longer than in the 'hello world' benchmark, mostly because of validation and the JSON round trip.

Here, the validation and JSON round trip libraries can matter more than the routing.

The more validation and JSON round trip a request needs, the better mion performs, thanks to its optimized JIT RunTypes functions.
Copyright © 2026