Skip to content

API overview ​

Novodat exposes your database through a REST API and a GraphQL API. Both are generated from the schema: there is nothing to declare, and a new table or field is available as soon as it exists.

Base address ​

Every route hangs from PUBLIC_URL, for example https://data.example.com/items/articles. Requests and responses are JSON (application/json), except file uploads (multipart/form-data) and assets.

Collections, items and fields ​

TermIn the database
CollectionA table or a view. On PostgreSQL with several schemas, tables outside the default one are named schema.table.
ItemA row.
FieldA column, or a relation with no column of its own (the "many" side of a relation).

Your tables are read and written under /items/<collection>. Novodat's own tables (novodat_*) have their own routes: /users, /files, /collections… and are never reachable through /items.

Responses ​

A successful response wraps its result in data, with meta when you ask for counts:

json
{
  "data": [{ "id": 1, "title": "Hello" }],
  "meta": { "filter_count": 1 }
}

Deletes, and writes whose result you may not read, answer 204 with no body.

Errors ​

Errors always come as a list, each with a stable code:

json
{
  "errors": [
    {
      "message": "Value for field \"slug\" of collection \"articles\" has to be unique.",
      "extensions": { "code": "RECORD_NOT_UNIQUE", "collection": "articles", "field": "slug" }
    }
  ]
}

Messages are in English and may change; programs should rely on extensions.code, and translate from it when they show the error to people.

CodeStatusMeaningExtra data
FORBIDDEN403No permission, or the resource does not exist. Novodat never reveals whether something exists that you cannot see.
INVALID_CREDENTIALS401Wrong email or password.
INVALID_OTP401Missing or wrong two-factor code.
INVALID_TOKEN401The token is not valid, its session was closed or its user is no longer active.
TOKEN_EXPIRED401The access token expired: refresh it.
TFA_REQUIRED403A policy requires two-factor authentication, and the user has not set it up.
INVALID_PAYLOAD400The body is not valid.path
INVALID_QUERY400A query parameter is not valid.parameter
RECORD_NOT_UNIQUE400A unique value already exists.collection, field
VALUE_TOO_LONG400A value is longer than its field allows.collection, field, maxLength
CONTAINS_NULL_VALUES400A field cannot become required: some rows have no value.collection, field
VECTOR_DIMENSION_MISMATCH400A vector has the wrong number of dimensions.field, expected, received
UNSUPPORTED_FEATURE400The database engine does not offer this feature.feature, engine
ROUTE_NOT_FOUND404No such route.path
CONTENT_TOO_LARGE413The upload is too large.limit
RANGE_NOT_SATISFIABLE416The requested byte range is outside the file.size
REQUESTS_EXCEEDED429Too many requests or failed attempts. The Retry-After header says when to try again.limit, retryAfter
EXTENSION_MISSING500A PostgreSQL extension is needed and could not be installed. The message includes the command for an administrator.extension, command
SERVICE_UNAVAILABLE503A service Novodat needs (the database, an AI provider, a directory…) did not answer.service
INTERNAL500Unexpected error. Details stay in the server log.

Request id ​

Every response carries an X-Request-Id header. Novodat keeps the one sent by the client or a proxy when it is safe (up to 128 letters, digits, ., _, : or -), or makes a new one. Logs use the same id, so it is the best thing to quote when reporting a problem.

Engine capabilities ​

Some features depend on the database engine (see PostgreSQL-only features). GET /database/capabilities tells what the current database offers:

json
{
  "data": {
    "engine": "postgres",
    "capabilities": {
      "vector": true, "customTypes": true, "arrays": true, "jsonb": true,
      "multiSchema": true, "fullTextSearch": "tsvector", "geometry": "postgis", "…": "…"
    }
  }
}

Features the engine lacks do not appear in GraphQL or OpenAPI, and using them anyway answers UNSUPPORTED_FEATURE.

OpenAPI ​

GET /server/specs/oas returns an OpenAPI 3.1 document of what the caller may do: only the collections, fields, operations and filter operators available to that user on this engine. Load it into Postman, Insomnia, Swagger UI or a client generator, using the token of the user the client will act as.

bash
curl -s https://data.example.com/server/specs/oas -H "Authorization: Bearer $TOKEN" > novodat.openapi.json

Server routes ​

These need no authentication, except where noted.

RouteAnswer
GET /server/health{ data: { status: "ok", checks: { database: { status, responseTimeMs } } } }, or 503 when the database does not answer. Not counted in the rate limits.
GET /server/info{ data: { name: "Novodat", version } }
GET /server/specs/oasThe OpenAPI document of the caller.
GET /server/specs/graphql, GET /server/specs/graphql/systemThe GraphQL schema of the caller, as SDL.
GET /server/mapThe base map in effect and how map fields are drawn. Signed-in users only.
GET /database/capabilitiesThe engine and its capabilities.
GET /database/extensionsPostgreSQL extensions available and installed. Administrators only.

Next ​