Building Real-Time Workspaces with Zero

How Epicat combines Postgres, Drizzle, and Rocicorp Zero for secure real-time workspace data.

Epicat uses PostgreSQL as its system of record, Drizzle for its schema, and Rocicorp Zero for reactive client reads.

Server-Authorized Queries

Zero forwards named queries to Epicat's API. The API verifies the signed Better Auth token and creates a trusted query context containing the current user ID. Every workspace query starts from that context and only traverses organizations where the user has a membership.

export const organizationProjects = defineQuery(({ args, ctx }) =>
  zql.member
    .where("userId", ctx?.userId ?? "")
    .where("organizationId", args.organizationId)
    .related("organization", (organization) => organization.related("projects"))
    .one(),
);

The browser never gets to choose its authenticated identity. Project writes go through the typed API, which repeats the organization-membership check before changing PostgreSQL.

One Schema Chain

The Drizzle schema generates the database migration and Zero schema. The Elysia API generates an OpenAPI document, and that document generates the @epicat/sdk client types. Keeping these artifacts generated makes drift visible during review instead of at runtime.

Why This Shape

  • PostgreSQL remains the durable source of truth.
  • Zero pushes committed changes to open project views without polling.
  • Authorization stays on the server at both read and write boundaries.
  • Shared generated types keep the web app, SDK, and API documentation aligned.
Insights

Related Posts

More writing on the decisions, workflows, and engineering ideas behind Epicat.