Skip to main content
Wrkks uses Supabase (PostgreSQL) to store user profiles and resume data. This guide will help you set up your database with the correct schema and Row Level Security (RLS) policies.

Prerequisites

Before you begin, ensure you have:

Database schema

Wrkks requires a users table to store user information and resume data.

Create the users table

Run this SQL in the Supabase SQL Editor:
1

Open SQL Editor

Navigate to your Supabase project dashboard and go to SQL Editor in the left sidebar.
2

Create new query

Click New query to create a new SQL query.
3

Run the schema SQL

Copy and paste the following SQL and click Run:
-- Create users table
create table if not exists public.users (
  id uuid default gen_random_uuid() primary key,
  clerk_user_id text unique not null,
  username text unique not null,
  email text not null,
  resume jsonb,
  created_at timestamp with time zone default timezone('utc'::text, now()) not null,
  updated_at timestamp with time zone default timezone('utc'::text, now()) not null
);

-- Create index on clerk_user_id for faster lookups
create index if not exists users_clerk_user_id_idx on public.users(clerk_user_id);

-- Create index on username for public profile lookups
create index if not exists users_username_idx on public.users(username);

-- Create updated_at trigger
create or replace function public.handle_updated_at()
returns trigger as $$
begin
  new.updated_at = timezone('utc'::text, now());
  return new;
end;
$$ language plpgsql;

create trigger users_updated_at
  before update on public.users
  for each row
  execute function public.handle_updated_at();
This schema automatically handles user synchronization with Clerk via the clerk_user_id field.

Table structure

The users table has the following columns:
id
uuid
required
Primary key, automatically generated UUID for each user.
clerk_user_id
text
required
Unique identifier from Clerk authentication. Used to link Supabase users with Clerk users.
username
text
required
Unique username for the user. Used in public profile URLs (e.g., wrkks.site/username).
email
text
required
User’s email address, synced from Clerk.
resume
jsonb
required
Stores the complete resume data as JSON. Structure matches the Resume interface from lib/types.ts:1.Can be null if the user hasn’t uploaded a resume yet.
created_at
timestamp
required
Timestamp when the user record was created.
updated_at
timestamp
required
Timestamp when the user record was last updated. Automatically updated via trigger.

Resume data structure

The resume field stores a JSON object with the following structure:
{
  "personalInfo": {
    "name": "John Doe",
    "title": "Software Engineer",
    "location": "San Francisco, CA",
    "phone": "+1 (555) 123-4567",
    "email": "[email protected]",
    "website": "https://johndoe.com",
    "linkedin": "https://linkedin.com/in/johndoe",
    "github": "https://github.com/johndoe",
    "twitter": "https://twitter.com/johndoe",
    "imageUrl": "https://..."
  },
  "summary": "Experienced software engineer...",
  "skills": {
    "languages": ["JavaScript", "TypeScript", "Python"],
    "frameworksAndTools": ["React", "Next.js", "Node.js"],
    "softSkills": ["Leadership", "Communication"]
  },
  "experience": [
    {
      "company": "Tech Corp",
      "position": "Senior Engineer",
      "location": "San Francisco, CA",
      "startDate": "Jan 2020",
      "endDate": "Present",
      "isCurrentRole": true,
      "description": ["Led team of 5 engineers", "Built scalable APIs"],
      "technologies": ["React", "Node.js"]
    }
  ],
  "projects": [...],
  "education": [...],
  "extracurricular": [...],
  "customSections": [...]
}
This structure is defined in lib/types.ts and is automatically populated by the AI parsing service.

Row Level Security (RLS)

Row Level Security ensures users can only access their own data. Set up RLS policies to secure your database:
-- Enable Row Level Security
alter table public.users enable row level security;

-- Policy: Users can read their own data
create policy "Users can read own data"
  on public.users
  for select
  using (clerk_user_id = auth.jwt() ->> 'sub');

-- Policy: Users can update their own data
create policy "Users can update own data"
  on public.users
  for update
  using (clerk_user_id = auth.jwt() ->> 'sub')
  with check (clerk_user_id = auth.jwt() ->> 'sub');

-- Policy: Users can insert their own data
create policy "Users can insert own data"
  on public.users
  for insert
  with check (clerk_user_id = auth.jwt() ->> 'sub');

-- Policy: Anyone can read published resumes (for public profiles)
create policy "Anyone can read published resumes"
  on public.users
  for select
  using (resume is not null);
The RLS policies above assume you’re using Clerk JWT tokens with Supabase. You may need to adjust the auth.jwt() ->> 'sub' check based on your Clerk configuration.

Clerk and Supabase integration

Wrkks synchronizes Clerk users with Supabase automatically through the SyncUser component:
  1. When a user signs in with Clerk, the SyncUser component runs server-side
  2. It checks if a user with the matching clerk_user_id exists in Supabase
  3. If not, it creates a new user record with:
    • clerk_user_id from Clerk
    • username derived from email
    • email from Clerk
    • resume set to null
User synchronization happens automatically. You don’t need to manually create users in Supabase.

Testing your database

Verify your database is set up correctly:
1

Test user creation

Sign up with a new account in your local or deployed app. Check the users table in Supabase to verify a new record was created.
2

Test resume upload

Upload a resume PDF and verify the resume field is populated with JSON data.
3

Test RLS policies

Try accessing another user’s data to verify RLS is working (it should fail).
4

Test public profiles

Access a published resume via its share URL to verify the public read policy works.

Database queries

Here are some useful SQL queries for managing your database:
select 
  id,
  clerk_user_id,
  username,
  email,
  created_at,
  updated_at,
  case when resume is not null then 'Yes' else 'No' end as has_resume
from public.users
order by created_at desc;

Backup and maintenance

Automatic backups

Supabase automatically backs up your database daily. To configure backup retention:
  1. Go to SettingsDatabase in your Supabase project
  2. Configure Point-in-Time Recovery (PITR) for production projects
  3. Set up backup alerts

Manual backup

To create a manual backup:
1

Navigate to Database

Go to DatabaseBackups in your Supabase project.
2

Create backup

Click Create backup to create a manual snapshot.

Database migrations

For schema changes, use Supabase migrations:
# Install Supabase CLI
npm install -g supabase

# Initialize migrations
supabase init

# Create a new migration
supabase migration new add_new_field

# Apply migrations
supabase db push

Troubleshooting

”relation public.users does not exist”

  • Verify you ran the schema SQL in the correct database
  • Check that you’re connected to the right Supabase project
  • Ensure the SQL query executed without errors

Users not syncing from Clerk

  • Check that Supabase environment variables are correct
  • Verify the clerk_user_id field exists and is unique
  • Look for errors in your application logs during sign-in
  • Ensure RLS policies allow insertion (checked in components/SyncUser.tsx:21)

RLS policies blocking requests

  • Verify your Clerk JWT tokens include the sub claim
  • Check that the auth.jwt() function works in your Supabase environment
  • Test policies with the Supabase policy editor
  • Temporarily disable RLS to test if that’s the issue (don’t forget to re-enable)

Resume data not saving

  • Verify the resume column type is jsonb, not json or text
  • Check that your JSON structure matches the Resume interface
  • Look for JSON validation errors in application logs
  • Ensure the JSON is valid (use JSON.parse() to test)

Production considerations

1

Enable PITR

Enable Point-in-Time Recovery for production databases to allow restoration to any point in time.
2

Set up monitoring

Configure alerts for database size, connection pool usage, and query performance.
3

Optimize indexes

Monitor slow queries and add indexes as needed for better performance.
4

Configure connection pooling

Use Supabase’s connection pooler (Supavisor) for serverless environments.
5

Review RLS policies

Regularly audit RLS policies to ensure they match your security requirements.

Next steps

Deployment overview

Deploy your application to production

Environment variables

Review all required environment variables

Build docs developers (and LLMs) love