JSPM

@ez-rpc/router

0.1.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 59
  • Score
    100M100P100Q9510F
  • License MIT

Type-safe Express router with Zod validation, in-flight deduplication, and concurrency queuing.

Package Exports

  • @ez-rpc/router

Readme

@ez-rpc/router

npm license

An Express router that validates inputs and outputs against your Zod schemas automatically. You write the handler logic — it handles the validation, error responses, deduplication, and queuing.

Install

npm install @ez-rpc/router express zod
npm install --save-dev @types/express

Usage

Define your endpoints as a plain object with Zod schemas, then pass them to createRouter:

// contract/user.ts
import { z } from "zod";
import type { Endpoint } from "@ez-rpc/core";

export const userEndpoints = {
  getUsers: {
    output: z.array(UserSchema),
  } satisfies Endpoint,
  createUser: {
    input: z.object({ name: z.string(), email: z.string().email() }),
    output: UserSchema,
  } satisfies Endpoint,
} as const;

// server/routes/user.ts
import { createRouter } from "@ez-rpc/router";
import { userEndpoints } from "../../contract/user";

export const userRouter = createRouter(userEndpoints, authMiddleware).implement({
  getUsers: {
    handler: async (_input, req) => getUsersFromDB(req.pool),
  },
  createUser: {
    handler: async (input, req) => createUserInDB(req.pool, input),
  },
});

app.use("/user", userRouter);

For each route, the router validates the request body before calling your handler and validates the return value before sending the response. A bad input is a 400. A schema mismatch on the output is a 500. Both use the same Zod schemas your client is typed against — so compile-time and runtime stay in agreement.

Concurrency queuing

Plug in a createConcurrencyQueue from @ez-rpc/concurrency to cap concurrent executions per route:

import { createConcurrencyQueue } from "@ez-rpc/concurrency";

const reportQueue = createConcurrencyQueue({ globalCap: 4, perUserCap: 1 });

const router = createRouter(reportEndpoints, authMiddleware).implement({
  generateReport: {
    handler: generateReportHandler,
    queue: { queue: reportQueue, key: (input) => input.projectId },
  },
});

Other features

  • In-flight deduplication — identical concurrent requests resolve from one promise instead of hitting your handler multiple times
  • NDJSON streaming — mark an endpoint streaming: true and write rows as they come; the client reassembles them
  • HMAC signature verification — validates signed requests from @ez-rpc/client
  • createDBServiceHandler — one-liner adapter for @ez-rpc/mssql services:
import { createDBServiceHandler } from "@ez-rpc/router";

const userRouter = createRouter(userEndpoints, authMiddleware).implement({
  getUsers: { handler: createDBServiceHandler(getUsersService) },
});

Full docs

See the ez-rpc README for streaming, signing, and more.