# Add a backend to Next.js with one command

> Give a Next.js app Postgres, auth, storage and realtime with `npx scribase init`: it writes .env.local, installs supabase-js and adds lib/supabase.ts.

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

One command turns a Next.js app into a full-stack app: a Postgres database with auth, storage and realtime, the keys in .env.local and a ready client module. Works with the App Router and the Pages Router.

## `npx scribase init` output

```
$ npx scribase init
Backend ready: https://acme-my-app-production-c2b0ea0a.scribase.com
  framework   next
  project     acme/my-app (production)
  env file    .env.local (NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_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.local

- NEXT_PUBLIC_SUPABASE_URL
- NEXT_PUBLIC_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
```

## What it wrote

The NEXT_PUBLIC_ pair is safe in the browser: the anon key only reaches rows your policies allow. SUPABASE_SERVICE_ROLE_KEY has no NEXT_PUBLIC_ prefix, so Next.js never bundles it; use it only in server code that must bypass row-level security. With a src/ folder the client goes to src/lib/supabase.ts instead.

_.env.local_
```bash
# Scribase (written by `npx scribase init`)
NEXT_PUBLIC_SUPABASE_URL=https://acme-my-app-production-c2b0ea0a.scribase.com
NEXT_PUBLIC_SUPABASE_ANON_KEY=<publishable key>
SUPABASE_SERVICE_ROLE_KEY=<service role key>
SCRIBASE_API_URL=https://api.scribase.com
SCRIBASE_ORG=acme
SCRIBASE_PROJECT=my-app
SCRIBASE_ENV=production
```

## 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');
```

## Read it from a Server Component

The generated client works in Server Components, Route Handlers, Server Actions and client components. Restart `next dev` once so it picks up the new .env.local.

_app/page.tsx_
```tsx
import { supabase } from '../lib/supabase';

export default async function Page() {
  const { data: todos, error } = await supabase.from('todos').select('id, title, done');
  if (error) throw error;
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}
```

## Deploying on Vercel

If the folder is linked to Vercel (`vercel link`), init also sets the same variables on the Vercel project for development, preview and production, so the first deploy already talks to your backend. Turn it off with --no-vercel.

## Agent prompt

```
Add a Scribase backend to this Next.js 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. Render the todos on the home page (app/page.tsx) with the client in lib/supabase.ts, as a Server Component.
4. Never print, log or commit the env file; it is already in .gitignore.
```

## FAQ

**Does it work with the Pages Router?**

Yes. The client module is plain supabase-js; call it from getServerSideProps, API routes or components the same way.

**Is the service role key exposed to the browser?**

No. Next.js only inlines variables that start with NEXT_PUBLIC_. Keep SUPABASE_SERVICE_ROLE_KEY in server-only code.

**I run init again. Does it create a second project?**

No. It remembers the project in .scribase/project.json and reuses it, refreshing the env file.