Docs / Build your own

Your own app

A Studio app is a workspace plus an application definition: a studio:AppDef node that gives the workspace a nav entry, a table with columns, views, the actions people may take, and what they may create. Studio synthesises the browser from it. For most record-keeping apps — submissions, requests, registrations, a small CRM — that is the whole app, with zero source code.

A Submissions app: a table of form entries with email, form, submitted date and status columns
An app made of one node type, one workspace and one application definition: the entries a website's forms sent, with their status.

The three files

A node type for the record — what is indexed is what the table can show and what a trigger can filter on:

# nodetypes/form-submission.yaml
name: acme:FormSubmission
title: Form submission
properties:
  - { name: title, type: String, required: true, index: [Fulltext] }
  - { name: form, type: String, index: [Property] }
  - { name: email, type: String, index: [Fulltext] }
  - { name: submitted_at, type: String, index: [Property] }
  - { name: status, type: String, default: new, index: [Property] }
  - { name: payload, type: Object }

A workspace that allows it:

# workspaces/submissions.yaml
name: submissions
title: Submissions
allowed_node_types: [studio:Folder, acme:FormSubmission]
allowed_root_node_types: [studio:Folder, acme:FormSubmission]

An application definition, with its views, actions and create rules as child nodes:

content/apps/local/appdefs/submissions/
├── .node.yaml                  studio:AppDef
├── views/.node.yaml            studio:Folder
├── views/list/.node.yaml       studio:AppView   { key: list, default: true }
├── actions/.node.yaml          studio:Folder
├── actions/edit/.node.yaml     studio:AppAction { key: edit }
├── actions/delete/.node.yaml   studio:AppAction { key: delete }
└── create/root/.node.yaml      studio:AppCreateRule { pattern: "**", archetypes: [acme:FormSubmissionPage] }
# content/apps/local/appdefs/submissions/.node.yaml
node_type: studio:AppDef
archetype: studio:AppDef
properties:
  title: Submissions
  workspace: submissions
  icon: tray-arrow-down
  color: "#104f5d"
  domain: { uuid: submissions-domain, element_type: studio:AppDomain, id: content }
  section:
    uuid: submissions-section
    element_type: studio:AppSection
    label: Submissions
    icon: tray-arrow-down
    description: Entries sent through the site's forms
    order: 95
  browser:
    root: "/"
    containerTypes: [studio:Folder]
    listColumns:
      - { id: state, header: "", render: status, width: "2.5rem" }
      - { id: name, header: Entry, render: title }
      - { id: email, header: Email, field: email, render: property }
      - { id: form, header: Form, field: form, render: property }
      - { id: sent, header: Submitted, field: submitted_at, render: property }
      - { id: handled, header: Status, field: status, render: property }
    emptyTitle: No submissions yet
    emptyDescription: Entries sent through the site's forms land here.

Creating the AppDef also grants the app’s role access to the workspace, so nobody adds a permission by hand.

Why the lists are nodes

Views, actions and create rules are child nodes rather than keys in the JSON for one reason: row-level security can hide a node and cannot hide a key. A role that may not read actions/delete has no Delete button, with no permission check in any component. The grouping folder’s readability is the switch: a readable folder with no readable members means none of these; an absent folder means not managed here and everything is offered. Grant with globs such as /**/actions/delete per role.

Action keys name behaviours Studio implements — edit, rename, favorite, add, copy, cut, paste, move, variations, publish, unpublish, delete — so an action node chooses what appears, in what order, for whom. A view adds a board with its own config (column field, columns, transition rules), a grid, an overview. Workplace is the reference for a bespoke app hung on the same switch: its views/* and actions/board-* nodes are what its board obeys.

Where it lives, and who owns it

Studio’s own apps ship under /apps/libs/appdefs/** and are refreshed on every update. Yours ship under /apps/local/appdefs/**, which an update never touches. Ship the two parent folders (/apps/local, /apps/local/appdefs) as create-if-missing, and claim only your subtree:

# .raisin-sync.yaml
defaults: { mode: skip }
filters:
  - { root: /apps/local/appdefs/submissions, mode: replace }

Verify

raisindb package create --check ./raisin-package
raisindb deploy ./raisin-package --repo <repo> --install

then read the tree back rather than trusting the deploy output:

SELECT path FROM apps WHERE path LIKE '/local/appdefs/%' ORDER BY path;

Records and automations

An app’s records are nodes, so everything on the Automations pages applies: trigger on acme:FormSubmission Created in submissions, read its fields by name, route with an agent, ask a person, write status back. The forms chapter puts the three together.

Pilot documentation. Studio is in a partner pilot. Screens and YAML shown here are current as of the version noted on each page; tell your Maravilla contact when something does not match what you see.