scribase
One command

Add a backend to an Expo app with one command

One command gives an Expo app a real backend: database, sign-in, file storage and realtime, with the public keys in .env and a client ready to import. The service role key is never written, because anything in an Expo env file can end up inside the app binary.

What you see (your project URL will differ)terminal
$ npx scribase init
Backend ready: https://acme-my-app-production-c2b0ea0a.scribase.com
  framework   expo
  project     acme/my-app (production)
  env file    .env (EXPO_PUBLIC_SUPABASE_URL, EXPO_PUBLIC_SUPABASE_ANON_KEY, SCRIBASE_API_URL, SCRIBASE_ORG, SCRIBASE_PROJECT, SCRIBASE_ENV)
  client      lib/supabase.ts

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

1.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

2.What it wrote

Expo inlines EXPO_PUBLIC_ variables at build time. Only the project URL and the publishable key are written; both are meant to ship in the app, and row-level security decides what they can read.

.envbash
# Scribase (written by `npx scribase init`)
EXPO_PUBLIC_SUPABASE_URL=https://acme-my-app-production-c2b0ea0a.scribase.com
EXPO_PUBLIC_SUPABASE_ANON_KEY=<publishable key>
SCRIBASE_API_URL=https://api.scribase.com
SCRIBASE_ORG=acme
SCRIBASE_PROJECT=my-app
SCRIBASE_ENV=production

3.Keep users signed in

The generated client works as is. To keep sessions across app restarts, pass AsyncStorage, as the comment in the file suggests.

lib/supabase.tsts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(
  process.env.EXPO_PUBLIC_SUPABASE_URL!,
  process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
  { auth: { storage: AsyncStorage, autoRefreshToken: true, persistSession: true, detectSessionInUrl: false } },
);

4.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');

5.Show it on screen

Restart the dev server with `npx expo start --clear` so the new variables are inlined.

app/index.tsxtsx
import { useEffect, useState } from 'react';
import { FlatList, Text } from 'react-native';
import { supabase } from '../lib/supabase';

type Todo = { id: number; title: string };

export default function Home() {
  const [todos, setTodos] = useState<Todo[]>([]);
  useEffect(() => {
    supabase.from('todos').select('id, title').then(({ data }) => setTodos(data ?? []));
  }, []);
  return <FlatList data={todos} keyExtractor={(t) => String(t.id)} renderItem={({ item }) => <Text>{item.title}</Text>} />;
}
Let your agent do it

Paste this into Claude Code, Cursor or Codex

The agent runs init, asks you to approve once in the browser, creates the table and wires up the first screen.

Expo setup promptprompt
Add a Scribase backend to this Expo 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. Show the todos in a FlatList on the first screen, and make the client persist sessions with AsyncStorage.
4. Never print, log or commit the env file; it is already in .gitignore.

Connect the MCP server first so the agent can run SQL safely: connect your agent.

FAQ

Common questions

Why is there no service role key in .env?

Expo can bundle any env value into the app. init never writes the service role key for Expo, so it cannot leak into a store build.

Does it work with EAS Build?

Yes. Set the two EXPO_PUBLIC_ variables as EAS environment variables (or keep .env in the build) and they are inlined at build time.

Can I sign users in with Apple or Google?

Yes. Auth speaks the supabase-js API, including signInWithIdToken for native Apple and Google sign-in.

Your Expo backend, live in a minute

$9 a month per project, flat. Sign up with one click; the $1 card check is credited to your first invoice.

Start now