# 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](app-manifest.md) for every field,
[app-runtime-api.md](app-runtime-api.md) for the app's own API,
[app-cli.md](app-cli.md) for the commands, [apps.md](apps.md) for the
storefront's reads.

## 1. Start the folder

```bash
# 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](app-manifest.md).
A complete example:

```json
{
  "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

```bash
# doc-noexec
cartbase app validate apps/store-locator
```

A refused manifest prints one line per problem, the path first, in words
that say what to change. Fix and run again.

## 4. Deploy

```bash
# doc-noexec
cartbase app deploy apps/store-locator
```

The 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:

```ts
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](app-runtime-api.md). Ship the storefront with
`cartbase deploy` from the project root, as always.

## 6. Share

```bash
# doc-noexec
cartbase app share apps/store-locator
```

The 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:

```bash
# doc-noexec
cartbase app install Cartbase/app-store-locator
cartbase deploy
```

## Rules

- **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.
