Add a backend to Nuxt with one command
init does not have Nuxt-specific conventions yet, so it treats a Nuxt app as a Node project: SUPABASE_URL and SUPABASE_ANON_KEY in .env and a client at the root. Nuxt code in the browser cannot read process.env, so move the client into a composable that reads runtimeConfig. That is the one edit.
$ npx scribase init
Backend ready: https://acme-my-app-production-c2b0ea0a.scribase.com
framework node
project acme/my-app (production)
env file .env (SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, SCRIBASE_API_URL, SCRIBASE_ORG, SCRIBASE_PROJECT, SCRIBASE_ENV)
client supabase.ts
Next:
- import { supabase } from './supabase' and query: await supabase.from('todos').select()
- create tables with SQL migrations (supabase/migrations/*.sql) and `scribase db push`, or the MCP server1.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.
npx scribase init2.Expose the public values through runtimeConfig
Nuxt loads .env in development and reads process.env when it builds the config. Only the public pair goes under public; the service role key stays server-only.
export default defineNuxtConfig({
runtimeConfig: {
supabaseServiceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY,
public: {
supabaseUrl: process.env.SUPABASE_URL,
supabaseAnonKey: process.env.SUPABASE_ANON_KEY,
},
},
});3.Replace the generated client with a composable
Delete supabase.ts at the root and add this. Nuxt auto-imports it everywhere.
import { createClient, type SupabaseClient } from '@supabase/supabase-js';
let client: SupabaseClient | undefined;
export function useSupabase(): SupabaseClient {
const { supabaseUrl, supabaseAnonKey } = useRuntimeConfig().public;
client ??= createClient(supabaseUrl as string, supabaseAnonKey as string);
return client;
}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.
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.Use it in a page
useAsyncData fetches on the server for the first render and hydrates on the client.
<script setup lang="ts">
const { data: todos } = await useAsyncData('todos', async () => {
const { data, error } = await useSupabase().from('todos').select('id, title');
if (error) throw error;
return data;
});
</script>
<template>
<ul><li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li></ul>
</template>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.
Add a Scribase backend to this Nuxt 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. Expose SUPABASE_URL and SUPABASE_ANON_KEY through runtimeConfig.public in nuxt.config.ts (service role key server-only), replace the generated root supabase.ts with composables/useSupabase.ts, and list the todos on pages/index.vue with useAsyncData.
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.
Common questions
In production the values are empty. Why?
Nuxt reads .env only in development. In production set NUXT_PUBLIC_SUPABASE_URL and NUXT_PUBLIC_SUPABASE_ANON_KEY (Nuxt maps them onto runtimeConfig.public), or set the SUPABASE_ variables at build time.
Can I use a Nuxt module instead?
Any module built on supabase-js works, because Scribase speaks the same API. Point it at the same URL and publishable key.
Your Nuxt 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