CodeBerry logo
CodeBerry Pie
frontend

Supabase + Next.js : A Powerful Fullstack Pairing

Supabase + Next.js : A Powerful Fullstack Pairing
0 views
4 min read
#frontend

Supabase has quickly become a developer favorite for building fullstack apps with modern tools. If you're coming from Firebase, PostgreSQL, or simply want a faster way to ship projects with authentication, databases, and serverless functions — Supabase paired with Next.js is a dream combo.

What is Supabase (in simple terms)?

Think of Supabase as an open-source alternative to Firebase — but built on PostgreSQL, which gives you more control and flexibility.

It provides:

  • A fully-managed PostgreSQL database
  • Built-in authentication (email/password, OAuth, magic links)
  • Edge functions for running backend logic
  • Realtime updates via websockets
  • Storage for files (images, docs, etc.)
  • A slick dashboard and instant REST/GraphQL APIs

Official site: https://supabase.com

Why Pair Supabase with Next.js?

Next.js brings SSR (server-side rendering), API routes, and an excellent DX (developer experience) to the frontend. Together, they form a:

  • Fullstack framework: database + API + frontend in one
  • Fast prototyping setup: auth, DB, UI — all in minutes
  • Ideal for SaaS, dashboards, internal tools, etc.

Setting Up Supabase in a Next.js App

Step 1: Install Supabase Client

npm install @supabase/supabase-js

Step 2: Create a Supabase Project

  • Go to https://app.supabase.com
  • Click "New Project"
  • Set project name, password, region
  • Copy the project URL and anon/public API key from the settings

Step 3: Initialize Supabase in Your App

Create a file: lib/supabaseClient.js

import { createClient } from '@supabase/supabase-js';

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

Update your .env.local:

NEXT_PUBLIC_SUPABASE_URL=https://xyzcompany.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here

Using Supabase Auth with Next.js

Let’s build a simple login/signup form.

// pages/login.js
import { supabase } from '../lib/supabaseClient';
import { useState } from 'react';

export default function LoginPage() {
  const [email, setEmail] = useState('');

  const handleLogin = async () => {
    const { error } = await supabase.auth.signInWithOtp({ email });
    if (error) alert(error.message);
    else alert('Check your email for login link');
  };

  return (
    <div>
      <input type="email" onChange={e => setEmail(e.target.value)} />
      <button onClick={handleLogin}>Send Magic Link</button>
    </div>
  );
}

For persistent auth, wrap your app in a SessionContextProvider using Supabase’s @supabase/auth-helpers-nextjs:

npm install @supabase/auth-helpers-nextjs

Wrap your _app.js:

import { SessionContextProvider } from '@supabase/auth-helpers-react';
import { supabase } from '../lib/supabaseClient';

function MyApp({ Component, pageProps }) {
  return (
    <SessionContextProvider supabaseClient={supabase} initialSession={pageProps.initialSession}>
      <Component {...pageProps} />
    </SessionContextProvider>
  );
}

export default MyApp;

Querying Data (from DB Table)

Let’s fetch all rows from a todos table:

const { data, error } = await supabase.from('todos').select('*');

Insert new record:

await supabase.from('todos').insert({ title: 'Learn Supabase', completed: false });

Realtime subscription:

supabase.channel('todos-changes')
  .on('postgres_changes', { event: '*', schema: 'public', table: 'todos' }, (payload) => {
    console.log('Change received!', payload);
  })
  .subscribe();

Server-Side Supabase Queries (Next.js API Routes)

// pages/api/todos.js
import { supabase } from '../../lib/supabaseClient';

export default async function handler(req, res) {
  const { data, error } = await supabase.from('todos').select('*');
  if (error) return res.status(500).json({ error });
  res.status(200).json({ todos: data });
}

File Storage Example

const { data, error } = await supabase.storage.from('avatars').upload('user1.png', file);

List uploaded files:

const { data } = await supabase.storage.from('avatars').list();

Calling a Supabase Function via RPC

Let’s say you created a PostgreSQL function called increment like so:

-- SQL in Supabase SQL Editor
create or replace function increment(slug_text text)
returns void as $$
begin
  update posts set views = views + 1 where slug = slug_text;
end;
$$ language plpgsql;

You can call it directly from your Next.js frontend:

const incrementView = async () => {
  const { data, error } = await supabase.rpc('increment', { slug_text: 'hello-world' });
  if (error) console.error('Error calling function:', error);
  else console.log('View incremented!');
};

Use this pattern for tracking views, likes, counters, or any backend-side mutation that should remain secure.

Supabase Edge Functions + Next.js

Use them when you need fast, server-side logic close to your users.

Docs: https://supabase.com/docs/guides/functions

Final Tips

  • Supabase Auth = super fast MVP-ready login
  • Realtime is great for dashboards, chat apps, games
  • Use RLS (Row-Level Security) for access control
  • Consider Prisma or Drizzle with Supabase for advanced DB typesafety
  • Dashboard SQL Editor is awesome for schema changes

Resources

Supabase + Next.js is a match made for productivity. Whether you’re building an internal tool, SaaS, or side project — you get speed, scalability, and a great developer experience with minimal setup.