build-app
# ClaudeWave Item Description The build-app skill guides users through all seven phases of building a production-ready Butterbase application, from provisioning a backend and designing database schema to implementing row-level security policies and deploying a live frontend. Use this skill when a user needs to create a new full-stack Butterbase app from scratch, set up complete infrastructure including database and authentication, or initialize a backend with an auto-generated REST API.
git clone --depth 1 https://github.com/butterbase-ai/butterbase-skills /tmp/build-app && cp -r /tmp/build-app/skills/build-app ~/.claude/skills/build-appSKILL.md
> **Prefer `/butterbase-skills:journey`** — for a fully guided multi-stage build with preflight, planning, deployment verification, and (optionally) hackathon submission. This one-shot skill remains for users who want the legacy linear setup.
# Build a Complete Butterbase App
This skill walks through all seven phases of building a production-ready Butterbase application — from provisioning a backend to deploying a live frontend. Follow each phase in order; later phases depend on artifacts (app_id, schema, RLS policies) produced by earlier ones.
> **Convention:** every JSON body below is the argument object for the tool named in its **Tool:** header. When the header reads `manage_schema` with `action: "apply"`, include `"action": "apply"` alongside the other fields when you make the call.
---
## Phase 1: Create the App
Use `init_app` to provision an isolated backend with its own database and auto-generated REST API.
**Tool:** `init_app`
```json
{
"name": "my-blog"
}
```
**Returns:**
```json
{
"app_id": "app_abc123",
"api_base": "https://api.butterbase.ai/v1/app_abc123"
}
```
**Important:** Save `app_id` and `api_base` — every subsequent tool call requires `app_id`.
### Optional: Generate a Service API Key
If the user needs programmatic access (CI/CD pipelines, server-to-server calls, admin scripts), generate a service key now.
**Tool:** `manage_auth_config` with `action: "generate_service_key"`
```json
{
"name": "Production Deploy Key"
}
```
> ⚠️ The full key (`bb_sk_...`) is shown **only once**. Store it securely — it cannot be retrieved again.
---
## Phase 2: Design & Apply Schema
Work with the user to understand their data model before writing any SQL. Ask:
- What are the primary entities (users, posts, products, orders)?
- Which tables are user-owned vs. shared/public?
- What relationships exist between tables (foreign keys)?
- Are there any boolean flags for public visibility (e.g. `published`, `is_public`)?
### Preview First with dry_run_schema
Always preview schema changes before applying them.
**Tool:** `manage_schema` with `action: "dry_run"`
```json
{
"app_id": "app_abc123",
"schema": {
"tables": {
"posts": {
"columns": {
"id": { "type": "uuid", "primaryKey": true, "default": "gen_random_uuid()" },
"author_id": { "type": "uuid", "nullable": false },
"title": { "type": "text", "nullable": false }
}
}
}
}
}
```
Review the generated SQL — make sure it matches intent before applying.
### Apply the Schema
**Tool:** `manage_schema` with `action: "apply"`
Below is a complete example for a **blog app** with posts and comments:
```json
{
"app_id": "app_abc123",
"schema": {
"tables": {
"posts": {
"columns": {
"id": { "type": "uuid", "primaryKey": true, "default": "gen_random_uuid()" },
"author_id": { "type": "uuid", "nullable": false },
"title": { "type": "text", "nullable": false },
"body": { "type": "text" },
"published": { "type": "boolean", "default": "false" },
"created_at": { "type": "timestamptz", "default": "now()" }
}
},
"comments": {
"columns": {
"id": { "type": "uuid", "primaryKey": true, "default": "gen_random_uuid()" },
"post_id": { "type": "uuid", "nullable": false, "references": "posts.id" },
"author_id": { "type": "uuid", "nullable": false },
"body": { "type": "text", "nullable": false },
"created_at": { "type": "timestamptz", "default": "now()" }
}
}
}
}
}
```
### Verify the Schema Was Applied
**Tool:** `manage_schema` with `action: "get"`
```json
{
"app_id": "app_abc123"
}
```
Confirm every table and column is present before moving to Phase 3.
### Schema Tips
- Always include `id` as UUID with `gen_random_uuid()` default
- Always include `created_at` with `now()` default
- Use `author_id` / `user_id` UUID columns on user-owned tables — RLS will reference these
- Use `references: "table.column"` for foreign keys (cascades must be set carefully)
- `manage_schema` action `apply` is idempotent — safe to call again if schema is unchanged
---
## Phase 3: Secure Data with RLS
Row-Level Security (RLS) ensures users can only access their own data. This phase is **not optional** for any table that holds user-generated content.
### Enable User Isolation
Call `create_user_isolation_policy` for each user-owned table. This single call:
1. Enables RLS on the table
2. Creates a policy so users only see their own rows
3. Installs a BEFORE INSERT trigger to auto-populate the user column
4. Creates a service bypass policy for admin access
**Tool:** `manage_rls` with `action: "create_user_isolation"`
```json
{
"app_id": "app_abc123",
"table_name": "posts",
"user_column": "author_id"
}
```
### Allow Public Reads (Optional)
For tables where some rows should be publicly visible (e.g. published blog posts), add `public_read_column`. This creates extra SELECT policies for both authenticated and anonymous users.
```json
{
"app_id": "app_abc123",
"table_name": "posts",
"user_column": "author_id",
"public_read_column": "published"
}
```
Repeat for every user-owned table. For the blog example:
```json
{
"app_id": "app_abc123",
"table_name": "comments",
"user_column": "author_id"
}
```
### Test RLS Isolation
After applying policies, verify they work correctly by simulating user requests.
**Test SELECT as a specific user** — should only see that user's rows:
**Tool:** `select_rows`
```json
{
"app_id": "app_abc123",
"table": "posts",
"as_role": "user",
"as_user": "11111111-1111-1111-1111-111111111111"
}
```
**Test SELECT as anonymous** — should only see published/public rows (or nothing if no public policy):
```json
{
"app_id": "app_abc123",
"table": "posts",
"as_role": "anon"
}
```
**Test INSERT as a specific user** — `author_id` should be auto-populated byClaude Code plugin for Butterbase — 30+ guided skills and auto-configured MCP for the AI-native backend-as-a-service.
Use when calling the app's AI gateway from agent tools — chat completions, embeddings, listing models, configuring defaults or BYOK, reading token/cost usage
Configure OAuth providers, auth hooks, JWT lifetimes, and service keys for a Butterbase app
Use when users report access denied errors, see wrong data, RLS policies are not working, or when troubleshooting Row-Level Security issues in Butterbase
Deploy a frontend (React, Next.js, or static HTML) to a live URL on Butterbase
Use when building stateful per-key actors — chat rooms, multiplayer rooms, rate limiters, long-running agents, leaderboards — that need persistent in-memory + storage state across requests
Develop, deploy, or debug a Butterbase serverless function
Stage 1 of the journey: concrete one-question-at-a-time idea brainstorm with inline Butterbase capability tagging.