How to connect Supabase to a React Native Expo template with the Supabase MCP server
Step-by-step: add a real Supabase backend (auth, Postgres, RLS, storage) to an Expo React Native template using Claude Code and the Supabase MCP server. Schema, security checks and typed client included.
September 24, 2026 · Thomino
Every one of our templates ships as front end only. The login screen looks finished, the profile screen has an avatar picker, the lists are full of content, and none of it talks to a server. That's on purpose: you pick the backend, not us.
This post shows how quickly that backend goes from "later" to "done". I took the Front-End Starter, opened it in Claude Code, connected the Supabase MCP server, and ended up with real email sign-up, a Postgres database with row level security, file uploads and generated TypeScript types. The front end barely changed, because the screens were already built.
To keep it concrete, the example here is a small reading list app: users sign up, save links, and organise them into collections. Swap in your own tables; the steps are the same.
Why Supabase for a React Native app
Supabase is Postgres with the parts a mobile app needs around it:
- Auth with email, magic links, Apple and Google, and sessions that persist on the device.
- A REST API generated from your tables, so there's no server code for basic reads and writes.
- Row level security (RLS), which lets the app talk to the database directly while each user only sees their own rows.
- Storage for avatars and uploads, protected by the same kind of policies.
supabase-js, which works in Expo with AsyncStorage and no native modules.
For a template buyer that last point matters. You don't need to eject, add native code or rebuild the dev client to get a working backend.
What the Supabase MCP server does
MCP (Model Context Protocol) is how coding agents like Claude Code and Cursor talk to external tools. The Supabase MCP server gives the agent direct, authenticated access to your Supabase project. With it connected, the agent can:
- list your tables and read the schema before changing anything,
- apply migrations (tables, policies, functions),
- run SQL to test what it built,
- run Supabase's security and performance advisors and fix what they flag,
- fetch your project URL and publishable key,
- generate TypeScript types from the live schema,
- search the current Supabase docs instead of relying on memory.
That last one sounds minor and isn't. Supabase changes often (new key formats, deprecated helpers, new defaults), and an agent working from training data will happily write last year's code. With the docs tool it checks first.
The practical result: you describe the backend in plain language and review what it did, instead of copying SQL between a dashboard and your editor.
Step 1: Create a Supabase project
Create a project at supabase.com. The free plan is enough for development. Note the project ref, the short id in your project URL (https://<project-ref>.supabase.co).
Step 2: Add the Supabase MCP server to your project
Supabase gives you a ready-made command in the dashboard (Connect → MCP). For Claude Code it looks like this:
--scope project writes the config to .mcp.json in your repo, so everyone who opens the project gets the same server. project_ref pins the server to one project, which is what you want: the agent can't touch your other projects by accident.
Then authenticate. In a regular terminal (not an IDE panel), start Claude Code and run:
Select supabase, choose Authenticate and finish the login in your browser. Back in the terminal the Supabase tools are now available.
Optionally, install Supabase's agent skills. They're instructions the agent loads for Supabase work: security rules, migration workflow, and common mistakes to avoid.
Cursor uses the same MCP URL; add it under Settings → MCP and authenticate there.
Step 3: Let the agent read the template first
Before any backend work, the agent should know what the app expects. Our templates include a CLAUDE.md that describes the stack, the folder structure and the rules, including this line:
All data is local mock data — there is no backend, no real auth (AuthContext holds a hard-coded mock profile in memory).
That tells the agent exactly where the seam is. Auth lives in one context. Screens call useAuth() and render profile. Replace the inside of that context and the screens don't need to change. (More on why this matters for agents in Starting a React Native app with Claude Code or Cursor.)
A good first prompt:
Read CLAUDE.md and the auth screens. Then check the Supabase project with the MCP tools: list tables and extensions. Don't change anything yet — tell me what you'd build for a reading list app where users save links into collections.
Reading before writing avoids most of the rework.
Step 4: Design the schema and security in one migration
Here's the prompt that does the real work:
Create the schema for the reading list app as a migration: a
profilestable linked toauth.usersand created automatically on sign-up,collectionsowned by a user, andsaved_linksbelonging to a collection. Enable RLS on every table so users can only read and write their own rows. Add anavatarsstorage bucket where users can only upload into their own folder. Show me the SQL before applying it.
The agent produced something close to this (trimmed):
Plus a trigger that inserts a profiles row whenever someone signs up, and storage policies that check the first folder of the file path equals the user's id.
A few details worth noticing, because they're the ones people get wrong by hand:
to authenticatedplus an ownership check.to authenticatedalone only checks that someone is logged in. Withoutauth.uid() = user_id, every user could read every other user's data.with checkon writes. Without it, a user could update a row and change itsuser_idto someone else's.(select auth.uid())instead ofauth.uid(). Wrapping it in a select lets Postgres evaluate it once per query instead of once per row. On large tables this is the difference between fast and slow.- Indexes on the columns the policies filter on. RLS adds a
whereclause to every query; it needs an index like any other filter. - The sign-up trigger runs as
security definerin a private schema. It has to write toprofileson behalf of the auth system, but it shouldn't be callable from the app.
The agent applies the migration with the MCP apply_migration tool, which also records it in the project's migration history.
Step 5: Check it instead of trusting it
This is where MCP pays for itself. Ask:
Run the security and performance advisors. Then test the RLS policies: create two test users inside a transaction, have user A create a collection, and confirm user B can't read, update or delete it, and that anonymous users see nothing. Roll everything back afterwards.
The agent runs the advisors (in our case: one missing index on a foreign key, fixed with a second small migration) and then runs a SQL block that impersonates each user by setting the JWT claims inside a transaction, tries each operation, and reports the results:
Then it raises an error on purpose so the transaction rolls back and no test data is left behind.
You'd normally do this by clicking around two accounts in a simulator. Here it takes one prompt and covers the cases you'd forget.
Step 6: Connect the Expo app
Now the front end. Install the client:
Ask the agent to fetch the project URL and publishable key with the MCP tools and write them to .env.local:
The publishable key is designed to ship inside the app. It can only do what your RLS policies allow, which is why step 4 matters. Never put the secret (service role) key in a mobile app; anything in the bundle can be extracted.
The client, following Supabase's current React Native guide:
database.types.ts comes from the MCP generate_typescript_types tool. Every query is now typed against your real schema: a typo in a column name is a compile error, not a runtime bug.
Step 7: Replace the mock auth with real auth
In the template, AuthContext holds a hard-coded profile. The agent rewrote it to:
- load the stored session on launch and listen to
onAuthStateChange, - fetch the signed-in user's row from
profiles, - expose
signIn,signUp,signOut,resetPassword,updateProfileanduploadAvatar.
The existing screens only needed their buttons wired up. The login screen already had email and password inputs and a loading state on the button; onPress now calls signIn and shows the error message if it fails. The edit profile screen already had an image picker; the chosen photo now uploads to the avatars bucket before the profile is saved.
The one new piece is an auth gate in the root layout: signed-out users go to the welcome screen, signed-in users skip the login screens. Around twenty lines with Expo Router's useSegments.
Step 8: Test the whole flow for real
Before touching a simulator, ask the agent to test end to end against the live project:
Sign up a throwaway user through the Auth API, update their profile, upload an avatar into their folder, try to upload into someone else's folder, then delete the user and the file.
Expected results: sign-up returns a session, the profile row exists, your own upload works, the other upload is rejected by the storage policy, and cleanup leaves the project empty. Then run the app and sign up for real.
What you end up with
Starting from a template with finished screens and no backend:
- email sign-up, login, logout and password reset,
- a Postgres schema with row level security on every table, tested,
- avatar uploads to Supabase Storage,
- migration files in your repo,
- TypeScript types generated from the live database,
- an updated
CLAUDE.md, so the next agent session knows there's a backend and how to use it.
The template did the part that takes the longest, the UI, and the agent with the Supabase MCP server did the plumbing. Your job was mostly to describe the data and read what came back.
Tips so it goes smoothly
- Keep the MCP server scoped to one project with
project_ref. Use a separate development project if you already have users in production. - Ask for SQL before it's applied on anything you care about. Reviewing a migration takes a minute.
- Always ask for advisors and RLS tests after schema changes. Missing policies are the most common Supabase security issue and the advisors catch them.
- Turn on email confirmation before launch. For development it's convenient to have it off; in production you want verified emails.
- Regenerate types after every migration so the app and the database never drift.
- Commit after each step. If the agent goes sideways, you throw away one step, not the afternoon.
FAQ
Do I need Claude Code to use Supabase with a React Native template?
No. Everything above can be done by hand with the Supabase dashboard and supabase-js. The MCP server just lets an agent do the repetitive parts (migrations, policies, type generation, testing) and check its own work. Cursor and other MCP-capable editors work the same way.
Does Supabase work with Expo Go?
Yes. supabase-js is plain JavaScript and stores the session with AsyncStorage, so there are no native modules to install. Apple and Google sign-in need extra setup for production builds, but email auth works in Expo Go out of the box.
Is it safe to put the Supabase key in my app?
The publishable key (or the legacy anon key) is meant to be public, as long as row level security is enabled and your policies are correct. The secret / service role key bypasses RLS and must never ship in an app.
Can I use Firebase or my own API instead?
Yes. Our templates don't assume a backend. The auth context and the screens are the same either way; you replace the inside of the context with calls to whatever you use. Supabase is just the fastest path we've found, because the MCP server lets the agent build and verify the backend without leaving the editor.
Which template should I start from?
If your app matches a category, start from the closest one: ecommerce, social, booking, dating, fitness, AI chat and more, so you get the screens and the agent gets working examples. For something new, the Front-End Starter gives you the component library, navigation and auth screens without a specific app on top. Want to try the workflow first? The free template is real code, and the vibe coding page explains how our templates are set up for AI agents.
All templates are TypeScript on Expo SDK 57 with NativeWind, and every one ships with a CLAUDE.md, which is exactly what makes connecting a backend like this a short job instead of a rewrite.