How to Create and Use RPC Functions in Supabase with Flutter (Step-by-Step Guide)

Introduction

When building modern apps, you often need to perform complex database operations — like filtering data, calculating values, or combining multiple queries. Writing all this logic directly in your Flutter app can quickly become messy and inefficient.

That’s where RPC (Remote Procedure Call) functions in Supabase come in.

RPC functions allow you to write SQL functions inside your database and call them directly from your app as if they were APIs.

Why use RPC functions in Supabase?

  • Keep business logic inside the database (cleaner architecture)
  • Reduce network calls (better performance)
  • Improve security (controlled access to data)
  • Reuse logic across multiple clients (Flutter, Web, etc.)

Prerequisites
Before we start, make sure you have:

  • A Flutter project set up
  • A Supabase project created
  • Supabase Flutter SDK installed
  • Basic knowledge of SQL (SELECT, WHERE, etc.)

Step 1: Create an RPC Function in Supabase

Let’s say we have a tasks table and we want to fetch all completed tasks for a specific user.

Example Table: tasks
idtitleuser_idis_completed1Task A101true2Task B101false

SQL Function
Go to Supabase Dashboard → SQL Editor, and run:

create or replace function get_completed_tasks(p_user_id uuid)
returns table (
  id uuid,
  title text,
  is_completed boolean
)
language sql
as $$
  select id, title, is_completed
  from tasks
  where user_id = p_user_id
    and is_completed = true;
$$;

Explanation (Simple)

  • create or replace function → creates the RPC function
  • p_user_id → input parameter
  • returns table → defines output structure
  • select ... → actual query logic

Step 2: Test the Function in Supabase

Before using it in Flutter, test it.

Run this in SQL Editor:

select * from get_completed_tasks('your-user-id-here');

Expected Output
You should see only completed tasks for that user.

Step 3: Call RPC Function in Flutter

Now let’s call this function from your Flutter app.

Add Supabase Dependency

dependencies:
  supabase_flutter: latest_version

Initialize Supabase

await Supabase.initialize(
  url: 'YOUR_SUPABASE_URL',
  anonKey: 'YOUR_SUPABASE_ANON_KEY',
);

Call RPC Function

final supabase = Supabase.instance.client;
Future<List> getCompletedTasks(String userId) async {
  final response = await supabase.rpc(
    'get_completed_tasks',
    params: {'p_user_id': userId},
  );
  return response;
}

Explanation

  • rpc() → calls Supabase function
  • 'get_completed_tasks' → function name
  • params → must match SQL parameter names

Use in UI

FutureBuilder(
  future: getCompletedTasks(userId),
  builder: (context, snapshot) {
    if (!snapshot.hasData) {
      return CircularProgressIndicator();
    }
    final tasks = snapshot.data as List;
      return ListView.builder(
        itemCount: tasks.length,
        itemBuilder: (context, index) {
          final task = tasks[index];
          return ListTile(
            title: Text(task['title']),
            subtitle: Text("Completed"),
          );
        },
      );
    },
  );

Real-World Use Case
Let’s say you’re building a Task Management App (like your current Flutter project 👀).

Instead of:

  • Fetching all tasks
  • Filtering in Flutter

You can:

  • Use RPC to fetch only required data
  • Improve performance and reduce app logic

Other Use Cases

  • Calculating total revenue
  • Fetching user-specific dashboards
  • Complex joins across tables
  • Role-based data filtering

Common Mistakes

1. Parameter Name Mismatch

params: {'userId': userId} // WRONG

Must match SQL:

p_user_id

2. Forgetting Permissions (RLS)

If Row Level Security (RLS) is enabled:

  • Make sure policies allow access

3. Returning Wrong Data Type

If your function returns:

returns json

Then handle it properly in Flutter.

4. Not Testing in SQL First

Always test in Supabase before calling from Flutter.

Best Practices

Keep functions simple and focused

One function = one responsibility

Use meaningful names

get_user_tasks ✔
fetch_data ❌

Secure with RLS policies
Don’t expose sensitive data

Use indexes for performance
Especially for large datasets

Prefer SQL functions over multiple API calls
Cleaner + faster

Conclusion
RPC functions in Supabase are a powerful way to move logic closer to your database, making your Flutter apps:

  • Faster
  • Cleaner
  • More secure

By following this step-by-step guide, you can now:

  • Create SQL functions
  • Test them in Supabase
  • Call them seamlessly from Flutter

Next Steps

  • Add pagination to your RPC functions
  • Use PostgreSQL joins inside functions
  • Explore Edge Functions vs RPC
  • Build reusable backend logic library

If you’re building scalable Flutter apps with Supabase, mastering RPC is a game changer