TechnicalApril 2026 · 12 min read

Connecting an AI-Generated App to a Real Backend

AI builders get you a working-looking app fast, but the data is often mock until you wire a real backend. This guide walks through the 2026 standard path: Supabase as the default, Firebase and API routes as alternatives, with auth, row-level security, and testing — all in about an hour.

Quick path

Create a Supabase project → define tables matching the AI-generated mock data shape → install @supabase/supabase-js → swap mocks for supabase.from('table').select() → add RLS policies → add auth via Clerk or Supabase Auth. 1–2 hours end to end.

Three backend shapes

  • BaaS (Backend-as-a-Service): Supabase, Firebase. Auth, database, storage, realtime in one product. Simplest choice for most mobile apps.
  • API routes: Expo Router API routes on EAS Hosting, or Next.js routes on Vercel. Good when you need custom server logic or to integrate multiple third-parties.
  • Custom server: Node, Go, Rails, Python on a VPS or container host. Most flexibility, most maintenance. Pick if you have existing infrastructure.

For a first-time AI-generated app, BaaS is the correct default. Specifically Supabase unless you have a reason otherwise.

Step 1: inspect what the AI generated

Before wiring, understand what you have. Open the generated project and look for:

  • Files named mockData.ts, seed.ts, or hardcoded arrays in components.
  • Imports like @supabase/supabase-js — ShipNative ships a wired Supabase layer if you asked for it.
  • Auth files — useAuth, ClerkProvider, or similar.
  • An .env.example listing the environment variables you need.

Step 2: create the Supabase project

  1. Go to supabase.com → New project. Pick region closest to users.
  2. Copy the Project URL and anon key. Set as EXPO_PUBLIC_SUPABASE_URL and EXPO_PUBLIC_SUPABASE_ANON_KEY in your .env.
  3. Open the SQL editor. Create tables matching your mock data shape.
  4. Enable row-level security on every table: alter table X enable row level security;
  5. Add policies for who can read/write each row (scoped by auth.uid() or workspace_id).

Step 3: wire the Supabase client

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

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,
    },
  }
);

Step 4: swap mocks for real queries

Go screen by screen. Before:

const tasks = [
  { id: '1', title: 'Buy milk', done: false },
  { id: '2', title: 'Call mom', done: false },
];

After:

import { useQuery } from '@tanstack/react-query';
import { supabase } from '@/lib/supabase';

const { data: tasks } = useQuery({
  queryKey: ['tasks'],
  queryFn: async () => {
    const { data } = await supabase
      .from('tasks')
      .select()
      .order('created_at', { ascending: false });
    return data;
  },
});

Step 5: add authentication

Two patterns:

  • Supabase Auth — simplest when you only need email, OAuth, or magic links. One less vendor.
  • Clerk + Supabase — Clerk handles user identity and UI; Supabase handles data. Pair them via Clerk’s JWT template pointed at Supabase. More expensive, much better UX.

Full comparison in React Native Authentication 2026.

Step 6: row-level security (RLS)

RLS is what keeps users from seeing each other’s data. Without it, your anon key in the client lets anyone read everything. Example policy:

alter table tasks enable row level security;

create policy "users see their own tasks"
on tasks for select
using (auth.uid() = user_id);

create policy "users insert their own tasks"
on tasks for insert
with check (auth.uid() = user_id);

create policy "users update their own tasks"
on tasks for update
using (auth.uid() = user_id);

create policy "users delete their own tasks"
on tasks for delete
using (auth.uid() = user_id);

Apply this pattern to every user-scoped table. Test by logging in as two different users and confirming each only sees their own data.

Step 7: testing the integration

  • Create two test accounts. Verify they cannot see each other’s data.
  • Try to write to a table the policy shouldn’t allow — should fail.
  • Check the Supabase logs for failed requests — useful for debugging RLS.
  • Test offline by toggling airplane mode. Confirm errors fail gracefully.
  • Verify environment variables load in both dev and production builds.

Common pitfalls

  • Forgetting to enable RLS. By default, Supabase tables are locked down — but if you disabled it during dev and never re-enabled, you have a data leak.
  • Putting the service_role key in the client. Never. Only use it from Edge Functions or server.
  • Mixing Clerk user IDs and Supabase auth.uid() without a JWT template — policies silently fail.
  • Not testing as a different user. Your own account works; a second user reveals the bugs.
  • Syncing every action individually. Batch writes when possible; it saves cost and latency.

Skip the wiring entirely

Apps generated via ShipNative with “Supabase backend + RLS” in the prompt arrive with the client wired, tables named, and baseline policies generated. You still set up your Supabase project and run the SQL, but the JavaScript plumbing is done. See Generate App from Description for how the pipeline works.

Frequently Asked Questions

Does AI-generated code come with a real backend already wired?

Mobile-first AI builders like ShipNative wire a backend layer (Supabase by default) when you mention it in the prompt. Web-first builders often ship with mocked data that looks like a backend but is just local arrays. Read the generated code before assuming — the import statements tell the truth.

Which backend should I pick for an AI-generated app?

Supabase for most mobile apps — auth + SQL + realtime + storage in one bill, excellent Expo compatibility. Firebase if you live in Google Cloud. API routes on Vercel or EAS Hosting if you need custom business logic server-side. See the full comparison in Supabase vs Firebase for React Native.

How do I replace mocked data with real Supabase calls?

Find the arrays or JSON blobs the generated app uses as mock data. Replace each with a Supabase query — useSWR or @tanstack/react-query makes this clean. Start with one screen, verify, expand. Don't swap the whole app at once.

Do I need to understand SQL for Supabase?

Basic SELECT, INSERT, UPDATE, DELETE — yes. Row-level security policies are SQL too. You don't need to be an expert. The Supabase dashboard lets you write most queries visually, and AI builders generate the client-side query code for you.

How do I keep API keys secure?

Supabase anon keys live in the client — that's by design. Your security comes from row-level security policies, not key secrecy. For service_role keys (admin access), never put them in the app. Keep them in server-only environment variables and only use them from Edge Functions or API routes.

Supabase vs Firebase for React Native

Pick the right backend before wiring.

Read comparison →

React Native Authentication 2026

Auth is the first piece of any real backend.

Read guide →

Ship a real React Native app today

Describe, preview, and export Expo code — free to start.

Build with ShipNative →