Build an app
From a folder to an app running in a store's admin, in six steps. You need
the cartbase CLI signed in to the store (cartbase login).
An app is a manifest, cartbase.app.json, and whatever storefront code the
project needs under apps/<key>/. The app ships no admin code:
- its records become record types the platform stores, validates and lists in the admin under the app, and serves to the storefront when public;
- its settings become the app's Settings screen, committing as the merchant leaves each field; a secret is stored encrypted;
- its screens appear under the app in the sidebar and on the Apps page;
- its storefront files are recorded so an uninstall removes exactly them.
The reference chapters: app-manifest.md for every field, app-runtime-api.md for the app's own API, app-cli.md for the commands, apps.md for the storefront's reads.
1. Start the folder
# doc-noexec
cartbase app create store-locator --name "Store locator" --description "Partner shops on a map"Inside a storefront project this writes apps/store-locator/; anywhere else
./store-locator/. Three files: cartbase.app.json (the app), CLAUDE.md
(the brief a coding agent reads) and README.md.
2. Describe the app
Edit cartbase.app.json. Every field is in app-manifest.md.
A complete example:
{
"key": "store-locator",
"name": "Store locator",
"description": "Partner shops on a map",
"version": "1.0.0",
"icon": "pin",
"records": [
{
"type": "location",
"name": "Location",
"name_plural": "Locations",
"visibility": "public",
"fields": [
{ "key": "name", "name": "Name", "kind": "text", "required": true },
{ "key": "address", "name": "Address", "kind": "text", "required": true },
{ "key": "city", "name": "City", "kind": "text", "required": true },
{ "key": "lat", "name": "Latitude", "kind": "number", "required": true, "validations": { "min": -90, "max": 90 } },
{ "key": "lng", "name": "Longitude", "kind": "number", "required": true, "validations": { "min": -180, "max": 180 } },
{ "key": "phone", "name": "Phone", "kind": "text" },
{ "key": "hours", "name": "Opening hours", "kind": "rich_text" }
]
}
],
"settings": [
{ "key": "maps_key", "name": "Maps API key", "kind": "secret", "description": "From your maps provider's console" },
{ "key": "default_zoom", "name": "Default zoom", "kind": "number", "default": 7 }
],
"storefront": {
"files": ["apps/store-locator/storefront/StoreLocator.tsx"],
"component": "apps/store-locator/storefront/StoreLocator.tsx"
}
}3. Validate
# doc-noexec
cartbase app validate apps/store-locatorA refused manifest prints one line per problem, the path first, in words that say what to change. Fix and run again.
4. Deploy
# doc-noexec
cartbase app deploy apps/store-locatorThe app appears in the store's admin under Apps with its screens: a
Locations list, a record page per location, a Settings screen with the two
settings. The first deploy prints the app's key once and writes it to the
project's .env.local as CARTBASE_APP_STORE_LOCATOR_KEY. Deploy again
after every manifest change; the version history is kept.
5. Write the storefront half
The storefront reads the app's public records with the publishable key, through the package:
import { listAppRecords } from "@cartbase/storefront/api/apps"
const { metaobjects: locations } = await listAppRecords(client, "store-locator", "location", { limit: 100 })Each record carries fields: [{ key, kind, value }]. Server code that needs
a setting or a secret calls the app's own API with the key from the
environment; see app-runtime-api.md. Ship the storefront with
cartbase deploy from the project root, as always.
6. Share
# doc-noexec
cartbase app share apps/store-locatorThe folder becomes a public repository under the Cartbase organisation with a README written from the manifest and the MIT licence. Anyone installs it into their own store as a fork:
# doc-noexec
cartbase app install Cartbase/app-store-locator
cartbase deployRules
- Records and settings are the platform's. Render what it answers; never keep a second copy.
- A secret never reaches the browser. Read it in server code through the runtime API; the storefront's client bundle carries the publishable key and nothing else.
- The manifest is the app. A change is a new deploy. Removing a record type or a field that still holds data is refused; clear the data first.
- Uninstall deletes. The merchant is shown what goes and offered the export; the click deletes the app and everything it held, at once.