Declarative APIs
that build themselves.

hydro turns resource definitions into REST endpoints, validation, relationships, OpenAPI output and Scalar docs — zero boilerplate.

Resource definition

One file turns data into a full REST surface.

Drop a file into server/resources, export defineResource(), and Hydro wires the routes, validation and documentation at runtime.

  1. schema
    Describe the shape

    Zod validates request bodies and feeds the generated OpenAPI document.

  2. provider
    Read from anywhere

    Implement list/get against memory, SQL, KV, an external API, or your own repository layer.

  3. processor
    Own the writes

    Create, update and delete receive already-parsed input plus params, auth and request metadata.

  4. extras
    Add capabilities

    Filters, relationships, auth rules and custom operations layer onto the same resource definition.

server/resources/book.tsdefineResource

          import { defineResource } from '#hydro'import { z } from 'zod'export default defineResource({  name: 'Book',  path: 'books',  schema: z.object({    id: z.string().optional(),    title: z.string().min(1).max(200),    authorId: z.string().optional(),    available: z.boolean().default(true),  }),  filters: ['title', 'authorId'],  provider: {    async list(ctx) {      const { offset, itemsPerPage } = ctx.query.pagination      return {        items: books.slice(offset, offset + itemsPerPage),        total: books.length,      }    },    async get(ctx) {      return books.find(book => book.id === ctx.params.id) ?? null    },  },  processor: {    async create(ctx) {      const book = { ...ctx.input, id: crypto.randomUUID() }      books.push(book)      return book    },    async update(ctx) {      return updateBook(ctx.params.id, ctx.input)    },    async delete(ctx) {      await deleteBook(ctx.params.id)    },  },})
        

Explore the API

Resource endpoints

Capability tour

Real request shapes, not toy links.

filters + sort + pagination

Query pipeline

Hydro parses the query string into ctx.query so providers can apply filters, sort rules, pagination and sparse fields in one place.

  • Allow-list filters per resource
  • Pagination values are clamped by resource options
  • fields selects only the properties the client asks for
example.httpfilters + sort + pagination

            GET /api/authors?country=US&featured=true&order[name]=asc&page=1&itemsPerPage=2&fields=id,name,country,bookCount
          
belongsTo

Reference writes

Create a book by sending an author reference. Hydro resolves the relationship and writes the foreign key before your processor runs.

  • Validates the referenced author exists
  • Stores authorId in the flattened input
  • Keeps the public request shape resource-oriented
example.httpbelongsTo

            POST /api/booksContent-Type: application/json{  "title": "The Fifth Season",  "author": "/authors/a5"}
          
hasMany

Nested writes

Create a parent and child records together. The Author resource accepts nested books and Hydro attaches the generated author id.

  • Nested inputs are validated against the target schema
  • Child records are created after the parent
  • Works with transactional processors when provided
example.httphasMany

            POST /api/authorsContent-Type: application/json{  "name": "Martha Wells",  "country": "US",  "bio": "Science fiction and fantasy author.",  "books": [    { "title": "All Systems Red" }  ]}
          
item + collection actions

Custom operations

Use named actions for behavior that is not plain CRUD. They still live under the resource, validate input and output, and appear in the generated docs.

  • Item scope creates /api/books/:id/checkout
  • Collection scope creates /api/authors/spotlight
  • Each action declares its own input and output schema
example.httpitem + collection actions

            POST /api/books/1/checkoutContent-Type: application/json{ "userId": "demo-user" }200 OK{  "id": "1",  "available": false}POST /api/authors/spotlightContent-Type: application/json{ "country": "US" }
          
validation errors

Problem details

Clients get a predictable error contract instead of parsing framework-specific messages. Every violation points to the failing field.

  • Stable RFC 7807 response shape
  • Field paths map cleanly to form inputs
  • Works for schemas, nested writes and operation inputs
example.httpvalidation errors

            POST /api/authorsContent-Type: application/json{ "name": "", "country": "USA" }422 Unprocessable EntityContent-Type: application/problem+json{  "type": "validation-error",  "title": "Validation Error",  "status": 422,  "violations": [    { "path": "/name", "message": "Required" },    { "path": "/country", "message": "Expected 2 characters" }  ]}
          

Nested writes

Create an author and books together.

The author resource accepts nested book creation. Send this body to POST /api/authors to create a new author with one related book in a single request.

request.jsonPOST

          {  "name": "Martha Wells",  "country": "US",  "bio": "Science fiction and fantasy author.",  "books": [    { "title": "All Systems Red" }  ]}
        

Generated documentation

Spec-first when you need it.