Bolt.new + Cradler

How to add a database to a Bolt app

Bolt.new builds and runs a full-stack app in your browser, and it already includes a database. This guide is about the choice underneath that convenience — Bolt-managed data versus a backend you own — and how to wire Cradler in without touching SQL.

Short answer

Bolt projects get a Bolt-managed database by default and can also use Bolt's native Supabase integration — so a fresh app can persist data immediately. You bring in an independent backend when you want to own the data and keep it portable. The no-SQL way to do that is a Cradler project: install @cradler/sdk in the Bolt editor, add your keys, and Cradler builds the schema from whatever your app saves.

How Bolt handles data today

Bolt.new, built by StackBlitz, is unusual among AI builders: it runs an entire Node toolchain in your browser through WebContainers. The app you prompt into existence is genuinely running — installing packages, serving a dev server — in the tab. Data has kept pace. New projects created with the Claude agent get a Bolt-managed database out of the box, and Bolt also ships a native Supabase integration you can authorize from your account settings.

So a brand-new Bolt app can store data with zero setup. As with Lovable, the question is not can it persist data — it is whose database that is, and how easily you can take it elsewhere.

The case for owning the backend

Bolt itself is candid that the default has friction: switching from a Bolt database to Supabase later requires extra steps. A built-in database is the right call for a throwaway prototype. It is the wrong call when:

  • The app is going to live, and you want the database — and its backups — under your own account.
  • Another service, a webhook, or a second app needs to read the same data.
  • You want the freedom to move off Bolt without an export project.

Connecting Cradler from the start sidesteps the later migration: the data is in an independent PostgreSQL database you own on day one. The native Supabase route does this too — if you are happy writing schemas and Row Level Security policies in SQL. Cradler is for the builder who wants ownership without the administration.

Connecting Cradler to a Bolt app, step by step

Because Bolt runs a real Node environment, this is just npm and a .env file.

  1. 01

    Create a Cradler project

    Sign up at cradler.ai and create a project. Cradler provisions an isolated PostgreSQL database and file storage, and gives you a gateway URL, a project ID, an anon key, and a service key.

  2. 02

    Install @cradler/sdk in the Bolt editor

    Bolt runs a real Node environment in the browser. Ask Bolt to add @cradler/sdk to package.json, or install it from Bolt's terminal — it installs like any npm package.

  3. 03

    Add your keys as environment variables

    Put the Cradler gateway URL, project ID, and anon key in your project's .env file inside Bolt. Bolt picks them up immediately in its live preview.

  4. 04

    Prompt Bolt and deploy

    Tell Bolt in plain language to save and read your data through the Cradler client. Cradler creates tables and columns as the data arrives, backs up daily, and Bolt deploys the app as usual.

The code: a small SaaS in Bolt

Suppose you prompted Bolt into a simple project tracker and you want tasks stored, updated, and queried. The full data layer is a handful of functions over one client:

import { createClient } from "@cradler/sdk";

const cradler = createClient({
  url: import.meta.env.VITE_CRADLER_URL,
  projectId: import.meta.env.VITE_CRADLER_PROJECT_ID,
  apiKey: import.meta.env.VITE_CRADLER_ANON_KEY,
});

// Create a task. The "tasks" table is created on the
// first call; "done" is inferred as a boolean column.
export async function addTask(title: string, projectId: string) {
  const { rows } = await cradler.from("tasks").insert({
    title,
    projectId,
    done: false,
  });
  return rows[0];
}

// Mark a task complete.
export async function completeTask(id: string) {
  await cradler.from("tasks").update({ done: true }).eq("id", id);
}

// Open tasks for one project, oldest first.
export async function openTasks(projectId: string) {
  const { rows } = await cradler
    .from("tasks")
    .select("id", "title", "createdAt")
    .eq("projectId", projectId)
    .eq("done", false)
    .order("createdAt");
  return rows;
}

// Delete a task — a filter is required, so you can never
// wipe the whole table by accident.
export async function removeTask(id: string) {
  await cradler.from("tasks").delete().eq("id", id);
}

One detail worth calling out: delete() refuses to run without at least one filter. There is no way to empty a collection with a stray call — a small guardrail that matters when an AI agent is writing the code.

Handling file uploads

If a task can carry an attachment, storage is already in the same Cradler project — no extra integration to authorize in Bolt:

async function attachFile(taskId: string, file: File) {
  const path = `tasks/${taskId}/${file.name}`;

  await cradler.storage.upload(path, file, {
    contentType: file.type,
  });

  await cradler
    .from("tasks")
    .update({ attachmentPath: path })
    .eq("id", taskId);
}

// List everything stored under one task.
async function taskFiles(taskId: string) {
  return cradler.storage.list(`tasks/${taskId}/`);
}

Gotchas worth knowing

Vite env vars must be prefixed. Bolt apps are usually Vite-based, and Vite only exposes variables that begin with VITE_ to client code. Name your variables VITE_CRADLER_URL, VITE_CRADLER_PROJECT_ID, and VITE_CRADLER_ANON_KEY, and keep the service key out of anything client-side.

Set table permissions before you go live. The anon key is visible in the browser bundle. In the Cradler dashboard, decide per table whether the anon key may read, insert, update, or delete — the default is deny. The service key, for server code only, ignores those toggles.

Schema growth is one-directional. Cradler adds columns and tables automatically but never removes or renames them on its own. Destructive changes are explicit, confirmed, backed-up dashboard actions, so a wayward Bolt prompt cannot delete a column.

Why Cradler fits Bolt

Bolt's magic is that the whole stack runs and deploys from one browser tab. Cradler keeps the backend just as low-friction without trapping your data inside the tool: it is standard PostgreSQL you own, with a typed SDK, generated types, an llms.txt, and an MCP server (@cradler/mcp) so Bolt's agent generates accurate data code. The full SDK reference lives on the @cradler/sdk npm page.

Frequently asked questions

Does Bolt.new come with a database?

Yes. Every Bolt project created with the Claude agent uses a Bolt-managed database by default, and Bolt also has a native Supabase integration with unlimited free databases. You connect a different backend when you want the data under your own account, when something outside Bolt needs to read it, or when you want to avoid the extra steps Bolt itself flags for switching off a Bolt database later.

How do I add a database to a Bolt app without writing SQL?

Create a Cradler project, install @cradler/sdk in the Bolt editor, add your keys to the .env file, and prompt Bolt to persist your data. Cradler runs PostgreSQL but never makes you write SQL or a migration — it creates the table on the first insert and adds columns as new fields appear.

Bolt's built-in database vs. Cradler — what is the difference?

Bolt's built-in database and Supabase integration are tied to the Bolt environment and, for the built-in option, Bolt notes that switching away later takes extra steps. Cradler is an independent backend you own from the start: it runs standard PostgreSQL, the schema auto-evolves, file storage is included, and your data is never coupled to Bolt. If portability matters, start on Cradler.

Will Cradler work inside Bolt's in-browser environment?

Yes. Bolt runs a full Node environment in the browser via WebContainers, so @cradler/sdk installs from npm and runs exactly as it would locally. The SDK talks to Cradler over HTTPS, which works fine from Bolt's preview and from the deployed app.

Where do uploaded images go in a Bolt app on Cradler?

Cradler includes file storage in the same project as the database. You upload with client.storage.upload(); the bytes go straight to object storage via a presigned URL and are served over a CDN. There is no separate bucket to provision inside Bolt.

Keep reading

Give your Bolt app a backend you own

Create a Cradler project and get a database, file storage, and API keys in seconds. Your first month is free — no credit card.