Getting started
Clone the Rails API starter, install dependencies, point it at a Supabase project, and make your first authenticated request.
This guide takes you from a clean machine to a green test suite and a verified GET /api/v1/me round-trip. Budget 15 minutes if you already have Ruby and Docker installed; 30 if you don't.
Prerequisites
| Tool | Version | Why |
|---|---|---|
| Ruby | matches .ruby-version (Rails 8.1, currently 4.0.x) | App runtime. Install with rbenv, asdf, or mise. |
| Bundler | 4.x | gem install bundler after Ruby is installed. |
| Postgres | 14+ | Rails needs a local DB. The Supabase CLI bundles one — see below. |
| Supabase CLI | 2.x | brew install supabase/tap/supabase, or official install docs. Only needed for a fully local Supabase stack. |
| Docker | recent | Required by supabase start and by the Kamal deploy. |
| Git | any | For cloning. |
You can skip Postgres if you let the Supabase CLI start one in Docker. You can skip the Supabase CLI if you point the kit at a Supabase Cloud project instead.
1. Get the code
The starter is published as a standalone repo, not a generator — copy it, don't render it:
git clone https://github.com/supabase-ruby/starter-kit-api my-api
cd my-apiThen either commit it into a fresh repo of your own, or keep the upstream remote if you want to pull future starter updates.
2. Install gems
bundle installThe first install pulls a Rails 8 baseline plus supabase-rails, rack-attack, rack-cors, rswag-*, and the test stack (rspec-rails, factory_bot_rails).
3. Run the tests
Run the spec suite before any environment setup — it's hermetic. spec/rails_helper.rb seeds SUPABASE_* test ENV with a dummy JWT secret and an in-memory JWKS so signed tokens minted by SupabaseAuthHelper verify without network calls.
bundle exec rspec
# 9 examples, 0 failuresIf you see failures here, stop and read Troubleshooting → Spec suite won't start before going further — a broken hermetic suite usually means a bad Ruby/Bundler install, not a configuration problem.
4. Set environment variables
The dev server needs real Supabase URLs and keys (the spec suite doesn't). Copy the template:
cp .env.example .envYou need four values. Pick A or B depending on whether you want a fully local Supabase or a Cloud project.
A. Local Supabase via the CLI
From the project root:
supabase init # first time only — creates ./supabase
supabase start # boots Postgres + GoTrue + Studio in DockerThe CLI prints something like:
API URL: http://127.0.0.1:54321
DB URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres
anon key: eyJhbGciOi...
JWT secret: super-secret-jwt-token-with-at-least-32-characters-longCopy each into .env:
| CLI key | .env variable |
|---|---|
API URL | SUPABASE_URL |
anon key | SUPABASE_ANON_KEY |
JWT secret | SUPABASE_JWT_SECRET |
DB URL | DATABASE_URL |
B. Supabase Cloud
If you already have a project at supabase.com:
.env variable | Dashboard location |
|---|---|
SUPABASE_URL | Project Settings → API → Project URL |
SUPABASE_ANON_KEY | Project Settings → API → anon / public key |
SUPABASE_JWT_SECRET | Project Settings → API → JWT Settings → JWT Secret |
DATABASE_URL | Project Settings → Database → Connection string (use the pooler) |
The .env file is gitignored. dotenv-rails loads it automatically in development and test. In production, .env is not read — set the same variables through your deploy platform's secret manager (see Deployment).
5. Prepare the database
bin/rails db:prepareEven though the kit has no domain models, Rails 8's Solid Cache / Queue / Cable schemas need to be loaded.
6. Boot the server
bin/rails s
# Listening on http://127.0.0.1:30007. Make your first requests
Smoke-test the two public-shape endpoints:
curl -i http://127.0.0.1:3000/healthz
# HTTP/1.1 200 OK
# {"status":"ok"}
curl -i http://127.0.0.1:3000/api/v1/me
# HTTP/1.1 401 Unauthorized
# {"error":"unauthorized"}The 401 is the right answer — there's no Bearer token on the request.
To hit /api/v1/me successfully, you need a real Supabase JWT. The fastest way to get one in development is from supabase-js in a quick Node script:
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!,
);
const { data, error } = await supabase.auth.signInWithPassword({
email: "alice@example.com",
password: "correct-horse-battery-staple",
});
if (error) throw error;
console.log(data.session!.access_token);Then call the API with that token:
TOKEN="eyJhbGciOi..." # paste the access_token here
curl -i http://127.0.0.1:3000/api/v1/me \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json"
# HTTP/1.1 200 OK
# {"id":"...","email":"alice@example.com","role":"authenticated", ...}For local Supabase, create the user first via Studio (http://127.0.0.1:54323 → Authentication → Users → "Add user"), or via supabase-js's signUp. For a Cloud project, the same flow works against the real auth endpoint.
8. Open the API docs
open http://127.0.0.1:3000/api-docsrswag-ui renders swagger/v1/swagger.yaml. The "Authorize" button there accepts a Bearer token so you can try GET /api/v1/me directly from the page.
Next
- Project structure — what each directory is for.
- Customization — adding your own resource is the natural next step.
Rails API starter
Rails 8 API-only starter kit that authenticates clients with Supabase Auth via supabase-rails — JWT verification, rswag OpenAPI docs, Rack::Attack rate limiting, and a Kamal deploy config.
Project structure
A directory-by-directory walkthrough of the Rails API starter — app/, config/, db/, spec/, and swagger/ — at the level of detail a new contributor needs.