Schemas

A collection's schema declares its fields and, optionally, which fields map onto filterable index columns. The schema validates writes and drives fast list filtering.

Shape

type CollectionSchema = {
  title?: string;
  fields: Field[];
  indexes?: {
    title?: string;   // field name to surface as the entry title
    summary?: string; // field name to surface as a short summary
    slug?: string;    // field name to use as the slug for ?slug= filtering
  };
};

type Field = {
  name: string;
  type:
    | "string"    // short single-line text
    | "text"      // long plain text
    | "richtext"  // HTML or markdown string, stored verbatim
    | "number"
    | "boolean"
    | "object"    // nested JSON object
    | "array"     // JSON array
    | "relation"; // entry id (or ids) referencing another entry
};

Field types

Type Stores Notes
string short text headlines, slugs, titles
text long text plain body copy
richtext HTML/markdown string stored verbatim, Floggy does not parse or sanitize it
number number
boolean boolean
object JSON object nested structured data
array JSON array lists, repeated blocks
relation entry id string, or array of ids references another entry, validated lazily

Floggy validates field presence and type on write. It does not interpret the meaning of a field. richtext is a string you render yourself.

The indexes mapping and why it exists

Entry data is stored as one JSON document. Filtering a list by reading and scanning every document does not scale. indexes maps up to three data fields onto dedicated columns so list queries filter without touching the JSON body.

{
  "title": "Landing pages",
  "fields": [
    { "name": "slug", "type": "string" },
    { "name": "headline", "type": "string" },
    { "name": "summary", "type": "text" },
    { "name": "body", "type": "richtext" }
  ],
  "indexes": {
    "title": "headline",
    "summary": "summary",
    "slug": "slug"
  }
}

With this mapping:

  • GET /entries?slug=black-friday filters on the indexed slug column, fast, no JSON scan.
  • List responses can surface title and summary without returning the full body (?full=1 returns the whole data).

Only title, summary, and slug are indexable. Point them at whichever of your fields carry that role. Changing an index mapping re-indexes existing entries.

Relations

A relation field stores the referenced entry's id (or an array of ids) as plain strings. Validation is lazy: Floggy does not block a write when a referenced entry does not exist, and it does not cascade deletes. Resolve relations in your own reads by fetching the referenced entries.

{
  "name": "author",
  "type": "relation"
}

Store the target entry id in that field, then look it up when you render.

Example: landing-page schema

{
  "title": "Landing pages",
  "fields": [
    { "name": "slug", "type": "string" },
    { "name": "headline", "type": "string" },
    { "name": "subhead", "type": "string" },
    { "name": "summary", "type": "text" },
    { "name": "body", "type": "richtext" },
    { "name": "hero", "type": "object" },
    { "name": "features", "type": "array" },
    { "name": "published", "type": "boolean" },
    { "name": "author", "type": "relation" }
  ],
  "indexes": {
    "title": "headline",
    "summary": "summary",
    "slug": "slug"
  }
}

An SEO engine writes entries against this shape; your site filters by slug, reads the indexed headline/summary for list views, and pulls the full body on the page.

Next

  • API reference - the list filters (?slug, ?tag, ?q, ?full) that indexes power.
  • Quickstart - create a collection with this schema.