Example: Simple Blog

A complete working blog — post model, list and detail pages, publishing flow, and the APIs behind them.

A blog is the smallest complete app: one storage, two pages, one publishing flow. This example shows every piece working together — clone the structure for any content-driven site.

The Post Document

Post Document
{
  "_meta": {},
  "author_guid": "a23575f49d0af385314c1f02280163374297e018a692e5e4ab85eb307ebf6ebc",
  "author_name": "sami b",
  "body": "# Why we rebuilt\n\nEverything starts with...",
  "published_at": 1782166695,
  "slug": "why-we-rebuilt-the-docs",
  "status": "published",
  "tags": [
    "docs",
    "meta"
  ],
  "title": "why we rebuilt the docs"
}

One posts storage, everything embedded: the author is denormalized (author_name stored next to author_guid) so list pages never join — the core storage principle from Storages.

Public Pages

  1. Index page (/blog, scope *public): a function_search for status = published, sorted by published_at descending, limit 20 — the list-query shape from templates. Iterate results into post cards.
  2. Post page (/blog/post, *public): reads slug from the page params, fetches with function_search_single_result (term on slug + term on status = published — drafts 404 for the public), renders title/body with state elements.
  3. Feed: the same list query behind an API block → JSON for external readers — see Creating a REST API.

The Publishing Flow

  1. An editor form (*loggedin + custom editor scope) writes drafts with function_create_document_from_mutations — the form template from Block Templates.
  2. The slug is generated once from the title (lowercase, dashes) with a uniqueness check before create.
  3. Publishing is a status mutation (draft → published + published_at timestamp) via function_update_document_mutations.
  4. Edits create revisions automatically; the list only ever shows published.

Search

Add a search box feeding query_bool_simple_query_string_field on title + body into the same list query — lowercase the fields at write time, per search optimization.

Takeaways to Reuse

  • One storage per content type; embed what lists display.
  • Status fields drive visibility — never delete content, transition it.
  • Public reads and editor writes are separate pages with separate scopes.
  • The same query serves the page, the API, and the search box.

Next Steps

First CRUD App The tutorial this example builds on. Storage Examples More production-shaped data models. E-Commerce Catalog The same pattern at product scale.