Database: Storing Your Data
Learning Objectives
- Understand what a database is and why you need one
- Set up Supabase and create tables
- Save and retrieve data from your app
- Understand environment variables and manage them properly
2.1 What is a Database?
Simple Analogy: Organized Excel
A database (DB) is where you store data systematically. Think of it like Excel.
- Database = Excel file
- Table = Sheet
- Row = One line of data
- Column = Fields (name, email, etc.)
Why Do You Need One?
The landing page we've built so far resets everything on refresh. To save user information, settings, posts, and other data, you need a database.
- To keep data even after refresh
- To manage data from multiple users
- Required for login, settings, history, and all features
2.2 Getting Started with Supabase
Supabase is a cloud database service that's free to start. You can use it immediately without complex setup, perfect for vibe coding.
Step 1: Create Account
- Go to supabase.com
- Sign up with GitHub (recommended)
- Click "New Project"
- Project name: linkhub
- Select Region: Northeast Asia (Tokyo)
- Set Database Password (save it somewhere safe!)
Step 2: Explore the Dashboard
After project creation, you'll see various features on the dashboard:
| Menu | Description |
|---|---|
| Table Editor | Visually manage tables |
| SQL Editor | Run SQL queries directly |
| Auth | Authentication/login management (next chapter) |
| Storage | File storage |
| API | API keys and URL |
Step 3: Check API Keys
In Settings → API, you can find two keys:
| Key Type | Purpose | Can Expose? |
|---|---|---|
| Project URL | Supabase address | O |
| anon (public) key | Used on client | O |
| service_role key | Server only | Never! |
Warning
If the service_role key is exposed, anyone can manipulate your database freely. Never put it in frontend code or push to GitHub!
2.3 Designing LinkHub Tables
Now let's create the tables LinkHub needs.
Data to Store
- Profiles - username, bio, theme
- Links - title, URL, icon, sort order
- Clicks - which link was clicked and when
Creating Tables
Ask Claude like this:
Create tables for the LinkHub service in Supabase.
profiles table:
- id (UUID, Primary Key)
- user_id (UUID, Foreign Key → auth.users)
- username (text, unique)
- display_name (text)
- bio (text)
- theme (text, default: 'minimal')
- created_at (timestamp)
links table:
- id (UUID, Primary Key)
- profile_id (UUID, Foreign Key → profiles)
- title (text)
- url (text)
- icon (text)
- sort_order (integer)
- is_active (boolean, default: true)
Create as SQL.Run the SQL Claude generates in Supabase's SQL Editor.
Understanding Relationships
Connecting tables is called a 'relationship'. In LinkHub:
- One user has one profile (1:1)
- One profile can have multiple links (1:N)
Foreign Key serves as this connection. The profile_id column contains the id from the profiles table.
2.4 Environment Variables Setup
Don't put API keys directly in code. Use environment variables.
Creating .env.local File
Create a .env.local file in project root:
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6...NEXT_PUBLIC_ Prefix
Variables starting with NEXT_PUBLIC_ are accessible in the browser. Only add this prefix to values that are safe to expose.
Supabase Client Setup
Create code to set up the Supabase client.
Make it /lib/supabase.ts file.
Read URL and anon key from environment variables.2.5 Implementing CRUD
CRUD is the 4 basic operations for handling data:
- Create - Create data
- Read - Retrieve data
- Update - Modify data
- Delete - Remove data
Creating Dashboard Page
Create a dashboard page.
- View my links list (fetch from Supabase)
- Add new link button
- Edit link title/URL
- Delete link
- Drag to reorder
Implement CRUD using Supabase client.Let's look at the key parts of the code Claude generates:
// Read - Retrieve data
const { data: links } = await supabase
.from('links')
.select('*')
.eq('profile_id', profileId)
.order('sort_order')
// Create - Create data
await supabase.from('links').insert({
profile_id: profileId,
title: 'My Website',
url: 'https://example.com'
})
// Update - Modify data
await supabase.from('links')
.update({ title: newTitle, url: newUrl })
.eq('id', linkId)
// Delete - Remove data
await supabase.from('links')
.delete()
.eq('id', linkId)2.6 Other Database Options
Besides Supabase, there are other database services suitable for vibe coding.
Options Comparison
| Service | Features | Free Tier | Recommended For |
|---|---|---|---|
| Supabase | PostgreSQL + Auth + Real-time | 500MB, 2 projects | All-in-one solution |
| Firebase | NoSQL + Google ecosystem | 1GB storage, 10GB transfer | Google service integration |
| Neon | Serverless PostgreSQL | 512MB, unlimited projects | Great with Vercel |
| PlanetScale | Serverless MySQL | Paid only ($39/mo+) | Large-scale scaling |
| Turso | Edge SQLite | 9GB, 500 DBs | Ultra-low latency |
| Convex | Real-time + Serverless functions | Generous free tier | Real-time focused |
Firebase (Google)
Easy integration with Google ecosystem, uses NoSQL.
Create LinkHub database with Firebase.
Use Firestore, collection structure:
- users/{userId}
- users/{userId}/profile
- users/{userId}/links/{linkId}SQL vs NoSQL
- Supabase (SQL): Table, row, column structure. Strong for complex queries
- Firebase (NoSQL): Document, collection structure. Flexible and fast
Neon
Official partnership with Vercel makes integration easy.
Connect Neon PostgreSQL to Next.js.
Use @neondatabase/serverless package.
Environment variable: DATABASE_URL// Neon connection example
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
// Run query
const agents = await sql`
SELECT * FROM agents WHERE user_id = ${userId}
`;PlanetScale
MySQL-based with unique branching feature. Manage DB schema like Git.
Connect PlanetScale MySQL to Next.js.
Use @planetscale/database package.Turso (LibSQL)
Use SQLite in the cloud. Runs at the edge so it's very fast.
Connect Turso to Next.js.
Use @libsql/client package.Convex
A platform integrating database + serverless functions + real-time.
Create LinkHub backend with Convex.
Define schema and set up real-time queries.Which One Should You Choose?
| Situation | Recommendation |
|---|---|
| First time, want all-in-one | Supabase |
| Planning to use Google login, Firebase Functions | Firebase |
| Vercel deployment, want PostgreSQL | Neon |
| Large-scale traffic, prefer MySQL | PlanetScale |
| Speed is top priority | Turso |
| Real-time + backend integration | Convex |
Vibe Coding Tip
Whichever DB you use, asking Claude "Migrate this project to [DB name]" will convert it. Start with Supabase and switch later if needed.
Chapter Summary
- Understood the concept of databases
- Created a Supabase project
- Designed and created tables
- Properly set up environment variables
- Implemented CRUD functionality
- Learned about various DB options
In the next chapter, we'll add login functionality.