Skip to content

refactor: replace uuid with int64 for user and todo IDs#155

Open
Gautam7352 wants to merge 8 commits into
devfrom
pr/uuid-to-int-refactor
Open

refactor: replace uuid with int64 for user and todo IDs#155
Gautam7352 wants to merge 8 commits into
devfrom
pr/uuid-to-int-refactor

Conversation

@Gautam7352

@Gautam7352 Gautam7352 commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

What & Why

The codebase previously used uuid.UUID (via github.com/google/uuid) for primary keys on users and todos tables, and passed UUIDs through every layer — models, services, handlers, middleware, and the frontend.

The database schema was changed to use SERIAL / auto-increment int primary keys (handled in the migration PR). This PR updates all application code to match that schema change.

Using database-generated integer IDs also simplifies inserts — no need to generate a UUID client-side before writing to the DB.


How to check failure

On dev (before this PR), with the new int-based schema applied:

  1. POST /signup → creates a user, DB returns an integer id
  2. POST /login → JWT is minted with user_id as a UUID string (now stale/wrong)
  3. GET /todos → middleware tries to parse user_id claim as uuid.UUID, fails → 401 invalid token claims
  4. POST /todos → same failure
  5. Frontend guest todos use crypto.randomUUID() for local IDs — these are strings, but the API now returns numeric IDs, causing type mismatches in the todo list

Changes

Backend — models

  • User.ID, Todo.ID, Todo.UserID: uuid.UUIDint64
  • Removed github.com/google/uuid import from models

Backend — services

  • auth_service: Signup uses INSERT ... RETURNING id instead of generating a UUID; JWT user_id claim is now an int64
  • todo_service: all method signatures updated from uuid.UUID to int64; AddTodo uses INSERT ... RETURNING id
  • TodoService interface updated accordingly

Backend — handlers

  • auth.go: extracted signupRequest / loginRequest structs; all status codes use http.Status* constants
  • todo.go: uuid.Parse replaced with strconv.ParseInt; request structs extracted; http.Status* constants used throughout

Backend — middleware

  • auth.go: JWT user_id claim parsed as float64 (JSON number default) then cast to int64; sets userID as int64 in context; removed uuid dependency

Backend — routes

  • Wiring updated to match new handler signatures

Frontend

  • types/index.ts: Todo.id widened to number | string to handle both API (numeric) and guest (string) todos
  • lib/api.ts: normalise() uses Number() instead of String() for id
  • hooks/useTodos.ts: guest todo ID generation replaced with a decrementing integer counter (negative to avoid collisions with server IDs)

How to verify correction

Backend

# From repo root
cd backend
go build ./...   # must compile with no errors
go vet ./...     # must pass
go test ./...    # unit tests must pass

- models: User.ID and Todo.ID/UserID changed from uuid.UUID to int64
- services: use RETURNING id on INSERT instead of generating uuid client-side
- handlers: use int64 type assertions, extract request structs, use http.Status* constants
- middleware/auth: parse user_id claim as float64 then cast to int64
- routes: update wiring for new handler signatures
- frontend: Todo.id type widened to number|string, api normalise uses Number(), guest id uses counter instead of crypto.randomUUID()
- types: id is number (not number|string) - API always returns integers
- hooks: genId() returns negative number instead of string
- hooks: toggleTodo/deleteTodo/updateTodo params changed from string to number
- api: toggle/update/delete id params changed from string to number
- fixes optimistic update comparisons (t.id === id was always false for authed todos)
}

func (h *AuthHandler) Signup(c *gin.Context) {
var req signupRequest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can name it as reqBody

Comment thread backend/db/init.sql
-- as the schema reference snapshot.
-- Source of truth for schema changes: backend/migration/db/migrations/*.sql

CREATE TABLE IF NOT EXISTS users (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mentioned, SERIAL in the design doc but in the code it's mentioned BIGSERIAL.

Why this deviation

Comment thread frontend/src/lib/api.ts

const normalise = (raw: Record<string, unknown>): Todo => ({
id: String(raw.id ?? raw.ID),
id: Number(raw.id ?? raw.ID),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't use Number for postgres's Bigserial, they are incompatible

Comment on lines +6 to +10
let _guestIdCounter = 0
const genId = (): number => {
_guestIdCounter -= 1
return _guestIdCounter
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we changing this here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants