Supabase as a Backend: Where Should Business Logic Actually Live?
Learn where business logic should live in a Supabase application, including PostgreSQL, RLS, database functions, Edge Functions, and application backends.

Introduction
Supabase gives developers many of the backend capabilities they need without requiring them to build and maintain a traditional backend from scratch. It provides a PostgreSQL database, authentication, Row Level Security (RLS), APIs, storage, realtime features, database functions, triggers, and Edge Functions.
But this flexibility creates an important architectural question: where should business logic actually live in a Supabase application?
Should it be inside PostgreSQL? Should it go into an Edge Function? Should the frontend call Supabase directly? Or is a separate application backend still the better choice?
The answer is not to put everything in one layer. This article explains how to divide responsibilities between the database, Supabase Edge Functions, and the application layer so that an application remains secure, maintainable, and scalable.
Quick Answer
Business logic in Supabase should be distributed according to its responsibility. Put data integrity, constraints, authorization policies, and atomic database operations close to PostgreSQL. Use Edge Functions for server-side business operations, secrets, webhooks, and third-party integrations. Keep complex multi-step workflows in an application or middleware layer when they become too large or difficult to manage inside the database.
The goal is not to avoid database logic. The goal is to put each type of logic in the layer that is best suited to handle it.
What Is Supabase?
Supabase is a backend platform built around PostgreSQL. A Supabase project provides a full PostgreSQL database along with services such as authentication, storage, realtime functionality, database APIs, and Edge Functions.
The Problem: Supabase Gives You Too Many Places to Put Logic
Traditional backend applications often have a relatively obvious place for business logic: the server.
With Supabase, developers can implement logic in several places:
- Frontend code
- PostgreSQL constraints
- RLS policies
- Database functions
- Database triggers
- Edge Functions
- A separate backend or middleware service
This flexibility is useful, but it can also create architectural problems.
For example, imagine an e-commerce application where creating an order involves several steps: Create Order → Validate Cart → Calculate Discount → Check Inventory → Process Payment → Create Invoice → Send Confirmation Email → Create Notification.
Supabase allows you to implement many of these operations close to PostgreSQL. However, putting the entire workflow into one database function can eventually make the database responsible for application orchestration rather than just data management.
The result can be harder to test, debug, review, monitor, and maintain as the application grows.
Where Should Business Logic Live in Supabase?
A useful way to think about Supabase architecture is to divide responsibilities into three major layers:
| Layer | Best suited for | Examples |
|---|---|---|
| PostgreSQL / Database | Data integrity and authorization | Constraints, RLS, transactions, database functions |
| Edge Functions | Server-side operations and integrations | Payments, webhooks, emails, external APIs |
| Application / Middleware | Complex workflows and orchestration | Multi-step business processes, complex rules, legacy integrations |
The exact boundary depends on the application, but this model provides a practical starting point.
1. Put Data Integrity Rules in PostgreSQL
The database should be responsible for rules that must remain true regardless of where a request comes from.
For example, suppose an application stores product prices. A price should never be negative.
That is a data integrity rule, so it makes sense to enforce it in PostgreSQL:
CREATE TABLE products (
id uuid PRIMARY KEY,
name text NOT NULL,
price numeric CHECK (price >= 0)
);Even if a frontend validation is bypassed, PostgreSQL will still reject invalid data.
Other examples include:
- Unique constraints
- Foreign keys
- Check constraints
- Not-null constraints
- Referential integrity
- Transactions
- Database-level consistency rules
These rules belong close to the data because they should apply regardless of whether the request comes from a React application, a mobile application, an Edge Function, or another backend service.Your source material similarly recommends keeping data integrity and consistency rules at the database level.
2. Use RLS for Data Authorization
Row Level Security is one of the most important parts of a Supabase architecture.
RLS answers a specific question: "Is this user allowed to access this particular row?"
For example, suppose users should only be able to access their own orders.
A policy can enforce that rule:
CREATE POLICY "Users can view their own orders"
ON orders
FOR SELECT
TO authenticated
USING ((SELECT auth.uid()) = user_id);With RLS enabled, the database itself determines which rows the authenticated user can access. Supabase recommends enabling RLS for tables exposed through the Data API and using policies to enforce least-privilege access.
This is important because frontend checks are not security boundaries.
For example, hiding an "Edit Order" button does not prevent someone from sending a request directly to your API.
RLS provides protection at the database level.
RLS Is Not the Same as Business Workflow Logic
RLS is excellent for authorization: determining whether a user is allowed to access a particular order.
However, RLS is not necessarily the right place for complex business workflows. For example, deciding whether an order can be cancelled may involve checking its current status, calculating a refund, calling a payment provider, updating a subscription, and sending a confirmation email.
This type of multi-step workflow is better handled in an application or server-side layer, while RLS remains focused on controlling data access.
3. Use Database Functions for Data-Intensive Operations
PostgreSQL functions are useful when an operation is closely tied to the database and needs to execute efficiently or atomically. Supabase supports database functions that run inside PostgreSQL and can be called through the API.
For example:
CREATE OR REPLACE FUNCTION calculate_order_total(
order_id uuid
)
RETURNS numeric
LANGUAGE sql
AS $$
SELECT COALESCE(SUM(quantity * price), 0)
FROM order_items
WHERE order_id = calculate_order_total.order_id;
$$;Database functions can be especially useful when an operation:
- Reads or updates multiple related records
- Requires database-level atomicity
- Performs data-heavy calculations
- Needs to avoid multiple client-to-database round trips
- Is naturally expressed using SQL
For example, an operation that updates inventory and creates an order record may benefit from running within one database transaction.
Supabase's documentation specifically recommends database functions for data-intensive operations, while Edge Functions are better suited to server-side operations that need low latency or external integrations.
4. Use Database Triggers for Automatic Database Actions
Triggers are useful when something should happen automatically because a database row changed.
For example, consider a user updating their profile. A PostgreSQL trigger can automatically update the updated_at timestamp whenever the profile record changes.
Triggers can be useful for:
- Maintaining timestamps
- Creating audit records
- Maintaining derived values
- Performing database-level side effects
For example:
CREATE OR REPLACE FUNCTION update_timestamp()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$;The important consideration is to keep triggers focused. A trigger that updates an audit timestamp is easy to understand. A trigger that silently starts a payment workflow, sends several external requests, modifies multiple business entities, and creates notifications is much harder to reason about.
5. Use Edge Functions for Server-Side Business Logic
This is where Supabase Edge Functions become particularly useful.
Edge Functions are server-side TypeScript functions running on Supabase's edge infrastructure. They can handle webhooks, external APIs, payments, transactional emails, AI integrations, and other server-side operations.
A common flow involves the frontend sending a request to an Edge Function, where the request is validated and the required business rules are applied. The Edge Function can then interact with PostgreSQL and communicate with external services when necessary.
For example, consider a payment workflow. The frontend should not contain payment-provider secret keys or handle trusted payment processing. Instead, the React Native or web application sends the request to an Edge Function, which validates the order and creates the payment through the payment provider before updating PostgreSQL.
Supabase specifically supports Edge Functions for integrations such as Stripe and other third-party services.
Example Edge Function
A simplified server-side operation might look like:
Deno.serve(async (req) => {
const { userId, amount } = await req.json();
if (!userId || amount <= 0) {
return new Response(
JSON.stringify({ error: "Invalid request" }),
{ status: 400 }
);
}
// Business logic can run here.
// Call external services if required.
return new Response(
JSON.stringify({ success: true }),
{
headers: {
"Content-Type": "application/json"
}
}
);
});Secrets should remain on the server rather than being exposed in frontend code. Supabase explicitly states that secret and service-role keys must not be exposed to frontend applications because they bypass RLS.
6. Keep Complex Orchestration in the Application Layer
Supabase Edge Functions can cover many backend requirements, but they do not mean every application must eliminate its backend.
For larger applications, a separate application or middleware layer can still make sense.
Examples include:
- Complex multi-step workflows
- Large domain-specific business rules
- Long-running processes
- Background jobs
- Legacy system integrations
- Workflows requiring sophisticated retry and queue mechanisms
- Applications that already have a dedicated backend
For example:
Frontend
↓
Application Backend
↓
Business Services
├── Payment Service
├── Order Service
├── Notification Service
└── Inventory Service
↓
Supabase
↓
PostgreSQLThis introduces more infrastructure, but it can provide greater flexibility when application complexity increases. Your source material identifies this layer as useful for multi-step workflows, complex business rules, and legacy integrations.
Don't Put Business Logic on the Client
One of the most important rules is that frontend validation is not a security mechanism.
Consider a React Native application:
if (amount <= 0) {
showError("Invalid amount");
}This validation is useful because it provides immediate feedback to the user. However, frontend code can be modified or bypassed, meaning a malicious client could send an invalid value directly to the API:
{
"amount": -100
}Therefore, important validation must also be enforced in a trusted server-side or database layer.
A good approach is to use frontend validation for user experience while using backend or database validation for security and data integrity.
In other words, use both layers rather than relying on frontend validation alone.
A Practical Example: Order Cancellation
Consider an application where users can cancel orders.
The frontend might display: Cancel Order
The frontend can hide that button when the order appears to be non-cancellable. But the actual cancellation workflow should not depend on that UI condition.
A secure implementation could handle the order cancellation workflow through an Edge Function or application backend. The server-side logic can authenticate the user, verify order ownership and status, calculate the refund, communicate with the payment provider, update the order in PostgreSQL, create an audit record, and send a confirmation.
Here, different layers have different responsibilities:
| Responsibility | Layer |
|---|---|
| Hide/disable Cancel button | Frontend |
| Verify user can access order | RLS |
| Check order state | Edge Function / Backend |
| Calculate complex refund | Edge Function / Backend |
| Update related records atomically | PostgreSQL |
| Maintain database integrity | PostgreSQL |
| Call payment provider | Edge Function / Backend |
| Send notification | Edge Function / Backend |
This separation keeps each layer focused.
When Should You Use Direct Supabase Access?
Direct access from a frontend application can be perfectly reasonable for simple CRUD operations.
For example:
const { data, error } = await supabase
.from("todos")
.select("*");If RLS is correctly configured, the frontend can safely interact with the Data API for operations that do not require additional server-side orchestration. Supabase documents this as a standard frontend access pattern when RLS and appropriate privileges are configured.
Use direct Supabase access when:
- The operation is simple CRUD
- RLS can express the authorization requirement
- No secret credentials are required
- No external API needs to be called
- No complex workflow is involved
Introduce an Edge Function or backend when those conditions no longer hold.
Common Problems and How to Avoid Them
1. Putting Everything in PostgreSQL
Problem: Large database functions become responsible for the entire application workflow.
Solution: Keep database functions focused on data-intensive and transactional operations. Move external integrations and complex orchestration to Edge Functions or the application backend.
2. Relying Only on Frontend Validation
Problem: A user can bypass frontend code and call the API directly.
Solution: Enforce important rules through RLS, constraints, database validation, or server-side business logic.
3. Exposing Secret Keys
Problem: Secret or service-role credentials are included in frontend code.
Solution: Keep them in server-side environments such as Edge Functions or your backend. Supabase states that secret and service-role keys should never be exposed to customers or browser applications.
4. Using RLS for Everything
Problem: Authorization policies become extremely complicated and start representing entire business workflows.
Solution: Use RLS primarily for row-level access control. Move complex workflow decisions into server-side business logic.
5. Creating Too Many Edge Functions
Problem: Every small database operation becomes a separate HTTP endpoint.
Solution: Use direct Supabase access and database operations for simple CRUD. Introduce Edge Functions when there is a real need for server-side processing, secrets, integrations, or complex business rules.
A Good Supabase Architecture
For many modern applications, a practical architecture looks like this:

This approach allows Supabase to remain simple where simplicity is useful while still providing a clear place for more complex business requirements.
When Should You Use Supabase for Your Backend?
Supabase is a strong choice when:
- Your application benefits from PostgreSQL.
- You want managed authentication and database infrastructure.
- You need direct APIs over your database.
- Your application mostly consists of CRUD operations.
- RLS can express your authorization requirements.
- You want serverless functions for integrations and backend operations.
Consider adding a dedicated application backend when:
- Business workflows are becoming highly complex.
- You need extensive background processing.
- You have substantial legacy integrations.
- You need specialized backend infrastructure.
- Your domain logic has grown beyond what is practical to manage through database functions and Edge Functions.
The choice does not have to be all-or-nothing. A hybrid architecture can use Supabase for its database, authentication, RLS, and APIs while using Edge Functions or a dedicated backend for more complex operations.
FAQ
Where should business logic live in Supabase?
There is no single correct location. Data integrity and authorization belong close to PostgreSQL, while external integrations and server-side operations are good candidates for Edge Functions. Complex application workflows may be better suited to a dedicated backend.
Should all business logic be inside PostgreSQL?
No. PostgreSQL is excellent for data integrity, transactions, RLS, and data-intensive operations, but putting an entire application workflow into database functions can make the system harder to test, maintain, and integrate.
Can I use Supabase directly from a frontend?
Yes. Supabase supports frontend access to its Data API when appropriate security controls such as RLS and least-privilege access are configured.
When should I use a Supabase Edge Function?
Use an Edge Function when the operation requires server-side logic, secrets, webhooks, third-party APIs, payments, emails, AI services, or other operations that should not run directly in the frontend.
Is RLS enough to secure a Supabase application?
RLS is a critical part of Supabase data security, but it should be combined with appropriate Postgres grants, authentication, secure handling of secrets, and other security controls.
Can Supabase replace a traditional backend?
For many applications, yes. Supabase can provide the database, authentication, APIs, storage, realtime functionality, and server-side Edge Functions needed to build an application without maintaining a traditional backend. For applications with complex domain workflows or specialized infrastructure requirements, adding a dedicated backend can still be appropriate.
Conclusion
Supabase gives developers an unusual amount of flexibility in deciding where backend logic should run. That flexibility is powerful, but the best architecture is not to put everything in PostgreSQL or everything in Edge Functions.
A better approach is to assign each responsibility to the appropriate layer: use PostgreSQL for data integrity and atomic operations, RLS for row-level authorization, database functions for data-intensive logic, Edge Functions for secrets and external integrations, and an application backend when workflows become sufficiently complex.
The main takeaway is simple: Supabase does not eliminate backend architecture decisions - it makes them more flexible. The goal is to use that flexibility deliberately so the application remains secure, testable, maintainable, and scalable.

Aarav Sharma
Lead Software Engineer
Aarav leads product engineering at Matlab Infotech, where he has shipped mobile and web platforms across healthcare, fintech, and SaaS. He writes about pragmatic engineering and shipping fast without cutting corners.


