Vibecode DB
Vibecode DB is the database layer for AppLighter, providing a unified API for mock and Supabase adapters with authentication, CRUD operations, and TanStack Query integration.
What is Vibecode DB?
Vibecode DB is a flexible database client that provides a unified API across different backend adapters. AppLighter uses Vibecode DB as its data layer, allowing you to switch between mock data and Supabase without changing your application code.
Why Vibecode DB?
- Adapter Pattern - Same API whether using mock data or Supabase
- Built-in Auth - Sign up, sign in, session management across all adapters
- Mock Mode - Start building immediately with seeded data, no backend needed
- Supabase Compatible - Familiar query builder API if you're coming from Supabase
- Type Safe - Full TypeScript support with schema types
Adapters
Mock Adapter (Default)
No setup required. Uses in-memory storage with seeded data from src/db/seeds/.
EXPO_PUBLIC_DB_ADAPTER=mockAuto-creates a demo user (demo@example.com) and seeds all mock data on app start.
Supabase Adapter
Connect to Supabase for cloud PostgreSQL with auth, storage, and realtime.
EXPO_PUBLIC_DB_ADAPTER=supabase
EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-keyPocketBase Scripts
AppLighter also includes scripts for pushing schema and seeding data to a PocketBase backend:
# Push schema collections to PocketBase
yarn pocketbase-db-push
# Seed data to PocketBase
yarn pocketbase-db-seedClient Setup
The Vibecode client is initialized in src/db/client.ts:
import { createClient } from '@vibecode-db/client'
const DB_ADAPTER = process.env.EXPO_PUBLIC_DB_ADAPTER ?? 'mock'
// Adapter is selected based on DB_ADAPTER value
// All adapters expose the same vibecode APIImport and use in your screens:
import { vibecode } from '@/src/db/client'API Chain Order
Important: Vibecode DB uses a specific chain order for queries:
from → filters → order → limit → selectCRUD Operations
Select
// Select all rows
const { data, error } = await vibecode.from('posts').select('*')
// With filters, ordering, and limit
const { data, error } = await vibecode
.from('posts')
.eq('user_id', userId)
.order('created_at', { ascending: false })
.limit(50)
.select('*')
// Single record
const { data, error } = await vibecode
.from('posts')
.eq('id', postId)
.limit(1)
.select('*')
const post = data?.[0] ?? nullInsert
const { data, error } = await vibecode.from('posts').insert({
id: crypto.randomUUID(),
caption: 'Hello world',
user_id: user.id,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
})
if (error) throw errorUpdate
const { error } = await vibecode
.from('posts')
.eq('id', postId)
.update({
caption: 'Updated caption',
updated_at: new Date().toISOString(),
})Delete
const { error } = await vibecode
.from('posts')
.eq('id', postId)
.delete()Filter Methods
| Method | Description | Example |
|---|---|---|
.eq(col, val) | Equal | .eq('published', true) |
.ne(col, val) | Not equal | .ne('status', 'draft') |
.gt(col, val) | Greater than | .gt('views', 100) |
.gte(col, val) | Greater than or equal | .gte('created_at', date) |
.lt(col, val) | Less than | .lt('price', 50) |
.lte(col, val) | Less than or equal | .lte('updated_at', date) |
.in(col, arr) | In array | .in('status', ['active', 'pending']) |
.like(col, pattern) | Pattern match | .like('title', '%search%') |
Authentication
Vibecode DB provides built-in auth methods:
Sign Up
const { data, error } = await vibecode.auth.signUp({
email: 'user@example.com',
password: 'securepassword',
name: 'User Name',
})Sign In
const { data, error } = await vibecode.auth.signIn({
email: 'user@example.com',
password: 'securepassword',
})
if (data) {
// User is authenticated
(globalThis as any).__setAuthUser?.(data.user)
}Sign Out
await vibecode.auth.signOut()Get Session
const { data } = await vibecode.auth.getSession()
if (data?.session?.user) {
// User is authenticated
console.log('Current user:', data.session.user.email)
}With TanStack Query
Use Vibecode DB with TanStack Query for caching, background refetching, and optimistic updates:
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { vibecode } from '../src/db/client'
export default function HomeScreen() {
const queryClient = useQueryClient()
const query = useQuery({
queryKey: ['posts'],
queryFn: async () => {
const { data, error } = await vibecode
.from('posts')
.order('created_at', { ascending: false })
.limit(50)
.select('*')
if (error) throw error
return data ?? []
},
})
const createPost = useMutation({
mutationFn: async (post: { caption: string; user_id: string }) => {
const { error } = await vibecode.from('posts').insert({
id: crypto.randomUUID(),
...post,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
})
if (error) throw error
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] })
},
})
return { posts: query.data, isLoading: query.isLoading, createPost }
}Mock Data Seeds
When using the mock adapter, data is seeded from src/db/seeds/. The starter template includes seeds for users, posts, and stories. Each seed file follows this pattern:
// src/db/seeds/posts.ts
import type { Post } from '../schema'
type SeedEntry = { table: string; rows: Post[] }
const seed: SeedEntry = {
table: 'posts',
rows: [
{
id: '1',
caption: 'Hello world',
// ... other fields
},
],
}
export default seedRegister seeds in src/db/seeds/index.ts:
import users from './users'
import posts from './posts'
import stories from './stories'
export const seeds: SeedEntry[] = [users, posts, stories]
export { DEMO_USER_ID } from './users'Schema Sync Rule
Important: When adding or updating database entities, always keep these three files in sync:
src/db/schema.ts- TypeScript type definitions (source of truth)src/db/seeds/<table>.ts- Mock data with matching shapesrc/db/seeds/index.ts- Register in the seeds array
Error Handling
Always check for errors from Vibecode DB operations:
const { data, error } = await vibecode.from('posts').select('*')
if (error) {
console.error('Database error:', error.message)
Alert.alert('Error', 'Failed to load posts')
return
}
// Safe to use dataNext Steps
- Learn about Expo Integration
- Explore UI Components