Refactor: normalize column naming to camelCase on term-related tables #4

Open
opened 2026-04-16 12:28:00 +00:00 by forgejo-lila · 0 comments
Owner

Background

Our Drizzle schema currently has inconsistent column naming conventions across tables:

  • camelCase in TS, snake_case in SQL (via Drizzle column aliasing): Better Auth tables (user, session, account, verification), and the newer lobbies / lobby_players tables.
  • snake_case in both TS and SQL (no aliasing): terms, term_glosses, translations, decks, deck_terms, topics, term_topics.

The industry standard for TS/JS codebases is camelCase in application code, snake_case in SQL, converted at the ORM boundary. The Better Auth and lobby tables already follow this pattern. The term-related tables are the outlier.

Scope

Add explicit column aliases to the following tables so their TS property names are camelCase while the underlying SQL column names stay snake_case:

  • termssource_id, created_at
  • term_glossesterm_id, language_code, created_at
  • translationsterm_id, language_code, cefr_level, created_at
  • deckssource_language, validated_languages, created_at
  • deck_termsdeck_id, term_id
  • term_topicsterm_id, topic_id
  • topicscreated_at

Example of the change pattern:

// before
term_id: uuid().notNull().references(() => terms.id, { onDelete: "cascade" }),
created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),

// after
termId: uuid("term_id").notNull().references(() => terms.id, { onDelete: "cascade" }),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),

Files to update

  • packages/db/src/db/schema.ts — table definitions, relations, check constraints, unique constraints, indexes
  • packages/db/src/models/termModel.ts — all property references (terms.source stays, but term_glosses.term_idtermGlosses.termId, etc.)
  • packages/db/src/seeding-cefr-levels.ts, seeding-datafiles.ts, generating-deck.ts, checking-cefr-coverage.ts — any property references
  • Any scripts under scripts/ that import from @lila/db/schema

Non-goals

  • No change to SQL column names (the DB stays snake_case).
  • No new Drizzle migration required — this is purely a TS-layer rename. Run pnpm --filter db generate to confirm it produces an empty migration.
  • No changes to raw SQL queries (db.execute(sql...)) — those reference actual SQL column names, which are unchanged.

Acceptance criteria

  • All term-related tables use camelCase TS property names with explicit snake_case SQL aliases.
  • pnpm tsc passes across the monorepo.
  • pnpm --filter db generate produces no new migration.
  • Existing tests pass.
  • Manual smoke test: a game round still loads terms correctly (single-player flow).

Why not now

This refactor is orthogonal to the current multiplayer slice. Bundling it in would inflate PR scope and obscure behavioral changes behind cosmetic ones. Deferring until the multiplayer work lands keeps the history bisectable.

## Background Our Drizzle schema currently has inconsistent column naming conventions across tables: - **camelCase in TS, snake_case in SQL** (via Drizzle column aliasing): Better Auth tables (`user`, `session`, `account`, `verification`), and the newer `lobbies` / `lobby_players` tables. - **snake_case in both TS and SQL** (no aliasing): `terms`, `term_glosses`, `translations`, `decks`, `deck_terms`, `topics`, `term_topics`. The industry standard for TS/JS codebases is camelCase in application code, snake_case in SQL, converted at the ORM boundary. The Better Auth and lobby tables already follow this pattern. The term-related tables are the outlier. ## Scope Add explicit column aliases to the following tables so their TS property names are camelCase while the underlying SQL column names stay snake_case: - `terms` — `source_id`, `created_at` - `term_glosses` — `term_id`, `language_code`, `created_at` - `translations` — `term_id`, `language_code`, `cefr_level`, `created_at` - `decks` — `source_language`, `validated_languages`, `created_at` - `deck_terms` — `deck_id`, `term_id` - `term_topics` — `term_id`, `topic_id` - `topics` — `created_at` Example of the change pattern: ```ts // before term_id: uuid().notNull().references(() => terms.id, { onDelete: "cascade" }), created_at: timestamp({ withTimezone: true }).defaultNow().notNull(), // after termId: uuid("term_id").notNull().references(() => terms.id, { onDelete: "cascade" }), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), ``` ## Files to update - `packages/db/src/db/schema.ts` — table definitions, relations, check constraints, unique constraints, indexes - `packages/db/src/models/termModel.ts` — all property references (`terms.source` stays, but `term_glosses.term_id` → `termGlosses.termId`, etc.) - `packages/db/src/seeding-cefr-levels.ts`, `seeding-datafiles.ts`, `generating-deck.ts`, `checking-cefr-coverage.ts` — any property references - Any scripts under `scripts/` that import from `@lila/db/schema` ## Non-goals - No change to SQL column names (the DB stays snake_case). - No new Drizzle migration required — this is purely a TS-layer rename. Run `pnpm --filter db generate` to confirm it produces an empty migration. - No changes to raw SQL queries (`db.execute(sql`...`)`) — those reference actual SQL column names, which are unchanged. ## Acceptance criteria - [ ] All term-related tables use camelCase TS property names with explicit snake_case SQL aliases. - [ ] `pnpm tsc` passes across the monorepo. - [ ] `pnpm --filter db generate` produces no new migration. - [ ] Existing tests pass. - [ ] Manual smoke test: a game round still loads terms correctly (single-player flow). ## Why not now This refactor is orthogonal to the current multiplayer slice. Bundling it in would inflate PR scope and obscure behavioral changes behind cosmetic ones. Deferring until the multiplayer work lands keeps the history bisectable.
forgejo-lila added this to the lila development project 2026-04-16 12:28:00 +00:00
Sign in to join this conversation.
No labels
feature
multiplayer
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: forgejo-lila/lila#4
No description provided.