# Add a backend to Astro with one command

> Give an Astro site a Postgres database, auth and storage: `npx scribase init` provisions it and writes .env; one edit switches to import.meta.env.

Source: https://scribase.com/frameworks/astro

init treats an Astro project as Node: SUPABASE_ variables in .env and a client at src/supabase.ts that reads process.env. Astro loads .env into import.meta.env instead, so change two words in the client. After that, frontmatter queries run on the server at build or request time.

## `npx scribase init` output

```
$ npx scribase init
Backend ready: https://acme-my-app-production-c2b0ea0a.scribase.com
  framework   node
  project     acme/my-app (production)
  env file    .env (SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, SCRIBASE_API_URL, SCRIBASE_ORG, SCRIBASE_PROJECT, SCRIBASE_ENV)
  client      src/supabase.ts

Next:
  - import { supabase } from './src/supabase' and query: await supabase.from('todos').select()
  - create tables with SQL migrations (supabase/migrations/*.sql) and `scribase db push`, or the MCP server
```

## Environment file: .env

- SUPABASE_URL
- SUPABASE_ANON_KEY
- SUPABASE_SERVICE_ROLE_KEY

## Run init

Run it in the project root. If you are not signed in, it prints a link and a code: approve it in the browser (sign up takes one click plus the $1 card check, credited back), and init carries on. No key is ever pasted. It creates the project, writes the env file, adds the env file to .gitignore, installs @supabase/supabase-js and writes a small client module.

```bash
npx scribase init
```

## Read the variables the Astro way

Astro exposes unprefixed variables to server code (frontmatter, endpoints) through import.meta.env, and never ships them to the browser.

_src/supabase.ts_
```ts
import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(import.meta.env.SUPABASE_URL, import.meta.env.SUPABASE_ANON_KEY);
```

## Create your first table

Open your project in the console, go to Editor, then SQL, and run this. Or ask your coding agent: with the Scribase MCP server connected it applies the same SQL as a migration. Row-level security is on, so the public key can only read what the policy allows.

```sql
create table public.todos (
  id bigint generated always as identity primary key,
  title text not null,
  done boolean not null default false,
  created_at timestamptz not null default now()
);

alter table public.todos enable row level security;

create policy "todos are readable" on public.todos
  for select to anon, authenticated using (true);

insert into public.todos (title) values ('Ship the backend'), ('Tell a friend');
```

## Query in the frontmatter

On a static site this runs at build time; with an SSR adapter it runs on every request.

_src/pages/index.astro_
```astro
---
import { supabase } from '../supabase';
const { data: todos, error } = await supabase.from('todos').select('id, title');
if (error) throw error;
---
<ul>{todos.map((todo) => <li>{todo.title}</li>)}</ul>
```

## Need it in an island?

For client-side code (a React or Svelte island), add PUBLIC_SUPABASE_URL and PUBLIC_SUPABASE_ANON_KEY to .env with the same values and create the client from import.meta.env.PUBLIC_ values there. Never give the service role key a PUBLIC_ prefix.

## Agent prompt

```
Add a Scribase backend to this Astro app.
1. Run `npx scribase init --yes --json` in the project root. If it returns authorization_pending, show me the link and code, wait for me to approve, then run it again.
2. Create a `todos` table (id, title, done, created_at) with row-level security on and a policy that lets anyone read it; use the Scribase MCP server or a SQL migration, not the service-role key in app code.
3. Change src/supabase.ts to read import.meta.env.SUPABASE_URL and import.meta.env.SUPABASE_ANON_KEY, then list the todos in the frontmatter of src/pages/index.astro.
4. Never print, log or commit the env file; it is already in .gitignore.
```

## FAQ

**Does it work on a fully static Astro site?**

Yes, for reading at build time. For anything per-user (sign-in, writes), use an island with the PUBLIC_ variables or an SSR adapter.

**Why not detect Astro directly?**

Not yet: init knows Next.js, Expo, Vite and Node today. The edit above is the whole difference.