Skip to content

A server in ten lines

A UDC server is a directory of collections and a few lines that serve it. This one keeps three books in memory; swap kind: memory for Postgres, a directory of files or an HTTP API later without touching the server.

  1. Install the server, the protocol package and Fastify:

    Terminal window
    mkdir books-server && cd books-server && npm init -y && npm pkg set type=module
    npm install @jsudc/server @jsudc/core fastify zod

    npm also installs drizzle-orm and pg, the server’s other peers; a memory collection never opens a connection.

  2. Lay out one collection, books:

    • server.ts
    • Directorycollections/
      • Directorybooks/
        • schema.json the fields
        • view.yaml how the data view shows it
        • source.yaml where the rows live
        • seed.json the rows a memory collection starts with
    collections/books/schema.json
    {
    "version": "v1",
    "core_fields": [],
    "fields": {
    "title": { "type": "string", "required": true },
    "author": { "type": "string" },
    "year": { "type": "integer", "min": 0 },
    "pages": { "type": "integer", "min": 1 },
    "read": { "type": "bool" }
    },
    "field_order": ["title", "author", "year", "pages", "read"]
    }
    collections/books/view.yaml
    default_renderer: table
    available_renderers: [table, cards]
    search:
    fields: [title, author]
    columns:
    - { field: title, width: 260 }
    - { field: author, width: 200 }
    - { field: year, width: 90, align: right }
    - { field: pages, width: 90, align: right }
    - { field: read, width: 80, align: center }
    card_options:
    title_field: title
    subtitle_field: author
    collections/books/source.yaml
    kind: memory
    collections/books/seed.json
    [
    {
    "title": "The Master and Margarita",
    "author": "Mikhail Bulgakov",
    "year": 1967,
    "pages": 384,
    "read": true
    },
    {
    "title": "The Bridge on the Drina",
    "author": "Ivo Andrić",
    "year": 1945,
    "pages": 314,
    "read": true
    },
    {
    "title": "Dictionary of the Khazars",
    "author": "Milorad Pavić",
    "year": 1984,
    "pages": 338,
    "read": false
    }
    ]
  3. The server:

    server.ts
    import { createDispatcher, loadRegistry, makeSource, udcPlugin } from "@jsudc/server";
    import Fastify from "fastify";
    const registry = loadRegistry("./collections", (asset, registry) =>
    makeSource(asset, { registry, env: process.env }),
    );
    const app = Fastify({ logger: true });
    const dispatcher = createDispatcher(registry, { log: app.log });
    await app.register(udcPlugin, { dispatcher, registry, swagger: false });
    await app.listen({ port: 5100 });

    loadRegistry reads every directory under collections/, validates its three files and fails at start on a mistake; makeSource turns each source.yaml into its source. udcPlugin mounts POST /udc/<action> on your own Fastify instance.

  4. Run it and ask for the books:

    Terminal window
    node server.ts
    curl -s localhost:5100/udc/list -H 'content-type: application/json' \
    -d '{"collection":"books","sort":[{"field":"year","dir":"asc"}]}'
    {
    "kind": "udc.list.ok",
    "id": "",
    "payload": {
    "rows": [
    { "__meta": { "id": "…", "version": 1 }, "title": "The Bridge on the Drina", "year": 1945, … },
    …
    ],
    "next_cursor": null,
    "total": 3
    }
    }

Every answer is HTTP 200; the envelope’s kind says ok or err. Try describe with {"collection":"books"} to see what a client learns before it draws anything, or mutate to add a fourth book: a memory collection keeps it until the process ends.

  • The OpenAPI document of every action at /docs: drop swagger: false and install @fastify/swagger @fastify/swagger-ui fastify-type-provider-zod.
  • Draw the books: a React data view pointed at this server.
  • Keep them in Postgres: the full stack.