> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stardeck.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Connecting & routing

> Connect a data store to an app, pick an access level, and route each environment to its own branch

This page covers the operational side of data stores: how an app actually gets access to one, what the different access levels mean, how to route each environment to a different branch, and how to browse and edit the data directly from the dashboard.

If you haven't read [Overview](/data-stores/overview) yet, start there—it explains what a data store is and when to reach for one. For rolling back changes, see [History & rewind](/data-stores/history-and-rewind).

## Connecting a store to an app

An app can't use a data store until you explicitly connect it.

1. Open your app and go to **Settings**
2. Scroll to the **Data Stores** section
3. Click **Connect**
4. Pick the data store and choose an **access level**

### Access levels

| Level     | What the app can do                                     |
| --------- | ------------------------------------------------------- |
| **Read**  | Query data only—no additions, edits, or deletions       |
| **Write** | Add, edit, and delete rows, but can't change the schema |
| **Admin** | Full control, including creating and altering tables    |

Pick the lowest level that still lets the app do its job. A customer-facing app that only displays products is safer with **Read**. An admin tool that manages inventory needs **Write** or **Admin**.

<Note>
  If your app was created before data stores existed, you'll see its built-in database listed
  here with **Admin** access. It's always connected and can't be disconnected—this is the legacy
  per-app database. New apps won't have this entry.
</Note>

## Branches and environment routing

This is the most important concept to understand, so take a minute with it.

A database store can have multiple **branches**—think of them as independent copies of your data, similar to git branches for code. Each branch has its own tables and its own rows. Changes on one branch never touch another.

When you connect a database store to an app, you decide which branch each environment reads from. Open **Settings → Data Stores** and you'll see a **Branch routing** panel under each connected database store, with three dropdowns:

| Environment    | Example branch | What it means                                |
| -------------- | -------------- | -------------------------------------------- |
| **Production** | `main`         | Real customers touch this data               |
| **Preview**    | `main`         | Client-facing previews share production data |
| **Sandbox**    | `dev`          | Safe to experiment—agent changes land here   |

### What happens when you change routing

* **Sandbox** — your sandbox restarts automatically so the new branch takes effect immediately
* **Preview** and **Production** — the change takes effect on your next deploy

<Warning>
  If the agent is in the middle of working when you change the sandbox branch, the restart is
  queued until the agent finishes. You'll see a notification letting you know. This prevents the
  agent from being cut off mid-task.
</Warning>

<Tip>
  A common setup: keep **production** and **preview** on `main`, and point **sandbox** at a `dev`
  branch. Now when the agent makes schema changes or experiments with data in sandbox, none of it
  touches your live customers. When you're happy with what's on `dev`, you can ask the agent to
  replicate the changes on `main`.
</Tip>

## Browsing and editing data

For database stores, the dashboard gives you a full view of your tables without touching any code.

1. Go to **Dashboard → Data Stores → \[your store]**
2. Use the **branch picker** at the top to choose which branch you're viewing
3. The left sidebar lists every table—click one to open it

From here you can:

* **Create tables and columns** with rich field types: text, number, date, boolean, select, multi-select, url, email, phone, currency, rating, relation, file reference, and JSON
* **Sort** by any column
* **Filter** rows with conditions like "equals", "contains", "is null"
* **Insert, edit, and delete** rows individually
* **Paginate** through large tables

<Note>
  When you delete a table from the dashboard, Stardeck soft-deletes it—the table is renamed and
  hidden rather than dropped. If you ever delete something by mistake, reach out to support and
  we can recover it.
</Note>

## Storage stores

Storage stores work a little differently because they hold files rather than rows.

1. Go to **Dashboard → Data Stores → \[your storage store]**
2. You'll see a folder tree on the left and a file list on the right
3. Drag and drop files to upload them, create folders with **New Folder**, and click a file to open its detail panel with a download link
4. Select files with the checkboxes and click **Delete** to remove them

Storage stores are a great fit for uploaded files you want to manage yourself—product photos, customer PDFs, downloadable assets—without writing any code to handle uploads.

<Note>
  Storage stores don't have branches. Every environment connected to a storage store sees the same
  files. If you need environment-isolated files, use separate storage stores.
</Note>

## Promoting an app database to an org-level store

Every app that uses Neon has its built-in databases (production and sandbox) automatically wrapped as data stores so you can browse them from the dashboard. By default these are owned by their app — they're listed with `admin` access in the app's data store settings, they can't be disconnected, and they're deleted along with the app.

If an app's database has grown into something more than one app should be able to read, you can **promote** it from the data store's settings page. Click **Promote** in the header next to the Branches and Rewind buttons, type the store name to confirm, and the wrapper becomes a real org-level data store.

After promotion:

* The originating app keeps a **read** connection via an explicit `data_store_connections` entry. Bump it to write or admin from the app's data store settings if you need to.
* The originating app **can no longer run schema migrations** against this database. Schema changes become your responsibility from the data store dashboard.
* **Deleting the originating app no longer deletes the underlying database.** That's the whole point — the data outlives the app that created it.
* **Your app's `DATABASE_URL` references will need to be updated.** The promoted store is injected under a new env var name. See the warning below for the rewrite.
* You can connect any other app in your org to the promoted store from the app's settings, just like any standalone store.

<Warning>
  **Your app's `DATABASE_URL` references will break.** Before promotion, your deployed app and
  sandbox receive the database connection string in `process.env.DATABASE_URL`. After promotion,
  the connection is injected as a data store env var instead — under a name derived from the
  data store's display name, e.g. `DATA_STORE_MY_APP__PRODUCTION_URL`. Any code that reads
  `process.env.DATABASE_URL` directly (your drizzle config, your db client, server actions, etc.)
  will see `undefined` until you switch the references over.
</Warning>

After promoting, ask the agent to do the rewrite for you. A prompt that works well:

> Find every reference to `process.env.DATABASE_URL` in this app and switch them to
> `process.env.DATA_STORE_<MY_STORE_NAME>_URL` (the env var name from my newly-promoted data
> store — check the data store dashboard for the exact name). Update the drizzle config, the
> db client, and any server-side code that reads it. Don't touch references inside `node_modules`
> or generated migration files.

<Tip>
  If you want the env var to land at a name you choose, **rename the data store before promoting
  it.** The store's display name (uppercased, non-alphanumerics replaced with `_`) becomes the
  env var prefix. Renaming "MyApp (production)" to "myapp-db" will give you
  `DATA_STORE_MYAPP_DB_URL`, which is shorter and easier to type than the default.
</Tip>

<Warning>
  Promotion is **not reversible from the dashboard.** If you need to undo it, email
  [support@stardeck.ai](mailto:support@stardeck.ai). Take a moment to make sure you actually want
  the database detached from the app lifecycle before you confirm.
</Warning>

<Note>
  Production and sandbox are promoted independently — each is its own data store wrapper. Most
  users only promote production; sandbox usually stays with the app so the agent can keep
  running migrations against it freely.
</Note>

## Access for deployed apps

You don't have to wire anything up manually. When you connect a data store to an app, Stardeck automatically makes the connection details available to your deployed app as environment variables. The agent uses them when it writes code that reads from or writes to the store, so you never need to copy a connection string. See [Environment Variables](/environment-variables) for more on how environment variables work in Stardeck.

## Common scenarios

### "I want my admin app and my customer app to share the same users"

Create a database store (e.g. `shared-users`), connect it to both apps, and give the admin app **Admin** access and the customer app **Write** access. Ask the agent to read and write users from the shared store instead of the built-in app database.

### "I want to try a schema change without breaking production data"

Create a new branch on your database store—say, `experiment`. Point your sandbox at that branch in the Branch routing panel. Now the agent can add, remove, or rename tables in sandbox without touching the `main` branch that production reads from. When you're ready, ask the agent to apply the same changes to `main`.

<Tip>
  For a less-involved safety net, take a [snapshot](/data-stores/history-and-rewind#snapshots) of
  the branch before the risky change and restore it if things go sideways. Branches are better for
  longer-lived experiments; snapshots are better for one-shot "let me try this" moments.
</Tip>

### "I want to keep uploaded PDFs in one place and view them in the dashboard"

Create a storage store, connect it to your app with **Write** access, and ask the agent to save uploads there. You'll be able to browse every uploaded file right from the dashboard without writing any admin UI.

### "I changed the sandbox branch and nothing seems to have happened"

Check two things: is the sandbox actually running, and was the agent in the middle of a task when you made the change? If the agent was busy, the restart is queued until it finishes. If the sandbox wasn't running, it'll pick up the new branch the next time you start it.

### "My production app still reads the old branch after I changed routing"

Production and preview only pick up branch routing changes on the next deploy. Publish a new production deploy from the dashboard and it'll start reading the branch you selected.
