Plugin Database Auth

Supabase Integration

Seamless integration of Supabase into the Nimbus framework. Use Supabase Postgres as your primary database with Lucid, handle user authentication using GoTrue-compliant middleware, perform S3-compatible storage operations, and tap into real-time WebSockets.

§ Overview

The Supabase plugin combines multiple backend capabilities into a unified SDK designed specifically for Nimbus applications:

Primary GORM Connection

Routes GORM/Lucid calls to the Supabase database instance automatically when selected as the primary driver.

Stateless Guard & Middleware

Decrypts and verifies Supabase JWT access tokens inside the HTTP router to identify users.

Dual Storage Support

Support for native storage operations or unified Nimbus Drive storage mapping (S3-compatible).

Real-time Events & Presence

WebSocket bindings to listen for database changes, broadcast events, or track user presence.

§ Installation & Scaffolding

During new project creation, select Supabase (Postgres) as your database driver. The CLI will automatically configure the Supabase Auth Guard and add the plugin to your project:

$ nimbus new my-app

To add the plugin to an existing project, install it via the CLI:

$ nimbus plugin install supabase

This command scaffolds config/supabase.go, registers the plugin in bin/server.go, and adds credentials to your environment files.

§ Configuration & Credentials

Fill in your Supabase project values in .env:

# Supabase API Credentials
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5...
SUPABASE_JWT_SECRET=your_jwt_signing_secret    # REQUIRED for auth middleware

⚠ Important: SUPABASE_JWT_SECRET is the HMAC signing secret from Supabase Dashboard → Settings → API → JWT Secret. It is required for the auth middleware. Do not confuse it with the anon key.

# Supabase Postgres Connection URL SUPABASE_DB_URL=postgres://postgres:password@db.your-project.supabase.co:5432/postgres # Supabase Storage S3-compatible endpoints (optional, for unified Drive plugin) SUPABASE_STORAGE_BUCKET=avatars SUPABASE_STORAGE_REGION=us-east-1 SUPABASE_STORAGE_KEY=your_s3_storage_access_key SUPABASE_STORAGE_SECRET=your_s3_storage_secret SUPABASE_ENDPOINT=https://your-project.supabase.co/storage/v1/s3

§ Lucid / Database Connection

When DB_DRIVER=supabase is set alongside SUPABASE_DB_URL in your environment, GORM handles connection bootstrap and redirects queries to the Supabase database instance. You can run Lucid migrations and make standard GORM queries as usual:

import (
    "github.com/CodeSyncr/nimbus"
    "app/models"
)

// Queries are executed against Supabase Postgres
var user models.User
nimbus.DB().First(&user, 1)

§ Authentication & Middleware

The plugin includes middleware to decode, verify, and validate Supabase JWT tokens. Claims are loaded onto the context automatically.

Registering Middleware

Register the middleware on a group of routes to require authentication:

package start

import (
    "github.com/CodeSyncr/nimbus/plugins/supabase"
    "github.com/CodeSyncr/nimbus/router"
)

func RegisterRoutes(r *router.Router) {
    // Authenticated API routes
    authRoutes := r.Group("/api", supabase.AuthMiddleware())
    authRoutes.Get("/profile", func(ctx *http.Context) error {
        userID := supabase.GetUserID(ctx)
        return ctx.JSON(200, map[string]string{"user_id": userID})
    })

    // Optional auth routes (injects claims if token exists but doesn't reject)
    publicRoutes := r.Group("/public", supabase.OptionalAuthMiddleware())
    publicRoutes.Get("/posts", func(ctx *http.Context) error {
        claims := supabase.GetClaims(ctx)
        if claims != nil {
            // User is authenticated
        }
        return ctx.JSON(200, posts)
    })
}

Auth Client Operations

Interact directly with the GoTrue backend using the Auth client to sign users up, sign them in, or verify OTPs:

import "github.com/CodeSyncr/nimbus/plugins/supabase"

client := supabase.GetClient()

// Sign up a new user
session, err := client.Auth.SignUp(supabase.SignUpRequest{
    Email:    "user@example.com",
    Password: "password123",
})
if err != nil {
    return err
}

// Sign in with email and password
session, err := client.Auth.SignInWithPassword(supabase.SignInRequest{
    Email:    "user@example.com",
    Password: "secret-pass",
})
if err != nil {
    return err
}

// Access the access token or user ID
token := session.AccessToken
uid := session.User.ID

§ File Storage

You can manage and interact with Supabase Storage buckets using either the native client or through the unified Nimbus Drive abstraction.

1. Native Storage Client

The native storage client utilizes your standard Supabase URL and credentials, removing the need for separate S3 keys:

import (
    "bytes"
    "github.com/CodeSyncr/nimbus/plugins/supabase"
)

client := supabase.GetClient()
bucket := client.Storage.From("avatars")

// Upload a file
fileData := []byte("image-data")
err := bucket.Upload("users/123/profile.png", bytes.NewReader(fileData), "image/png")

// Create a signed URL (expires in 1 hour)
url, err := bucket.CreateSignedURL("users/123/profile.png", 3600)

2. Unified Drive Client

Map Supabase Storage as a disk in the Nimbus Drive system using S3 compatibility. Once configured, you can call storage operations through the unified `drive` package:

import (
    "strings"
    "github.com/CodeSyncr/nimbus/plugins/drive"
)

// Select the Supabase Storage disk
supabaseDisk := drive.Disk("supabase")

// Put a file
err := supabaseDisk.Put("documents/invoice.pdf", strings.NewReader("pdf-content"))

// Check if file exists
exists, err := supabaseDisk.Exists("documents/invoice.pdf")

// Delete the file
err = supabaseDisk.Delete("documents/invoice.pdf")

§ Real-time Subscriptions

Listen to broadcast messages, Postgres updates, or user presence changes using WebSockets:

import (
    "fmt"
    "github.com/CodeSyncr/nimbus/plugins/supabase"
)

client := supabase.GetClient()

// Optional: observe reconnection errors
client.Realtime.OnError = func(err error) {
    fmt.Printf("realtime: %v\n", err)
}

// Connect to the WebSocket server (auto-reconnects on disconnect)
err := client.Realtime.Connect()
if err != nil {
    return err
}
defer client.Realtime.Close()

// Join a channel
channel := client.Realtime.Channel("room-1")

// 1. Subscribe to Postgres Database Changes
channel.OnPostgresChanges(supabase.EventInsert, func(payload supabase.ChangePayload) {
    fmt.Printf("New record in %s: %v\n", payload.Table, payload.Record)
})

// 2. Listen to Broadcast messages
channel.OnBroadcast("chat-message", func(payload supabase.BroadcastPayload) {
    fmt.Printf("Broadcast received: %v\n", payload.Payload)
})

// 3. Track Presence state changes
channel.OnPresence(func(payload supabase.PresencePayload) {
    fmt.Printf("Presence update: key=%s\n", payload.Key)
})

// Subscribe to the channel
err = channel.Subscribe()

§ Edge Functions & Remote Procedure Calls (RPC)

Call custom database functions (RPC) or trigger deployed Supabase Edge Functions directly from Go:

import (
    "fmt"
    "github.com/CodeSyncr/nimbus/plugins/supabase"
)

client := supabase.GetClient()

// Call a stored SQL procedure with parameters
var total int
err := client.Rpc("calculate_total", map[string]any{"user_id": 123}, &total)
if err != nil {
    return err
}
fmt.Printf("User Total: %d\n", total)

// Trigger a deployed Edge Function
response, err := client.Functions.Invoke("send-welcome-email", map[string]any{
    "email": "user@example.com",
    "name":  "John Doe",
})