# camelCase table or column names not working

> Postgres folds unquoted names to lower case, so a table called mealPlans or a column called createdAt breaks in SQL. Quote them, or rename them to snake_case safely.

Source: https://supahost.app/kb/camelcase-identifiers · Updated 2026-11-11

## What you see

A table or column you can see in Studio's table editor "doesn't exist" in SQL:

```
ERROR:  42P01: relation "mealplans" does not exist
LINE 1: select * from mealPlans
```

```
ERROR:  42703: column "createdat" does not exist
HINT:  Perhaps you meant to reference the column "mealPlans.createdAt".
```

Notice that the name in the error is all lower case, even though you typed `mealPlans`.

## Why it happens

Postgres folds unquoted identifiers to lower case. `select * from mealPlans` is read as `select * from mealplans`. Only a name in double quotes keeps its case, so a table created as `"mealPlans"` (which is what happens when you type a camelCase name in a table editor) can only be reached as `"mealPlans"` from then on.

supabase-js usually hides this, because the Data API quotes every name you pass. `supabase.from('mealPlans').select('createdAt')` works fine. The trouble starts in raw SQL: RLS policies, database functions, triggers, views, migrations and ORMs that don't quote.

## How to fix it

You have two options.

**Option 1: quote the names everywhere in SQL.**

```sql
select "id", "createdAt"
from public."mealPlans"
where "ownerId" = (select auth.uid());
```

This works, but every policy, function and ad-hoc query needs the quotes, and forgetting one gives the error again.

**Option 2 (recommended): rename to snake_case once.**

1. Rename the table and its columns:

   ```sql
   alter table public."mealPlans" rename to meal_plans;
   alter table public.meal_plans rename column "createdAt" to created_at;
   alter table public.meal_plans rename column "ownerId" to owner_id;
   ```

   Renames are instant and don't rewrite data. Policies, indexes, foreign keys and triggers follow the rename automatically. Function bodies don't: they store their SQL as text, so update any function that mentions the old names.

2. Find functions that still use the old names:

   ```sql
   select p.oid::regprocedure
   from pg_proc p
   join pg_namespace n on n.oid = p.pronamespace
   where n.nspname = 'public'
     and p.prosrc ilike '%mealPlans%';
   ```

3. Update your app. Change `.from('mealPlans')` to `.from('meal_plans')` and regenerate your TypeScript types.

4. If old app versions are still in use, keep a temporary view with the old name so they keep working while you roll out:

   ```sql
   create view public."mealPlans"
   with (security_invoker = true)
   as select id, created_at as "createdAt", owner_id as "ownerId" from public.meal_plans;
   ```

   `security_invoker` makes the view apply the table's RLS policies to the caller. Drop the view once every client is updated.

5. If the Data API still reports the old names, reload its schema cache:

   ```sql
   notify pgrst, 'reload schema';
   ```

Going forward, use lower-case snake_case names for tables and columns. If you want camelCase in your JavaScript objects, rename in the query: `select('id, createdAt:created_at')`.

## On SupaHost

- Run renames in Studio's SQL editor (*Dashboard → your project → **Database** tab → Open Studio*) or with MCP `apply_migration`, so the change is recorded in your migration history.
- MCP `generate_typescript_types` gives you the new types after the rename.

## Related

- [42P01: relation does not exist](/kb/relation-does-not-exist)
- [Tables, columns and data](/docs/database/tables)
- [Generating TypeScript types](/docs/api/generating-types)
- [column does not exist when using or() filters](/kb/or-filter-column-error)
