Collections
A collection is Paprika's equivalent of a database table: a named, schema-defined set of records, scoped to the active tenant, that automatically gets a REST API. Collections are created empty (see New collection) and then shaped with fields on the Schema tab.
Fields
Each field has a name — used as the JSON key in API requests and responses — and a type. A name may contain letters, digits, underscores and hyphens, has to start with a letter and stays under 64 characters. That is enforced, on the Schema tab and on a schema import alike: a name is also a key in MongoDB, where a dot means "nested path" and a $ starts an operator, and a field called _id would collide with MongoDB's own primary key. Collection names follow the same rule.
| Type | Stores | Notable options |
|---|---|---|
STRING | Text | minLength, maxLength, regex pattern |
NUMBER | Number (incl. decimals) | numberMin, numberMax |
BOOLEAN | true/false | default value |
EMAIL | Text, validated as an email address | — |
URL | Text, validated as a URL | — |
DATE | ISO date (yyyy-MM-dd) | minDate, maxDate |
TIME | Time of day | minTime, maxTime |
DATETIME | ISO timestamp with timezone | minDateTime, maxDateTime |
SELECT | One or more values from an allow list | values, maxSelect |
JSON | Arbitrary JSON | maxBytes, maxDepth, onlyObject/onlyArray |
RELATION | ID(s) of record(s) in another collection | target collection, maxSelect, cascadeDelete |
FILE | Uploaded file(s), stored alongside the record | maxSize, mimeTypes, maxSelect |
Any field can be marked required (must be present and non-empty on create) and given a default value applied when the field is omitted on create.
Relations
A RELATION field stores the id (or ids, if maxSelect > 1) of records in another collection. Relations to the built-in users collection are how ownership works — see Roles & Permissions. Enabling cascade delete on a relation means deleting the record that holds the relation also deletes the record(s) it points at — and only those the caller could have deleted directly: the deleteRule of the target collection is evaluated for every one of them, with the identity of whoever triggered the delete. A target the caller may not delete stays untouched, and a target collection without a delete rule is never emptied this way.
Ownership is not the only shape access control takes: when data belongs to a group of users rather than to one person, the record points at a group and a second collection records who is in it. That is what the group and peers rules do — see Roles & Permissions → Group membership.
Relations are validated when the record that holds them is written: create/update fails with a validation error if a referenced id doesn't exist in the target collection at that moment. There's no ongoing enforcement after that, though — if the referenced record is later deleted, the relation field is left pointing at an id that no longer exists (a dangling reference), and nothing revalidates or cleans it up automatically. Cascade delete doesn't help here: it runs in the other direction, from the holding record to its target.
Files
FILE fields are part of the schema, not a bolted-on system: they're validated (max size, allowed MIME types, max count) the same way any other field is, and they follow the same collection rules as the rest of the record — a viewRule that denies a user also denies downloading that record's files. Files are cleaned up automatically when their record is deleted or when a file field is overwritten. FILE fields can't be sent as JSON — creating or updating a record with a file requires a multipart/form-data request (see the API Reference tab for exact examples per collection).
One request, at most 4 MiB
maxSize limits a single file, but the whole multipart/form-data request is capped at 4 MiB by the server (Undertow's undertow.maxentitysize), and a reverse proxy in front of it may cap it lower still — nginx defaults to 1 MB, see the nginx example. The default maxSize is 4,000,000 bytes, which leaves room for the multipart boundaries and the other fields of the same request. Raise it and several files add up beyond the ceiling, or one file alone exceeds it, and the upload fails no matter what the schema says.
On disk, uploaded files are stored under <PAPRIKA_STORAGE>/<tenantId>/<fileId> — one folder per tenant, flat within it. Deleting a tenant removes this folder along with the tenant's MongoDB database, so no files are left behind.
Indexes
Any field can have a single-field MongoDB index, with a chosen sort direction and an optional uniqueness constraint, to speed up filtering/sorting or to enforce no-duplicates at the database level. Compound (multi-field) indexes exist in the data model and are preserved if a collection already has one, but there's no UI to create one — the Schema tab's per-field index toggle only ever produces single-field indexes.
System fields
Every record automatically gets three fields that are not part of the schema and can't be set by API clients:
id— the record's unique identifier.createdAt— set once, when the record is created.updatedAt— refreshed every time the record is saved.
id, createdAt, updatedAt (and the legacy names created/updated) are reserved and can't be used as schema field names.
Where collections show up
Each collection has five tabs in the admin UI, one per concern:
- Data — browse and edit records.
- Schema — define fields and indexes (this page).
- Rules — who can list/view/create/update/delete records via the API.
- Hooks — webhooks tied to this collection's lifecycle events.
- API Reference — auto-generated REST/SSE documentation for this collection.