DEV Community

Cover image for Supabase with Supabase CLI, React + TypeScript – Part 2: APIs and Edge Functions
Sukanta Biswas
Sukanta Biswas

Posted on

Supabase with Supabase CLI, React + TypeScript – Part 2: APIs and Edge Functions

In the first post of this series, we kept things simple and set up authentication with Supabase in a React + TypeScript app. We covered signup, login, and logout flows styled with Tailwind CSS.

In this post, we'll go a step further and explore how to write custom APIs using Supabase Edge Functions and consume them in React. This is where Supabase starts to shine because you're not limited to just auth and database, you can extend it just like a traditional backend.


What are Edge Functions?

Supabase Edge Functions are serverless functions powered by Deno that run at the network edge, close to end users, to process API requests, personalize content, or handle dynamic logic with minimal latency.

  • They are fast (deployed globally to the edge).
  • Secure – run with role-based access and API keys.
  • Flexible – you can handle business logic, external APIs, webhooks, or even transform data before it reaches your frontend.

Step 1: Setting up Supabase CLI

First, install the Supabase CLI if you don’t already have it:

npm install supabase --save-dev
Enter fullscreen mode Exit fullscreen mode

Log in to your Supabase account:

npx supabase login
Enter fullscreen mode Exit fullscreen mode

And link your project:

npx supabase link --project-ref your-project-ref
Enter fullscreen mode Exit fullscreen mode

Step 2: Create an Edge Function

Here we will write a basic api to get all users list. Let's go!
Create a function called get-all-users:

npx supabase functions new get-all-users
Enter fullscreen mode Exit fullscreen mode

This will generate a functions/get-all-users/index.ts file. Open it and add:

functions/get-all-users/index.ts

import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";

// CORS header helper
function getCorsHeaders(origin: string) {
  return {
    "Access-Control-Allow-Origin": origin,
    "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
    "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
  };
}

// Initialize Supabase client with environment variables
const supabase = createClient(
  Deno.env.get("SUPABASE_URL") ?? "",
  Deno.env.get("SUPABASE_ANON_KEY") ?? ""
);

serve(async (req) => {
  const origin = req.headers.get("origin") || "*";

  // Handle preflight CORS
  if (req.method === "OPTIONS") {
    return new Response(null, {
      status: 204,
      headers: getCorsHeaders(origin),
    });
  }

  try {
    // Extract userId from query params (example: /?userId=123)
    const url = new URL(req.url);
    const userId = url.searchParams.get("userId");

    if (!userId) {
      return new Response(JSON.stringify({ error: "Missing userId" }), {
        status: 400,
        headers: getCorsHeaders(origin),
      });
    }

    // Query Supabase to get all users list
    const { data, error } = await supabase
      .from("users")
      .select("*")
      .eq("auth_user_id", userId);

    if (error) throw error;

    return new Response(JSON.stringify(data), {
      headers: {
        "Content-Type": "application/json",
        ...getCorsHeaders(origin),
      },
    });
  } catch (err) {
    return new Response(JSON.stringify({ error: String(err.message) }), {
      status: 500,
      headers: getCorsHeaders(origin),
    });
  }
});

Enter fullscreen mode Exit fullscreen mode

Deploy it:

npx supabase functions deploy get-all-users
Enter fullscreen mode Exit fullscreen mode

Step 3: Call the Function from React

In your React app, you can call this function using Supabase client:

src/api/users.ts

import { supabase } from "./supabaseClient";

export const getAllUsers = async () => {
  const { data, error } = await supabase.functions.invoke("get-all-users");

  if (error) {
    console.error("Error calling get-all-users:", error);
    return null;
  }
  return data;
};
Enter fullscreen mode Exit fullscreen mode

And use it in your component:

src/components/Users.tsx

import React, { useEffect, useState } from "react";
import { getAllUsers } from "../api/users";

interface User {
  id: string;
  full_name: string;
  email: string;
  dob?: string;
  profession?: string;
}

const Users: React.FC = () => {
  const [users, setUsers] = useState<User | undefined>("");

  useEffect(() => {
    const fetchAllUsers = async () => {
      const data = await getAllUsers();
      if (data) setUsers(JSON.parse(JSON.stringify(data)));
    };
    fetchAllUsers();
  }, []);

  return (
    <div className="overflow-x-auto rounded-lg shadow-md border border-gray-200">
      <table className="min-w-full border-collapse bg-white text-sm">
        <thead>
          <tr className="bg-gray-100 text-left">
            <th className="px-4 py-2 border-b">Name</th>
            <th className="px-4 py-2 border-b">Email</th>
            <th className="px-4 py-2 border-b">Date of Birth</th>
            <th className="px-4 py-2 border-b">Profession</th>
          </tr>
        </thead>
        <tbody>
          {users.map((user) => (
            <tr
              key={user.id}
              className="hover:bg-gray-50 transition-colors duration-150"
            >
              <td className="px-4 py-2 border-b">{user.full_name}</td>
              <td className="px-4 py-2 border-b">{user.email}</td>
              <td className="px-4 py-2 border-b">{user.dob || "N/A"}</td>
              <td className="px-4 py-2 border-b">{user.profession || "N/A"}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>  );
};

export default Users;
Enter fullscreen mode Exit fullscreen mode

Step 4: Securing Edge Functions

By default, functions are secured and require a service role or authenticated user token.

You can make a function public by adding this to supabase/config.toml:

[functions.get-all-users]
verify_jwt = false
Enter fullscreen mode Exit fullscreen mode

Then redeploy:

npx supabase functions deploy get-all-users
Enter fullscreen mode Exit fullscreen mode

Real-world Use Cases

Edge Functions are great for:

  • Sending transactional emails (e.g., SendGrid, Resend).

  • Integrating 3rd party APIs securely.

  • Running cron jobs or scheduled tasks.

  • Custom data transformations before saving to PostgreSQL.


Wrapping up

In this post, we learned how to:

  • Create and deploy a Supabase Edge Function.

  • Call it from a React + TypeScript app.

  • Secure or expose it depending on your use case.

Edge Functions give your Supabase project the flexibility of a full backend, while keeping the simplicity of BaaS.

In the next post, we’ll explore Realtime Channels to build live notifications and updates.

If you found this useful, please share it with your dev friends!

Top comments (0)