# Add a backend to a React Native app with one command

> Add Postgres, auth and storage to a bare React Native CLI app: `npx scribase init` provisions the backend and writes the client, no Expo needed.

Source: https://scribase.com/frameworks/react-native

init detects a React Native app (react-native plus app.json) and uses the Expo conventions: EXPO_PUBLIC_ variables in .env. Bare React Native CLI apps do not read those at build time, so there is one small edit. Using Expo? Follow the Expo guide instead; it needs no edit.

## `npx scribase init` output

```
$ 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
```

## Environment file: .env

- EXPO_PUBLIC_SUPABASE_URL
- EXPO_PUBLIC_SUPABASE_ANON_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
```

## Put the two public values in a config module

Metro without Expo does not replace process.env.EXPO_PUBLIC_ values, so the generated client would get undefined. Copy the URL and the publishable key from .env into a small module. Both are public by design (they ship in every client build); row-level security protects the data.

_lib/supabase.ts_
```ts
import 'react-native-url-polyfill/auto';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createClient } from '@supabase/supabase-js';

const SUPABASE_URL = 'https://acme-my-app-production-c2b0ea0a.scribase.com'; // from .env
const SUPABASE_ANON_KEY = '<publishable key>'; // from .env

export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
  auth: { storage: AsyncStorage, autoRefreshToken: true, persistSession: true, detectSessionInUrl: false },
});
```

## Install the two native helpers

AsyncStorage keeps sessions across restarts and the URL polyfill fills a gap in the React Native runtime. Then run `npx pod-install` for iOS.

```bash
npm install @react-native-async-storage/async-storage react-native-url-polyfill
```

## 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 it

Any screen can now read and write through the client.

_App.tsx_
```tsx
const { data: todos, error } = await supabase.from('todos').select('id, title');
if (error) console.warn(error.message);
```

## Agent prompt

```
Add a Scribase backend to this bare React Native CLI 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. Rewrite lib/supabase.ts to read the URL and publishable key as constants copied from .env (never the service role key), add AsyncStorage and react-native-url-polyfill, and list the todos on the first screen.
4. Never print, log or commit the env file; it is already in .gitignore.
```

## FAQ

**Is it safe to put the key in source code?**

The publishable (anon) key, yes: it ships in every client build anyway and can only reach rows your policies allow. Never put the service role key in an app.

**Can I use react-native-config instead?**

Yes. Rename the two variables in .env to whatever your config library expects and read them from there.