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.
§ 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",
})