Aurabase Logo
aurabasedocs
docs›Foundations›Quick start

Quick start

From zero to a queryable API: dedicated Postgres database, row-level security, realtime and TypeScript type generation included — no credit card required.

5 min read·Level beginner·Revised on Oct 21, 2026
This English text was generated automatically from the French original and has not been reviewed yet.

Quickstart

From terminal to your first request in 3 simple steps.

About 5 min to your first request
1. Installation
pnpm add @aurabase/aurabase-js
2. Initialisation
import { createClient } from '@aurabase/aurabase-js'

const aura = createClient(
  process.env.NEXT_PUBLIC_AURABASE_URL!,
  process.env.NEXT_PUBLIC_AURABASE_ANON_KEY!,
)
3. DB Query
// `todos` is created in "Create a table" below
const { data, error } = await aura
  .from('todos')
  .select('id, title, done')
  .limit(10)

console.log(data, error)
#
Prerequisite

Prerequisites

We assume that Node.js ≥ 20 is installed and a POSIX terminal is within range. No local database is required: Aurabase provisions a dedicated Postgres database for each project.

Info

Are you coming from Supabase or Firebase? Most concepts are transposable. See the migration guide.

#
Official SDK

Installing the SDK

The official SDK exposes a typed client for CRUD, auth, realtime and storage. It works the same way in Next.js, React, Node, Bun or Deno.

terminalbash
# Recommended
pnpm add @aurabase/aurabase-js

The package name includes the @aurabase/ scope. The package aurabase-js without a scope is not maintained by Aurabase: do not install it.

Astuce

The SDK directly includes @aurabase/aurabase-js/ssr (createServerClient) export for Next.js (App Router, Server Actions, Middleware), Remix, Nuxt and SvelteKit with automatic session cookie management and anti-truncation chunking.

#
Provisioning

Create a project

Create a project from the Studio. When it becomes active, the Studio shows a Copy your keys now panel with a ready-to-use .env.local (copy it or download it): the service_role key appears there once, only its hash is kept afterwards. Settings › Connection later shows the project URL and the public anon key; if you lost the service_role key, regenerate it there (the previous one is revoked). NEXT_PUBLIC_AURABASE_URL and NEXT_PUBLIC_AURABASE_ANON_KEY can be published on the client side; AURABASE_SERVICE_ROLE_KEY stays strictly on the server side.

.env.localbash
NEXT_PUBLIC_AURABASE_URL=https://<slug>.aurabase.cloud
NEXT_PUBLIC_AURABASE_ANON_KEY=aura_anon_…
AURABASE_SERVICE_ROLE_KEY=aura_sk_…

SchemaAccessContains

project_{uuid}userYour business tables

project_{uuid}_authsystemusers, sessions, oauth_accounts, mfa

project_{uuid}_storagesystembuckets, objects, rls_policies

project_{uuid}_platformsystemfunctions, jobs, cron_jobs, api_keys

Prefer the terminal?

The whole path also works from the CLI (see CLI for the installation): aura projects create waits until the project is ready and prints the URL and the keys once.

terminalbash
aura login
aura projects create todos-app --json > project.json    # waits until the project is active
aura link --project-id "$(jq -r .id project.json)"
export NEXT_PUBLIC_AURABASE_URL="$(jq -r .url project.json)"
export AURABASE_SERVICE_ROLE_KEY="$(jq -r .service_role_key project.json)"
aura db raw "create table todos (id bigint generated always as identity primary key, title text not null, done boolean not null default false, user_id uuid not null default auth.uid(), created_at timestamptz not null default now())"
aura db raw "create policy todos_own_read on todos for select to aura_authenticated using (auth.uid() = user_id)"
aura db table realtime todos on
aura db raw "select count(*) from todos" --as authenticated --user <a user id>   # see what that user sees
Service role key

Never include AURABASE_SERVICE_ROLE_KEY in a client bundle. It bypasses all RLS policies, and the API refuses it from a browser (401 SECRET_KEY_IN_BROWSER). Reserve it for server calls (route handlers, server actions, jobs).

#
Schema

Create a table

Open Database › SQL Editor and run this script. Row-level security is enabled on every table: the two policies let each signed-in user read and create their own rows. Policies target the aura_authenticated role (aura_anon for visitors).

todos.sqlsql
create table todos (
  id bigint generated always as identity primary key,
  title text not null,
  done boolean not null default false,
  user_id uuid not null default auth.uid(),
  created_at timestamptz not null default now()
);
create policy "todos_own_read" on todos for select to aura_authenticated using (auth.uid() = user_id);
create policy "todos_own_insert" on todos for insert to aura_authenticated with check (auth.uid() = user_id);

Then generate the TypeScript types of your schema (the command reads .env.local):

terminalbash
npx @aurabase/gen --out lib/aurabase.types.ts
#
First code

First call

Create a reusable Aurabase client by reading keys from environment variables, then query your first table. The paths below are relative to the root of your app: if your project uses a src/ directory, put lib/, components/ and app/ inside it, so that the @/ import alias resolves.

lib/aurabase.tstypescript
import { createClient } from '@aurabase/aurabase-js'
import type { Database } from './aurabase.types'

export const aura = createClient<Database>(
  process.env.NEXT_PUBLIC_AURABASE_URL!,
  process.env.NEXT_PUBLIC_AURABASE_ANON_KEY!,
)

Then, in a route handler or a server component:

app/api/todos/route.tstypescript
import { aura } from '@/lib/aurabase'

export async function GET() {
  const { data, error } = await aura
    .from('todos')
    .select('id, title, done')
    .order('created_at', { ascending: false })
    .limit(10)

  if (error) throw error
  return Response.json(data)
}
Astuce

Without a signed-in user this returns []: the policies above only open rows to aura_authenticated. Sign a user in first with aura.auth.signInWithPassword({ email, password }). Regenerate the Database types with npx @aurabase/gen after each schema change.

To insert a first row, sign a user up and in, then insert. The user_id column defaults to auth.uid():

scripts/first-row.tstypescript
import { aura } from '../lib/aurabase'

const email = 'you@example.com'
const password = 'a-strong-password'

await aura.auth.signUp({ email, password })
await aura.auth.signInWithPassword({ email, password })

const { data, error } = await aura.from('todos').insert({ title: 'First todo' }).select()
console.log(data, error)
Attention

The service_role key has no signed-in user, so auth.uid() is NULL and an insert that relies on the default fails with NOT_NULL_VIOLATION. From a server, pass user_id explicitly.

#
Live by default

Connect realtime

Three auto-negotiated transports: WebSocket, SSE, NATS. Customer chooses the best one based on the network — no configuration necessary.

Enable realtime on the table once, with the CLI:

terminalbash
aura db table realtime todos on

or with the HTTP API, from a server (values from .env.local):

terminalbash
curl -X PUT "$NEXT_PUBLIC_AURABASE_URL/v1/db/schema/tables/todos/realtime" \
  -H "apikey: $AURABASE_SERVICE_ROLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'

Then subscribe to changes. A signed-in user receives the rows that its RLS policies let it read, including when the channel was opened before sign-in (it is re-authenticated automatically); a refused subscription reports CHANNEL_ERROR with the reason.

components/LiveTodos.tsxtypescript
'use client'
import { aura } from '@/lib/aurabase'
import { useEffect } from 'react'

export function LiveTodos() {
  useEffect(() => {
    const channel = aura.realtime
      .channel('todos')
      .on('postgres_changes', { event: '*', schema: 'public', table: 'todos' }, (change) =>
        console.log(change.eventType, change.new),
      )
      .subscribe((status, err) => {
        // In development React StrictMode mounts, unmounts and re-mounts the component:
        // the first CLOSED comes from that clean-up and is expected.
        if (status === 'SUBSCRIBED') console.log('realtime connected')
        if (status === 'CHANNEL_ERROR') console.error('realtime refused:', err?.message)
      })

    return () => channel.unsubscribe()
  }, [])
  return null
}
Astuce

Subscriptions are multiplexed over a single TCP connection. Open as many as you need — zero additional network costs.

CONCEPT

Architecture

The 8 services and their partitioning by project.

SERVICE

Database

Schemas, migrations, TypeScript codegen, RLS.

SERVICE

Authentication

OAuth 15+, MFA, granular RBAC.

SERVICE

Edge Functions

WASM multi-runtime, cron, DLQ, AI Gateway.

Last updated · Oct 21, 2026