Building Production-Ready Multi-Tenant SaaS in Rust with Actix-web
These articles are AI-generated summaries. Please check the original sources for full details.
Building Production-Ready Multi-Tenant SaaS in Rust with Actix-web
Developer Chinedu built SmartFarmAI to manage poultry operations across Nigeria and Tanzania. The platform employs PostgreSQL Row-Level Security (RLS) to prevent data leaks between farms, ensuring enterprise-grade isolation for operations with up to 60,000 birds.
Why This Matters
In multi-tenant architectures, relying solely on application-level logic like manual WHERE clauses creates a high risk of catastrophic data leaks due to human error. By shifting isolation enforcement to the database layer via PostgreSQL RLS, developers create a structural failsafe that ensures data remains private even if application-layer filters are omitted.
For SmartFarmAI, this approach balances the management simplicity of a shared database with the security guarantees of isolated schemas. This technical strategy significantly reduces operational overhead while maintaining the high level of trust required in agricultural business management.
Key Insights
- PostgreSQL Row-Level Security (RLS) enforces data isolation at the database layer, removing the need for manual ‘WHERE org_id = $1’ clauses in application code.
- SmartFarmAI uses composite foreign keys (org_id, farm_id) to make it structurally impossible to associate a batch with a farm from a different organization.
- Actix-web middleware extracts tenant IDs from JWT claims and uses ‘SET LOCAL app.current_tenant_id’ to scope transactions to a specific tenant.
- Using ‘SET LOCAL’ with the third argument set to ‘true’ ensures the tenant context is transaction-specific, preventing connection pool contamination between requests.
- Infrastructure tables like Outbox queues must bypass RLS to allow background workers to poll across all organizations before switching to a per-tenant context for processing.
Working Examples
Schema implementation using composite foreign keys and PostgreSQL RLS policies.
CREATE TABLE batches (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
org_id UUID NOT NULL,
farm_id UUID NOT NULL,
CONSTRAINT batch_farm_fk FOREIGN KEY (org_id, farm_id) REFERENCES farms (org_id, id) ON DELETE CASCADE
);
ALTER TABLE farms ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON farms
USING (org_id = current_setting('app.current_tenant_id')::uuid);
Actix-web middleware logic to set the PostgreSQL transaction-local tenant context.
pub async fn set_rls_context(
pool: &PgPool,
org_id: &TenantId,
) -> Result<sqlx::Transaction<'_, sqlx::Postgres>, Error> {
let mut tx = pool.begin().await.map_err(|e| {
actix_web::error::ErrorInternalServerError(format!("DB error: {}", e))
})?;
sqlx::query("SELECT set_config('app.current_tenant_id', $1, true)")
.bind(org_id.to_string())
.execute(&mut *tx)
.await?;
Ok(tx)
}
Practical Applications
- SmartFarmAI isolates egg production and mortality logs for distinct farms using SQLx and Actix-web. Pitfall: Using session-scoped session variables instead of transaction-local ones can leak data to the next user using that pooled connection.
- Background workers process event queues by polling a global outbox table. Pitfall: Enabling RLS on infrastructure tables like work queues causes workers to return zero results because they lack a default tenant context.
- Database migrations are performed by superusers to bypass RLS restrictions. Pitfall: Assuming RLS is active during migrations can lead to logic errors in scripts that require global data access.
References:
Continue reading
Next article
Automating Terraform Security Scans with Checkov and Azure Pipelines
Related Content
Building a Multi-Tenant Observability Platform with SigNoz + OneUptime
Modern SaaS teams require robust observability while maintaining tenant isolation, achieving this platform reduced operational overhead and aligned with SOC 2/ISO 27001 compliance.
Why Backend Engineering is Fundamental to Generative AI Systems
Backend engineers are uniquely positioned to solve the systems engineering challenges inherent in scaling Generative AI beyond simple demos.
Building a Production-Grade Async Job Queue: Engineering Resilience and Backpressure
A technical deep dive into building an async job queue with Redis Streams, achieving 85% test coverage and a sustained throughput of 56 req/s.