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

# App

> Build a slate's browser interface and use declared resources from React Router loaders and actions.

The **app** is the interactive part of a [slate](https://docs.usecontextlayer.com/core-concepts/slate): the pages people open in a browser. It is a [React Router](https://reactrouter.com) app whose server code runs as a Cloudflare Worker.

React components render the interface. Route **loaders** read data for a page, and route **actions** handle submissions that change data. Loaders and actions use the ContextLayer SDK to call connected services and query databases. The browser receives the results your route returns.

## The starter

The CLI's `init` command creates the app from the starter bundled with the CLI, with Tailwind and shadcn/ui set up:

* `app/routes/home.tsx` is the first screen. The starter's version is a small notes page that reads and writes the `notes` database.
* `app/root.tsx` re-exports the SDK's document `Layout`, which mounts the [chrome](https://docs.usecontextlayer.com/core-concepts/slate#the-chrome) on the hosted slate. Keep this re-export when you edit the root. The root's error boundary shows setup guidance when a connection has no assigned account or a viewer-mode secret has no value.
* `public/slate.json` is the app's declaration.
* `databases/notes/` holds the `notes` database's Kysely configuration, migrations and generated query types.

## Declare resources

The app names the resources it requires in `public/slate.json`. ContextLayer uses this declaration to prepare the app's resource capabilities; the file does not contain account credentials, secret values, or database contents. The starter declares one database:

```json public/slate.json theme={null}
{
  "connections": {},
  "databases": {
    "notes": { "mode": "owner" }
  },
  "secrets": {}
}
```

`connections` is required, even when empty. Here, `notes` is the database slug that the app uses in queries, and `"mode": "owner"` selects the slate Owner's database for all viewers. A resource declared with `"mode": "viewer"` instead selects the current viewer's resource. [Resources](https://docs.usecontextlayer.com/core-concepts/resources) explains the declaration fields and ownership modes.

## Run locally

From the checkout root, `pnpm dev` starts the development server; open the local URL it prints. Locally, the app uses your own resources:

* Connections use your personal accounts, whatever their declared mode. Assign them with the CLI's `connections` command (`npx @usecontextlayer/cli connections --help`). Local assignments are saved in `.contextlayer/connections.json`.
* Secrets come from environment variables with the declared names.
* Databases are stored in `.contextlayer/databases`.

Restart `pnpm dev` after changing declarations, assignments, or secret values so startup can prepare the new configuration. When the app declares connections, the development server also needs your CLI login. The [account connection guide](https://docs.usecontextlayer.com/how-to/connect-an-account.md) walks through local assignments.

Run `pnpm typecheck` to check the checkout's TypeScript and `pnpm build` to build it for production. Local database contents and environment values are not uploaded when you deploy.

## Use connected services and databases

The SDK supplies resource capabilities through React Router's request context. A loader or action asks for the capability it needs and selects a resource by its declared slug:

* `context.get(toolsContext)` returns Tools. `tools.call(slug, tool, arguments)` runs a tool through one of the app's declared connections and resolves to `{ data, logId }`. Validate `data` before using it.
* `database<DB>(context.get(databasesContext), slug)` returns a typed Kysely instance for a declared database. `context.get(databasesContext).query(slug, sql, params)` runs SQL directly.

### Query the notes database

This excerpt from the notes route shows the loader reading notes newest first and the action inserting a submitted note. Both select the declared `notes` database; the action validates the form value before inserting it. Keep the route's rendered component alongside these server functions:

```tsx app/routes/home.tsx theme={null}
import { database, databasesContext } from "@usecontextlayer/sdk"
import { z } from "zod"
import type { DB } from "@/databases/notes/types"
import type { Route } from "./+types/home"

export async function loader({ context }: Route.LoaderArgs) {
  const db = database<DB>(context.get(databasesContext), "notes")
  return {
    notes: await db.selectFrom("notes").selectAll().orderBy("id", "desc").execute(),
  }
}

export async function action({ context, request }: Route.ActionArgs) {
  const body = z
    .string()
    .trim()
    .min(1)
    .parse((await request.formData()).get("body"))
  const db = database<DB>(context.get(databasesContext), "notes")
  return db.insertInto("notes").values({ body }).returningAll().executeTakeFirstOrThrow()
}
```

The `"notes"` argument to `database()` is a database slug. The `"notes"` argument to `selectFrom()` or `insertInto()` is a table name inside that database. They happen to match in the starter; they are separate names.

### Call a connected service

A connected-service call follows the same pattern. This excerpt assumes you have declared a Gmail connection named `work-email` in `public/slate.json` and assigned an account to it. Inside a route loader or action, obtain Tools and name that connection:

```ts theme={null}
import { toolsContext } from "@usecontextlayer/sdk"

// Inside the loader or action:
const tools = context.get(toolsContext)
const { data, logId } = await tools.call("work-email", "GMAIL_FETCH_EMAILS", {
  max_results: 5,
})
```

Here, `work-email` selects the declared connection, `GMAIL_FETCH_EMAILS` selects the provider tool, and the object supplies that tool's arguments. `data` is the provider result; validate its shape before using it. `logId` identifies the tool execution. See the [SDK reference](https://docs.usecontextlayer.com/reference/sdk.md#tools) for the call contract.

Keep these calls in loaders and actions. Browser components submit requests to those routes and receive the data they return; they receive no ContextLayer credentials. On the hosted slate, each resource call is bound to the slate and the viewer making the request. The declaration controls which resources are available, and your route code controls which operations the interface exposes.

When a declared connection has no assigned account, or a viewer-mode secret has no value, the call throws an HTTP 409 response. The starter's root error boundary turns it into setup guidance; the SDK's `connectionRequiredSchema` and `secretRequiredSchema` recognize it in a custom error boundary. The [SDK reference](https://docs.usecontextlayer.com/reference/sdk.md) documents the full API.

## Deploy

Commit your changes and push `main` to the slate's remote. See [Deploy with a Git push](https://docs.usecontextlayer.com/core-concepts/slate#deploy-with-a-git-push).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.