Files
skills/docs/api-stack/api/api-plugin-events/src/schemas/event-schemas.md
2026-05-29 08:20:56 +01:00

3.7 KiB

event-schemas

Purpose

The package-local Zod schemas for the event entity surface. Owns every input / output shape touching the events routes and repositories: the persisted EventSchema, the create / update request bodies, the list-query parameters, the path param shape, the EventLocationSchema value object, and the EventStatusSchema 0..2 integer encoding. The @suaveplan/api-schemas package ships only event-bus envelope schemas (DomainEvent, EventEnvelope, EventMetadata); the event-management entity schemas are owned here because no other package needs them.

The schemas double as the type sources — every exported *Schema has a matching type Foo = z.infer<typeof FooSchema> so consumers get a single source of truth for both runtime validation and compile-time narrowing. The CreateEventSchema and UpdateEventSchema carry a .refine() constraint that asserts end_time > start_time at the parser boundary so the repository never sees a malformed time pair.

Features

  • 11 schemas spanning persistence, request, response, and value objects.
  • CreateEventSchema / UpdateEventSchema enforce end_time > start_time via .refine().
  • EventStatusSchema constrains status to 0..2 (draft / active / cancelled) matching the upstream encoding.
  • EventQuerySchema coerces query-string limit / offset / upcoming / past to typed numbers / booleans.
  • EventLocationSchema validates (lat, lng) coordinates against geographic bounds.

Usage: Basic Example

import { CreateEventSchema, EventSchema } from "@suaveplan/api-plugin-events";

const parsed = CreateEventSchema.parse({
    title: "Meetup",
    start_time: "2026-06-01T18:00:00Z",
    end_time: "2026-06-01T22:00:00Z",
});
console.log(parsed.timezone); // "UTC"

Usage: Advanced Example

import {
    EventQuerySchema,
    EventListResponseSchema,
    UpdateEventSchema,
} from "@suaveplan/api-plugin-events";

// Coerce from URL search params
const url = new URL("https://api.example.com/events?limit=10&past=true");
const query = EventQuerySchema.parse(Object.fromEntries(url.searchParams));

// Build a typed response payload
const body = EventListResponseSchema.parse({
    data: [
        /* events from repository */
    ],
    total: 42,
});

// Forbid time inversion at the boundary
const result = UpdateEventSchema.safeParse({
    start_time: "2026-06-01T22:00:00Z",
    end_time: "2026-06-01T18:00:00Z",
});
if (!result.success) {
    // result.error.issues[0].path === ["end_time"]
}

API Reference

  • EventStatusSchema: z.ZodNumber — integer 0..2.
  • EventLocationSchema: z.ZodObject — { name, address?, coordinates? }.
  • AttendeeCountSchema: z.ZodObject — { going, maybe, invited }.
  • EventSchema: z.ZodObject — the persisted event row.
  • CreateEventSchema: z.ZodEffects<...> — POST / body.
  • UpdateEventSchema: z.ZodEffects<...> — PUT /{id} body.
  • EventQuerySchema: z.ZodObject — GET / query params.
  • EventIdParamSchema: z.ZodObject — { id: string }.
  • EventListResponseSchema: z.ZodObject — GET / response.

Every schema exports a sibling type Foo = z.infer<typeof FooSchema>.

Implementation Notes

status is encoded as an integer (0=draft, 1=active, 2=cancelled) rather than a string enum to match the upstream SurrealDB schema and to keep label cardinality low when emitted as OTel attributes. The two cross-field .refine() rules (end_time > start_time) are duplicated on CreateEventSchema and UpdateEventSchema rather than factored to a shared helper because the UpdateEventSchema variant must tolerate a single-field update ({ title }) without raising the refinement, which means the rule fires only when both start_time and end_time appear in the same payload.