Overview
The Address API uses:- Atlas for database migrations
- sqlc for generating type-safe Go code from SQL queries
- PostgreSQL as the database
Making SQL changes
Step 1: Update the schema
Editdatabase/schema.sql with your changes:
Step 2: Write SQL queries
SQL queries must be placed in specific.sql files that follow the pattern defined in sqlc.json:
queries.sql file inside internal/*/sql/ directories.
Example: Create a queries file
Createinternal/your_feature/sql/queries.sql:
sqlc query annotations
| Annotation | Description | Returns |
|---|---|---|
:one | Query returns exactly one row | Single struct |
:many | Query returns zero or more rows | Slice of structs |
:exec | Query doesn’t return rows | Error only |
:execrows | Query doesn’t return rows | Rows affected count |
:execresult | Query doesn’t return rows | sql.Result |
Step 3: Generate Go code from SQL
Run sqlc to generate type-safe Go code:- Models: Struct definitions for your tables in
database/generated_models/models.go - Queries: Functions for your SQL queries in
database/generated_models/queries.sql.go
Example generated code
For the query above, sqlc generates:Step 4: Use generated code in your handler
Step 5: Generate a migration
After updatingdatabase/schema.sql, generate a migration:
database/migrations/ with a timestamp and your description.
Example migration file
Step 6: Apply the migration locally
Test your migration locally:Step 7: Commit your changes
Migration deployment
Local
Migrations are applied manually using./scripts/apply.sh.
Staging/Production
Migrations are automatically applied during deployment:- GitHub Actions builds the Docker image
- The deployment workflow runs migrations before deploying the new version
- If migrations fail, the deployment is aborted
sqlc best practices
Use named parameters
Use appropriate return types
Handle nullable columns
Use transactions
For operations that need atomicity, use transactions:Atlas migration best practices
Never modify existing migrations
Once a migration is committed and deployed, never modify it. Always create a new migration to fix issues.Test migrations on a copy of production data
Before deploying to production:- Take a snapshot of production database
- Restore it locally
- Run migrations against the snapshot
- Verify data integrity
Use reversible migrations when possible
While Atlas doesn’t have built-in rollback, you can create a new migration to reverse changes:Handle data migrations carefully
When migrating data, do it in steps:Common patterns
Upsert (INSERT … ON CONFLICT)
Bulk insert
Conditional queries
Troubleshooting
Error: “sqlc: no queries found”
Cause: Query files are not in the correct location. Fix: Ensure queries are ininternal/*/sql/queries.sql files.
Error: “migration failed: duplicate key”
Cause: Migration tries to insert data that violates a unique constraint. Fix: Update the migration to handle existing data:Error: “column does not exist”
Cause: You’re using a column in a query that doesn’t exist indatabase/schema.sql.
Fix:
- Add the column to
database/schema.sql - Generate a migration:
./scripts/generate.sh --name add_column - Apply the migration:
./scripts/apply.sh - Regenerate sqlc code:
sqlc generate