A recruiting profile that each athlete keeps up to date. A tool that gathers performance data from a team. A request tracker, a lab log, a register of who borrowed what. Many small web apps are records, logins and forms around them, plus a screen where someone looks after it all, and building one from a framework means writing that back office before the app itself.

Total CMS starts with the back office done. A schema becomes a collection with an admin table and an editing form, and the parts of an app attach to it.

This page's JumpStart builds one: a lending app for Ninebark Tool Library, a fictional neighborhood tool library. Borrowers sign up, request tools and see only their own loans, volunteers answer them in the admin, and two automations keep the due dates and the shelves up to date. The screenshots below come from it.

Records, and who may touch them

A schema names the fields and their rules, and every record is validated against it on each save, whether the change comes from the admin, a form, the API or an import. Relational fields link records across collections: an entry to its project, a booking request to its customer.

  • Owned records. Set an owner property on a collection and the server stamps the signed-in user's id on each record they create, then refuses any read or write of someone else's. Users see and edit only their own.
  • Access groups decide which collections a signed-in user may create, read or update, and nothing beyond that.
  • Protected fields. Mark a field as protected and only an admin or an automation can change it; a user's save leaves it as it was. Protect draft and a user's new record waits for an admin's approval, or set it to return to draft so every later edit waits too. In the sample, a loan's status and due date are protected, so a borrower can't mark their own request approved.
A borrower's loans page in the Ninebark Tool Library sample: a wet tile saw ready for pickup with a note from the desk, a floor sander out and due back Tuesday, a circular saw requested, and a form to borrow a jigsaw.
The borrower's side of the sample: only their own loans, each with its status and the desk's note, and a request form built from the schema.

Accounts that never reach the admin

Your users go in their own auth collection, separate from the people who run the admin. A public registration form creates the account and signs the user in, password resets are built in, and an account can expire on a date or after a set number of logins.

Forms, the API and jobs that run on their own

  • Forms from the schema. cms.form.builder() renders a form for a collection, with validation and file uploads, on any page.
  • A REST API with scoped API keys, and OAuth for clients that sign in as a user. A mobile client or another system reads and writes the same records, through the same validation.
  • Automations. Short PHP handlers run on a cron schedule, when a webhook arrives at the automation's own endpoint, or when a record is created, updated or deleted. Event runs are queued, so a slow handler never holds up the save that triggered it.
  • Pages that update in place. The HTMX recipes cover live search, faceted filters and load-more lists without a JavaScript framework.
The Loan desk automation in the Total CMS admin: two event triggers, object created and object updated, and the start of its PHP handler.
The sample's Loan desk automation runs on every new or changed loan: it sets the due date from the pickup day and keeps each tool's status in step with its loans.

The admin is the back office

Every collection gets an admin table to search, sort and edit, so support and operations happen in a screen you didn't have to build. Data Views pre-compute and cache queries across collections for dashboards and reports, and any collection exports as JSON or CSV.

Where it stops

  • It isn't a relational database. Each record is a file, saved on its own. There are no joins and no transactions that change several records together, so an app built on thousands of writes a second, or on reports across millions of rows, belongs on a database and an application framework.
  • There is no billing. Subscriptions and payments need a payment service alongside.
  • Logic beyond forms is PHP. Automations and extensions are code, so plan on a developer for anything the schema and access groups don't cover.
  • Nothing is real-time. Chat, live cursors and other push updates are outside what it does.

What you need

  • Pro. An app needs custom schemas, and the REST API, API keys, OAuth, automations, webhook form actions and Data Views are all Pro.
  • A cron job to run automations:process, if you use scheduled or event automations.
  • Any host with PHP 8.2 or later, with no database to set up.