Developer Guide
Code Tour
Section titled “Code Tour”The best way to understand Fluxbase is to follow a request through the system. The diagram below shows the path a typical API request takes. Below it, each numbered step lists the files you should open and read to understand what happens at that stage.
graph TD REQ["① HTTP Request"] --> ROUTER["② Fiber Router<br/>api/server.go"] ROUTER --> CORS["③ CORS Middleware"] CORS --> RATE["③ Rate Limiter"] RATE --> TENANT["④ Tenant Middleware<br/>Resolve tenant context"] TENANT --> TENANTDB["④ Tenant DB Middleware<br/>Resolve database pool"] TENANTDB --> AUTH["⑤ Auth Middleware<br/>JWT validation, role extraction"] AUTH --> HANDLER["⑥ Handler<br/>internal/api/*.go"] HANDLER --> SERVICE["⑦ Service Layer<br/>internal/{module}/service.go"] SERVICE --> DB["⑧ PostgreSQL<br/>pgx/v5 pool"] DB --> SERVICE SERVICE --> HANDLER HANDLER --> RESP["⑨ JSON Response"]
style REQ fill:#e1f5fe style RESP fill:#e8f5e9 style DB fill:#fff3e0① Entry Point — How the server starts
Section titled “① Entry Point — How the server starts”Before any request arrives, the server boots up and wires everything together using a module-based dependency injection system:
- Open
cmd/fluxbase/main.goand follow themain()function. You’ll see config loading, database pool creation, and the call toNewServer()which triggers the full module initialization pipeline. internal/api/server.go—NewServer()creates aServiceRegistry, instantiates 20 modules in dependency order, callsInitModules(), and then wires each module’s handler group to theServerstruct (~20 lines of assignment).internal/api/module.go— TheModuleinterface (Name()+Init(ctx, *ServiceRegistry) error), the optionalShutdownerinterface for clean teardown, and theServiceRegistrywhich stores config, DB, pub/sub, rate limiter, metrics, and registered services. Modules read dependencies viaGetService[T]()and register outputs viaregistry.Register().internal/api/module_*.go— 20 module files, one per domain. Each module’sInit()creates its services and writes directly to its handler group struct (m.Handlers). Modules run in order: Email → Secrets → Storage → Logging → Auth → Webhook → Extensions → Tenancy → Settings → Schema → Functions → Jobs → RPC → AI → Realtime → Branching → GraphQL → MCP → Metrics → BackgroundServices.internal/api/handler_groups.go— 22 handler group structs (e.g.,AuthHandlers,StorageHandlers,AIHandlers). Each module populates its own group duringInit().
For configuration, open internal/config/config.go for the root Config struct. Domain-specific config types (Auth, Database, Storage, etc.) are split into config_auth.go, config_database.go, and a dozen more files in the same package. Field names map directly to YAML keys and FLUXBASE_* env vars. For defaults, see internal/config/config_defaults.go.
② Routing — Where requests land
Section titled “② Routing — Where requests land”Open internal/api/server.go for the server struct (8 methods: lifecycle, health, and a few cross-group accessors). The actual route registration happens in internal/api/server_routes.go:
setupRoutes()callsregisterRoutesViaRegistry()which delegates toroutes.RegisterAllRoutes()internal/api/routes/registry.go— The centralized route registry. Every route is registered with explicit auth requirements (AuthRequirement), and middleware is auto-injected based on those declarations. The registry also validates consistency (e.g., no route withAuth: AuthNoneandPublic: false)
Route dependencies (handler references, middleware) are organized in per-domain files: routes_auth.go, routes_ai.go, etc.
③ Middleware — CORS and rate limiting
Section titled “③ Middleware — CORS and rate limiting”The global middleware chain runs before any route-specific middleware:
internal/api/server_middlewares.go— Structured logger, CORS, recover, and other global middleware setupinternal/middleware/rate_limiter.go— Rate limiting per route
④ Tenant & Branch Context — Which database to talk to
Section titled “④ Tenant & Branch Context — Which database to talk to”Tenant middleware runs before auth so that tenant context is available for authentication (e.g., per-tenant JWT secrets). Open these in order:
internal/middleware/tenant.go— Tenant resolution:X-FB-Tenantheader → JWT claims → default tenant. Setstenant_id,tenant_slug, and the mergedtenant_configin fiber localsinternal/middleware/tenant_db.go— Database pool resolution.GetPoolForSchema()implements the branch > tenant > main priority chaininternal/middleware/branch.go— Branch context fromX-Fluxbase-Branchheader
⑤ Auth Middleware — Who is making the request
Section titled “⑤ Auth Middleware — Who is making the request”With tenant context established, authentication validates the user:
internal/middleware/clientkey_auth.go— JWT validation, role extraction,RequireAuth/RequireServiceKey. This is where theAuthorizationheader is parsed and claims land in fiber localsinternal/auth/jwt.go— How tokens are created, what claims they carry (TokenClaimsstruct), and how roles map to PostgreSQL roles
⑥ ⑦ ⑧ Handler → Service → Database — The actual work
Section titled “⑥ ⑦ ⑧ Handler → Service → Database — The actual work”This is the three-layer architecture every feature follows. Trace it with the REST CRUD (the most-used feature):
internal/api/rest_crud.go— The CRUD handler. See howSELECT,INSERT,UPDATE,DELETEare built from URL path and query params. Handles all/api/v1/tables/{table}requestsinternal/api/query_parser.go+query_parser_select.go+query_parser_filter.go+query_parser_order.go+query_parser_sql.go— URL query parsing split by concern:?select=→ select fields,?order=→ ordering,?col.eq=→ filters, all composed into SQLinternal/api/query_builder.go— Filters → SQLWHERE,ORDER BY,LIMITinternal/database/connection.go— The connection pool, transaction helpers, and thesync.RWMutexsafety pattern. Migration-related methods are inconnection_migrations.gointernal/database/schema_inspector.go— Schema introspection: how Fluxbase discovers tables, columns, types, and relationships to power the auto-generated API
Digging deeper — Multi-tenancy internals
Section titled “Digging deeper — Multi-tenancy internals”Once you’ve traced a request, the multi-tenancy subsystem is the most complex piece worth understanding:
internal/tenantdb/manager.go— TheManagerstruct. ReadCreateTenantDatabase()to see the full provisioning flow: database creation → bootstrap → FDW setup → declarative schemainternal/tenantdb/fdw.go— Foreign Data Wrapper setup:postgres_fdwconfiguration, per-tenant roles withNOBYPASSRLS, shared schema importsinternal/tenantdb/router.go— Per-tenant connection pool cache with LRU evictioninternal/config/tenant_loader.go+internal/config/tenant_merge.go— Per-tenant config override loading and deep-merge logic
For the full multi-tenancy architecture (database-per-tenant, FDW, connection routing, RLS roles), see the Multi-Tenancy Guide.
Database schemas — The foundation
Section titled “Database schemas — The foundation”Internal schemas are managed declaratively — the SQL files show the current state, not a migration history. When the server starts, internal/migrations/declarative.go diffs the current database state against the desired schema files using pgschema and applies only the changes needed. This means you can read any schema file and see exactly what the tables, RLS policies, and grants look like right now — no git archaeology required. This is especially valuable for security reviews.
The SQL files live in internal/database/schema/schemas/. Start with:
platform.sql— Tenants, service keys, users, memberships, settingsauth.sql— Users, sessions, identities, OTP codes, client keysbootstrap.sql— The SQL that runs on every startup (schemas, extensions, roles, privileges)
Tracing a complete feature — Edge Functions
Section titled “Tracing a complete feature — Edge Functions”To see how a full feature fits together end-to-end, trace the edge functions system:
internal/api/routes/functions.go— Route definitions and middlewareinternal/functions/handler.go— HTTP handlers for CRUD and invocationinternal/functions/loader.go— Loading functions from disk at startupinternal/functions/storage.go+storage_sync.go+storage_executions.go+storage_files.go— Database storage for function metadata, sync, executions, and filesinternal/runtime/runtime.go— The Deno runtime wrapper
Tests — How to test
Section titled “Tests — How to test”internal/api/rest_crud_test.go— Table-driven unit tests with mock dependenciestest/e2e/— E2E tests against a real databaseinternal/testutil/— Shared helpers, mocks, and assertions
How to Add a New Feature
Section titled “How to Add a New Feature”Add a Module
Section titled “Add a Module”Fluxbase uses a module-based dependency injection system. To add a new feature:
- Create a handler group struct in
internal/api/handler_groups.go - Create a module file
internal/api/module_myfeature.goimplementing theModuleinterface:type MyFeatureModule struct{ Handlers *MyFeatureHandlers }func (m *MyFeatureModule) Name() string { return "myfeature" }func (m *MyFeatureModule) Init(ctx context.Context, registry *api.ServiceRegistry) error {// Read dependencies: db := registry.DB; config := registry.Config// Create services, handlers, and populate m.Handlers// Register outputs: registry.Register(myService)return nil} - If the module has resources to clean up (goroutines, connections), implement
Shutdowner:func (m *MyFeatureModule) Shutdown(ctx context.Context) error {// Stop goroutines, close connectionsreturn nil} - Wire the module in
NewServer()ininternal/api/server.go: add it to themodsslice in the correct dependency order, then assignm.Handlersto the server afterInitModules()returns - Add routes in
internal/api/routes/
Tenant Scoping
Section titled “Tenant Scoping”If the feature should be tenant-scoped:
- Add
tenant_idto the database table - Create RLS policies for
tenant_servicerole - Ensure the middleware chain includes
TenantMiddlewarefor the route group - Use
middleware.GetTenantID(c)in handlers to get the current tenant
Write Tests
Section titled “Write Tests”Add tests alongside the source file:
func TestGetMyResource_Found_ReturnsResource(t *testing.T) { // Test implementation}Update Documentation
Section titled “Update Documentation”- Add API endpoint to
docs/src/content/docs/api/http/index.md - Add guide to
docs/src/content/docs/guides/if it’s a new feature - Update
CLAUDE.mdif it changes configuration or architecture
Related Documentation
Section titled “Related Documentation”- Multi-Tenancy - Multi-tenancy architecture and configuration
- Row Level Security - RLS implementation details
- Configuration - Complete configuration reference
- HTTP API - HTTP API endpoint reference