supabase-rb-rb
Rails API starter

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.

The Rails API starter is a thin, production-shaped Rails 8 backend for clients that already have a Supabase identity. Mobile apps, single-page apps, and other server-to-server callers sign in directly with Supabase, then call this API with Authorization: Bearer <jwt>. The starter never mints its own sessions, never holds a cookie, never serves HTML — it verifies the JWT on every request through supabase-rails middleware, populates Current.user from the verified claims, and returns JSON.

What is it

A :api-mode Rails 8 app pre-wired with the pieces every JSON API needs the day it ships:

  • JWT authsupabase-rails middleware in front of /api/* verifies tokens against your project's JWKS. Invalid tokens never reach a controller.
  • Rate limitingRack::Attack runs ahead of JWT verification with two throttles (per-IP and per-token) on /api/*.
  • CORSRack::Cors driven by a CORS_ORIGINS env var; safe-by-default (no origins allowed in production until you set it).
  • OpenAPIrswag integration specs are the source of truth for swagger/v1/swagger.yaml, served at /api-docs by rswag-ui.
  • Tests — RSpec request specs and rswag integration specs that mint real signed JWTs against an in-memory JWKS.
  • DeployDockerfile and a Kamal config/deploy.yml for a single-command production push.

Who it's for

You should pick this kit when:

  • Your authenticated clients are not browsers asking the same Rails app to render HTML — they're a mobile app, an SPA, a CLI, or another backend.
  • You want Supabase Auth to own the identity surface (sign-up, email confirmation, OAuth, password reset, MFA) and your Rails app to focus on domain logic.
  • You'd rather verify a JWT on every request than carry a server-side session.

If your frontend is a Rails view layer, look at the Hotwire or Inertia + React starters instead — both run supabase-rails in :web mode with cookie sessions, which is the right shape for a server-rendered monolith.

What's included

LayerWhat ships in the box
Authsupabase-rails middleware, Authentication concern, JsonUnauthorizedResponder for canonical 401 bodies
EndpointsGET /api/v1/me (authenticated), GET /healthz (public liveness), GET /up (Rails default)
ThrottlingRack::Attack with api/ip (300/5min) and api/token (1000/min) limiters
CORSRack::Cors keyed off CORS_ORIGINS, off-by-default in production
TestsRSpec request specs, rswag integration specs, Minitest infrastructure tests, SupabaseAuthHelper JWT minter
Docsswagger/v1/swagger.yaml, /api-docs UI gated by SWAGGER_UI_ENABLED in production
DeployDockerfile, Kamal config/deploy.yml, .kamal/secrets template, Hetzner-shaped defaults

What's not included

The kit is deliberately small so you can grow it in whichever direction your product needs. It does not ship:

  • A domain model. There's no User AR record, no business-logic models, no migrations beyond Solid Cable/Cache/Queue schemas. Current.user is a value object built from JWT claims — opt into a host-app users table later with bin/rails generate supabase:user_model.
  • Server-rendered views or cookie sessions. config.api_only = true, no ActionDispatch::Cookies, no CSRF tokens. If you need either, use the Hotwire or Inertia starters.
  • A background job pipeline. Solid Queue is configured but unused — the kit has no jobs.
  • Per-target deploy guides. Dockerfile and Kamal config are generic; turning them into a Fly/Render/Heroku flow is left to your platform.
  • Multi-tenancy primitives. No org/team scoping, no per-tenant Postgres schemas — bring your own.
  • Observability. Logs go to STDOUT, nothing else. Wire up your APM (Sentry, Datadog, OpenTelemetry) when you need it.

Next

Repository

On this page