Skip to content

Join the Seedly owners community →

Databases

Supabase in Your App

Reading, writing and listening for changes in code, and Supabase vs Railway PostgreSQL

Written by 6 min read2 activities
Pixl, your presenter

Pixl presents

Reading, writing and live updates in code, then Supabase versus plain Railway PostgreSQL. Spoiler, the answer is it depends.Reading, writing and live updates in code, then Supabase versus plain Railway PostgreSQL. Spoiler, the answer is it depends.

Pixl listens through a brass ear trumpet at a desk as a notepad fills with squiggles
Your code can read, write and listen for new data

Using Supabase in Your Code#

Here's a simple example. Don't stress about memorizing any of it. Just notice how straightforward it reads.

Setting Up the Client#

Every example below imports a supabase client from a file called supabaseClient.js, so make that first. Install Supabase's JavaScript library in your project folder.

npm install @supabase/supabase-js

Grab your project URL and publishable key from the Connect panel in the Supabase dashboard and put them in .env.local. These are the Next.js names, and other frameworks use their own prefix.

NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...

Then create the client once and export it.

// supabaseClient.js
import { createClient } from '@supabase/supabase-js'
 
export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
)

The publishable key is safe to use in the browser. What keeps your data safe is row level security (RLS), which is a set of rules on each table that says who can read and write which rows.

Reading Data#

import { supabase } from './supabaseClient'
 
// Get all users
const { data: users } = await supabase
  .from('users')
  .select('*')
 
// Get one user
const { data: user } = await supabase
  .from('users')
  .select('*')
  .eq('id', 1)
  .single()

Writing Data#

// Add a new user (and get the new row back)
const { data, error } = await supabase
  .from('users')
  .insert({
    name: 'Alex',
    email: '[email protected]'
  })
  .select()

That .select() on the end matters. Without it, Supabase saves the row but hands you back nothing, and you'll be staring at a null wondering what broke.

Listening for Real-time Changes#

// Listen for new messages
supabase
  .channel('messages')
  .on('postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'messages' },
    (payload) => {
      console.log('New message:', payload.new)
    }
  )
  .subscribe()

Any time a message gets added to the database, your code hears about it instantly.

One setup step first though. Real-time is off for each table until you switch it on. In the Supabase dashboard, find the supabase_realtime publication and toggle on the tables you want to listen to (the messages table here). Your row level security rules also decide which changes each user actually gets.

Supabase vs. Railway PostgreSQL#

Pixl weighs a plain crate against a gadget topped crate on a brass balance scale
Pick Supabase or plain PostgreSQL based on what you need

You might be thinking "wait, didn't we already learn about Railway for databases?" Good catch!

Supabase and Railway can BOTH host PostgreSQL databases. Here's when each one makes sense.

Use Supabase When:#

  • You want real-time updates
  • You want a simple API without writing backend code
  • You're building something fast
  • You like having a visual dashboard to look at your data
  • You want built-in extras (auth, storage, edge functions)

Use Railway PostgreSQL When:#

  • You just need a plain database
  • You're using an ORM like Prisma (which we covered earlier)
  • You want more control over your database
  • You're already deploying other stuff on Railway

This lesson ends with 2 short activities.