Stack

Nuxt 3 + Supabase

A production-ready PWA stack.

Every developer has a default stack — the combination of tools they reach for when starting a new project. Mine is Nuxt 3 and Supabase. Not because it's perfect, but because it solves the right problems for the applications I typically build.

Why These Two Work Together

Nuxt handles the frontend. Server-side rendering when needed, static generation when appropriate, client-side navigation throughout. The file-based routing, auto-imports, and Vue 3 composition API make development fast without sacrificing structure.

Supabase handles the backend. Database (Postgres), authentication, file storage, realtime subscriptions, and edge functions. It replaces what would otherwise be four or five separate services.

The integration is clean. Nuxt's server routes can call Supabase directly with service role access. The client-side can use Supabase's JavaScript SDK with row-level security. Realtime subscriptions push updates without polling.

What Supabase Replaces

Before Supabase, a typical stack might include:

  • A separate auth service (Auth0, Firebase Auth, custom JWT implementation)
  • A database with its own hosting (PlanetScale, Railway, managed Postgres)
  • File storage (S3, Cloudinary, custom uploads)
  • A realtime layer (Pusher, Socket.io server, Ably)
  • Serverless functions (Vercel, Netlify, AWS Lambda)

Supabase consolidates these. One dashboard, one SDK, one billing relationship. The cognitive overhead reduction is significant.

Auth Done Right

Authentication is where Supabase shines brightest. Email/password, magic links, OAuth providers, phone auth — all built in. Row-level security policies let you write authorization logic directly in the database, which means your API can't accidentally expose data it shouldn't.

The Nuxt integration handles session management automatically. Protected routes, middleware for auth checks, server-side session validation — it works out of the box with the official @nuxtjs/supabase module.

Realtime Without Complexity

Realtime data is traditionally painful. WebSocket servers, connection management, reconnection logic, message broadcasting. Supabase abstracts all of it. Subscribe to database changes with a few lines of code. The client SDK handles connections, reconnections, and cleanup.

For applications where users need to see updates from other users — collaborative tools, dashboards, notification systems — this eliminates days of infrastructure work.

Where Custom Logic Still Lives

Supabase isn't a complete backend replacement. Complex business logic doesn't belong in database functions or edge functions alone. Some things still need custom code:

  • External integrations. Payment processing, email services, third-party APIs — these need server-side code. Nuxt's server routes or Supabase edge functions handle this, but you're writing custom logic either way.
  • Complex validation. Database constraints catch some issues. RLS policies catch others. But multi-step validation flows, business rule enforcement, and cross-entity checks often need application code.
  • Background jobs. Supabase doesn't have built-in job queues. For scheduled tasks, webhook processing, or long-running operations, you need additional infrastructure (Vercel cron, external queue services, etc.).

Scaling Considerations

Supabase scales vertically with their managed Postgres instances. For most applications, the Pro tier handles significant load. But there are architectural decisions worth making early:

  • Connection pooling. Supabase uses PgBouncer for connection pooling. Understand the limits of your tier and design accordingly. Serverless functions can exhaust connection pools if you're not careful.
  • RLS performance. Row-level security policies run on every query. Complex policies with subqueries can become bottlenecks. Keep policies simple and test with realistic data volumes.
  • Realtime limits. Concurrent connections have limits per tier. If you're building something with many simultaneous users, verify the limits before committing.
  • Storage costs. File storage is cheap, but egress adds up. CDN caching and image optimisation matter at scale.

When I Wouldn't Use This Stack

This stack isn't universal. I'd choose differently for:

  • Heavy computation. Data processing, ML inference, video transcoding — Supabase edge functions aren't the right tool. Use dedicated compute.
  • Extreme scale. Millions of concurrent users, hundreds of thousands of writes per second — you'd outgrow managed Postgres. But that's a good problem to have.
  • Offline-heavy apps. Supabase's realtime is great when connected. For true offline-first with complex sync, you might want a local-first database like RxDB or PowerSync on top.
  • Multi-tenant isolation. If you need strict data isolation between tenants (separate databases, not just RLS), the architecture gets more complex.

Why It Became My Default

Developer experience matters. The time from idea to working prototype with this stack is measured in hours, not days. That speed compounds over a project's lifetime — faster iterations, more experiments, quicker pivots.

The stack is also boring in the best way. Postgres is proven. Vue is mature. Nuxt is stable. Supabase is backed by solid infrastructure. I'm not debugging framework issues or waiting for library updates to fix critical bugs.

For data-driven applications — dashboards, admin tools, content platforms, workflow systems — this combination handles 90% of requirements with minimal friction. The remaining 10% can be solved with targeted additions rather than architectural changes.

See the Architecture

Detailed walkthrough and architecture diagrams available.

HD
Headless Digital

Senior, hands-on, and accountable. No inflated teams. No unnecessary layers.

Connect

© 2025 Headless Digital. All rights reserved.

Built withNuxt & Tailwind