> For the complete documentation index, see [llms.txt](https://codemash.gitbook.io/codemash/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://codemash.gitbook.io/codemash/cli/recipes.md).

# Recipes

Scripts and CI examples for the Norbix CLI — create and fill a collection, export records to JSON, copy data to TEST, back up to Files, run in GitHub Actions, follow a failed request in the logs.

Short scripts you can copy. Every recipe was run with `@norbix.ai/cli` 1.16.0 on a self-hosted test installation (project `pr_13ECeE5HP8U2eHJDLwFxIA`); the outputs are the real ones.

## Before you start

* The CLI is installed and signed in — [Norbix CLI](/codemash/cli/cli.md#sign-in). The recipes use the browser sign-in with the **Administrator** project role, except the CI recipe, which uses an API key.
* A project is selected (`norbix config set projectId pr_…`) — [Select a project](/codemash/cli/cli.md#select-a-project).
* [`jq`](https://jqlang.org/) for the recipes that read JSON (`brew install jq`, `apt-get install jq`; GitHub's `ubuntu-latest` runners have it).
* Every write can be previewed with `--dry-run` first; deletes and overwrites need `--yes` in a script.

## Create and fill a collection

A collection is a database schema. Write the schema of one record to a file, create the schema, publish it, then seed records.

`books.schema.json`:

```json
{
  "$schema": "https://norbix.ai/schemas/meta/v1.json",
  "title": "books",
  "type": "object",
  "additionalProperties": true,
  "properties": {
    "title": { "type": "string", "maxLength": 200 },
    "pages": { "type": "integer", "minimum": 0 },
    "in_stock": { "type": "boolean" },
    "genre": { "source": "enum", "values": ["fiction", "science", "history"] }
  },
  "required": ["title", "pages"]
}
```

```sh
norbix db schema create --name books --file books.schema.json
norbix db schema publish sch_4oM56MYEQnqGbp54Xs3rJp --yes
```

```
{
  "id": "sch_4oM56MYEQnqGbp54Xs3rJp",
  "responseStatus": { "isSuccess": true, "errors": [] }
}
Schema sch_4oM56MYEQnqGbp54Xs3rJp published.
```

A new schema is a draft until you publish it. The same schema in Cloud: [Create a schema](/codemash/cloud/database/collections/create-schema.md).

**Seed your own records** (`--mode realistic`) from a file — `seed.json`:

```json
[
  {
    "collectionName": "books",
    "documents": [
      { "title": "The Hobbit", "pages": 310, "in_stock": true, "genre": "fiction" },
      { "title": "A Brief History of Time", "pages": 256, "in_stock": true, "genre": "science" },
      { "title": "SPQR", "pages": 608, "in_stock": false, "genre": "history" }
    ]
  }
]
```

```sh
norbix db seed --mode realistic --collections @seed.json --yes
```

```json
{
  "result": {
    "insertOrder": ["books"],
    "report": [
      {
        "collectionName": "books",
        "requested": 3,
        "inserted": 3,
        "ids": ["6ac550b5c045af0d247a14e1", "6ac550b5c045af0d247a14e2", "6ac550b5c045af0d247a14e3"],
        "errors": []
      }
    ]
  },
  "responseStatus": { "isSuccess": true, "errors": [] }
}
```

**Seed made-up test records** (the default `--mode dummy`): Norbix fills every field from the schema.

```sh
norbix db seed --collections '[{"collectionName":"books","count":5}]' --env TEST --yes --json \
  | jq -c '.result.report[] | {collectionName, requested, inserted, errors}'
norbix db find books --env TEST --page-size 1 --json | jq -c '.list.items[0] | {title, pages, in_stock, genre}'
```

```
{"collectionName":"books","requested":5,"inserted":5,"errors":[]}
{"title":"Jonas ember","pages":62,"in_stock":true,"genre":"science"}
```

Seeding is all or nothing: every document is checked against the schema first. Several collections in one call are inserted parents first; in realistic mode a reference to another seeded record is written as `{"$seedRef":{"collection":"<name>","index":<n>}}`.

## Export records to JSON

`norbix db find` returns one page and a cursor (`startingAfter`) while `hasMore` is `true`. This script follows the cursor and writes every record into one JSON array — `export-books.sh`:

```sh
#!/bin/sh
# Export every record of a collection to one JSON array, page by page.
# usage: export-books.sh <collection> <file.json>
set -e
collection=${1:-books}
out=${2:-$collection.json}
after=""
echo "[]" > "$out"
while :; do
  page=$(norbix db find "$collection" --page-size "${PAGE_SIZE:-100}" ${after:+--after "$after"} --json)
  printf '%s' "$page" | jq -s '.[0] + .[1].list.items' "$out" - > "$out.tmp"
  mv "$out.tmp" "$out"
  [ "$(printf '%s' "$page" | jq -r '.list.hasMore')" = "true" ] || break
  after=$(printf '%s' "$page" | jq -r '.list.startingAfter')
done
echo "$(jq length "$out") records written to $out"
```

```sh
PAGE_SIZE=2 ./export-books.sh books books.json
jq -c '[.[].title]' books.json
```

```
3 records written to books.json
["SPQR","A Brief History of Time","The Hobbit"]
```

`PAGE_SIZE=2` only shows the paging at work; leave it out for real exports. Add `--filter '{"in_stock":true}'` to the `db find` line to export a part of the collection, or `--env TEST` for another environment.

## Copy PROD records into TEST

The **TEST** environment has its own database. Create the same schema there, then insert the exported records without their ids and meta data:

```sh
norbix db schema create --name books --file books.schema.json --env TEST
norbix db schema publish sch_5psl5Pouenm1cA2vVyxezF --env TEST --yes
jq 'map(del(._id, ._meta))' books.json | norbix db insert-many books --docs - --env TEST
norbix db count books --env TEST --json | jq .count
```

```
{
  "id": "sch_5psl5Pouenm1cA2vVyxezF",
  "responseStatus": { "isSuccess": true, "errors": [] }
}
Schema sch_5psl5Pouenm1cA2vVyxezF published.
{
  "responseStatus": { "isSuccess": true, "errors": [] }
}
3
```

`--docs` takes inline JSON or `-` (read from the pipe); a file name with `@` works for `db seed --collections` only. To move whole schemas, templates and triggers between environments, use **Promote** in Cloud ([Environments](/codemash/cloud/project/settings/environments.md)) — it does not copy records.

## Back up an export to Files

Upload the JSON file to a Files integration and read it back. Files needs an integration — the id comes from `norbix integrations files` ([Files → Integrations](/codemash/cloud/files/integrations.md)).

```sh
norbix config set filesIntegrationId int_6Cp3gUBA9in5FptVfd8QmM
norbix files upload books.json "exports/books-$(date -u +%F).json"
norbix files list exports --json | jq -c '.list.items[] | {path, size: .resource.sizeBytes}'
norbix files download exports/books-2026-10-07.json restored.json
cmp books.json restored.json && echo same
```

```
filesIntegrationId = int_6Cp3gUBA9in5FptVfd8QmM
Uploaded books.json → exports/books-2026-10-07.json (773 bytes).
{"path":"exports/books-2026-10-06.json","size":773}
{"path":"exports/books-2026-10-07.json","size":773}
Downloaded exports/books-2026-10-07.json → restored.json (773 bytes).
same
```

`NORBIX_FILES_INTEGRATION_ID` or `--integration int_…` work instead of the saved value. Uploading needs a role with `files:create` — **Developer** or **Administrator**; **Data Editor** is refused.

## Run it in GitHub Actions

A nightly job that exports `books` and stores the file in Files, with an **AI service user key** from the repository secrets — no person, no browser.

1. Create an AI service user with the **Developer** role on the project and copy its `nbsu_…` key: [Create a key for CI](/codemash/ai/developers/read-only-keys.md#create-a-key-for-ci).
2. In GitHub → **Settings → Secrets and variables → Actions**: the secret `NORBIX_API_KEY` = the key; the variables `NORBIX_PROJECT_ID` (`pr_…`), `NORBIX_REGION` (for example `nb-eu-germany`) and `NORBIX_FILES_INTEGRATION_ID` (`int_…`). For a self-hosted installation set `NORBIX_HUB_URL` and `NORBIX_API_URL` instead of `NORBIX_REGION`.
3. Commit `scripts/export-books.sh` from [Export records to JSON](#export-records-to-json) (executable: `chmod +x`) and this workflow as `.github/workflows/nightly-export.yml`:

```yaml
name: Nightly books export

on:
  schedule:
    - cron: "0 2 * * *"
  workflow_dispatch:

jobs:
  export:
    runs-on: ubuntu-latest
    env:
      NORBIX_API_KEY: ${{ secrets.NORBIX_API_KEY }}
      NORBIX_PROJECT_ID: ${{ vars.NORBIX_PROJECT_ID }}
      NORBIX_REGION: ${{ vars.NORBIX_REGION }}
      NORBIX_FILES_INTEGRATION_ID: ${{ vars.NORBIX_FILES_INTEGRATION_ID }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm install -g @norbix.ai/cli@1.16.0
      - run: norbix --version
      - run: ./scripts/export-books.sh books books.json
      - run: norbix files upload books.json "exports/books-$(date -u +%F).json"
```

The steps of this job were run in a clean `node:22` container with the same environment variables (a self-hosted test installation, so `NORBIX_HUB_URL` / `NORBIX_API_URL` instead of the region):

```
$ npm install -g @norbix.ai/cli@1.16.0
$ norbix --version
@norbix.ai/cli/1.16.0 linux-arm64 node-v22.23.3
$ ./scripts/export-books.sh books books.json
3 records written to books.json
$ norbix files upload books.json "exports/books-$(date -u +%F).json"
Uploaded books.json → exports/books-2026-10-07.json (773 bytes).
```

A failed command ends with a non-zero [exit code](/codemash/cli/cli.md#errors-and-exit-codes), so the job fails. Pin the CLI version (`@1.16.0`) so a new release does not change the job without you knowing. `norbix whoami` is not a good first check in CI: it ends with 0 even when the server check fails.

## Find a failed request and follow it in the logs

Every request to the Hub and the API is logged with a **correlation id** when the project has the Logs module with **Norbix Logging** ([Logs](/codemash/cloud/logs.md)). A refused request is still answered with HTTP 200 and logged at the **Information** level, so filter the `http:response` entries by their answer — `failed-requests.sh`:

```sh
#!/bin/sh
# List the requests of the last hour that the server refused, then show the whole trail of the newest one.
since=$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-1H +%Y-%m-%dT%H:%M:%SZ)
norbix logs list --event-code http:response --from "$since" --page-size 200 --json \
  | jq -r '.list.items[]
           | select((.meta["response.responseStatus"] // "") | contains("\"isSuccess\":false"))
           | [.timestamp, .title, .correlationId] | @tsv' > failed.tsv
cat failed.tsv
id=$(head -1 failed.tsv | cut -f3)
[ -n "$id" ] && norbix logs trail "$id" --json \
  | jq -r '.items[] | [.eventCode, .title, (.meta["response.responseStatus"] // .meta["request.document"] // "")] | @tsv'
```

After `norbix db insert books --doc '{"title":"No pages"}'` (refused: `pages` is required):

```
2026-10-07T04:49:46.261Z	POST /v2/database/collections/books -> 200	d452c522d868e9f74c6ba2ca4b96f242
http:request	POST /v2/database/collections/books	{"title":"No pages"}
http:response	POST /v2/database/collections/books -> 200	{"isSuccess":false,"errors":[{"message":"Field \\u0027pages\\u0027 is invalid: a value is required","errorCode":"CM-ERRORS-DATABASE-030","context":{"FieldName":"pages"},"stackTrace":[]}]}
```

`norbix logs trail <correlationId>` shows every entry of one request in order: the request, the commands and queries it ran, and the answer. The CLI's own error output does not print the correlation id, so find it in the log list as above, or in Cloud (**Logs → Logs**, open an entry).

## Where you use it

* Every command and flag: [CLI reference](/codemash/cli/reference.md)
* The same calls from code: [SDKs](/codemash/sdks/install.md)
* For coding agents: [The CLI for agents](/codemash/ai/developers/cli-for-agents.md)
