Help us measure site usage?

With your permission, we use self-hosted analytics to understand page usage. It is optional and does not affect the Service. Learn more.

TypeScriptintermediatebackendAI generated

Typed Event API

Build an API in TypeScript that records events (such as user.signed_up, order.placed or payment.failed) where each event type has its own data shape, checked at runtime with Zod and fully typed in the code, so an order event without an amount is rejected with a clear error. Clients send events one at a time or in batches, can safely resend them without creating duplicates, and can query them by type, time range and fields, page by page. A small React page lets you browse and filter events. You'll learn discriminated unions in TypeScript and Zod, sharing types between server and client, idempotent writes, cursor-based pagination, building a Node.js API with validation, and testing.

Estimate
~12.5h
Steps
6
Completed by
0
Proposed by
codeseed.app

Node.js · Fastify · Zod · React

Project roadmap

  1. 01

    Define the event types

    ~2h

    Write the event schemas with Zod as a discriminated union on the type field: common fields (id chosen by the client, type, occurredAt and source) plus a data object whose shape depends on the type (for example order.placed has orderId, amount in cents and currency). Derive TypeScript types with z.infer, and put them in a shared module. Write API.md with POST /events, POST /events/batch and GET /events. Done when: the schemas and API.md cover every event type and endpoint.

  2. 02

    Build the ingestion endpoints

    ~2.5h

    Build a Node.js server (with Fastify or Express) with POST /events and POST /events/batch (up to 500 events), validating with the Zod schemas and returning per-event errors with the path of each problem. Store events in SQLite or PostgreSQL with the data as JSON, and use the client's event id as a unique key, so resending an event is accepted but not stored twice. Done when: valid events are stored, invalid ones get precise errors, and duplicates are ignored.

  3. 03

    Query events

    ~2.5h

    Add GET /events with filters for type, source, a time range, and a field in the data (for example data.orderId), validated with Zod. Use cursor-based pagination (a cursor encoding the last event's time and id) instead of offsets, so pages stay correct while new events arrive. Add indexes for the filters. Done when: filtered queries return the right events, and paging through a busy stream never skips or repeats events.

  4. 04

    Handle bad input and limits

    ~1.5h

    Limit request body size and batch size, reject events far in the future, handle a batch where some events are invalid (store the valid ones and report the rest, or reject all, as an option), and return consistent error responses. Log each request with its counts and duration. Done when: every kind of bad input gets a clear error, and limits apply.

  5. 05

    Build the event browser

    ~2h

    Build a React page (with Vite) that uses the shared types to show events in a table with filters for type, source and time, loads more with the cursor, and shows each event's data formatted by its type. Done when: the browser shows and filters events, and loads more pages smoothly.

  6. 06

    Test it

    ~2h

    Write Vitest tests: each event schema with valid and invalid data, ingestion with duplicates and partially invalid batches, filters, cursor pagination while inserting new events, and type tests with expectTypeOf checking the inferred types. Done when: npm test passes.

Ready to build this?

Get a GitHub repo and start building. Your AI reviewer checks each step as you go.

~12.5h · 6 steps

Tech stack

Node.jsFastifyZodReact