π What is sqlc
?
sqlc
is a code generation tool that converts your SQL queries into type-safe Go functions. It eliminates manual boilerplate code and avoids ORMs.
β Benefits
- Type-safe queries
- No ORM overhead
- Native SQL with full control
- Auto-generated Go models & methods
π¦ Installation
CLI
brew install sqlc # macOS # or go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
π Recommended Project Structure
myapp/ βββ db/ β βββ migrations/ β βββ queries/ β β βββ users.sql β βββ schema.sql βββ sqlc.yaml βββ go.mod βββ main.go
βοΈ sqlc.yaml
Configuration
version: "2" sql: - engine: "postgresql" queries: "db/queries/" schema: "db/migrations/" gen: go: package: "db" out: "db/sqlc" sql_package: "pgx/v5" emit_json_tags: true emit_interface: true
βοΈ Writing SQL Queries
db/queries/users.sql
-- name: CreateUser :one INSERT INTO users (username, email) VALUES ($1, $2) RETURNING id, username, email; -- name: GetUser :one SELECT id, username, email FROM users WHERE id = $1; -- name: ListUsers :many SELECT id, username, email FROM users ORDER BY id DESC;
π Query Types in sqlc
Tag | Purpose | Go Return Type |
---|---|---|
:one | Returns a single row. Fails if no row or more than one is returned. Use it for SELECT with unique constraint or primary key filters. | (Model, error) |
:many | Returns multiple rows as a slice. Use for general SELECT queries that return 0 to many rows. | ([]Model, error) |
:exec | Executes query with no result rows. Use for INSERT , UPDATE , or DELETE that don't return rows. | (error) |
:execrows | Executes and also returns the number of rows affected. Useful when you care how many rows were impacted by UPDATE or DELETE . | (int64, error) |
:copyfrom | Generates a method using PostgreSQL's COPY FROM. Use for fast bulk insert operations. | (int64, error) |
π Example Migration (db/migrations/init.up.sql
)
CREATE TABLE users ( id SERIAL PRIMARY KEY, username TEXT NOT NULL, email TEXT UNIQUE NOT NULL );
π οΈ Generating Go Code
sqlc generate
Creates Go code in db/sqlc
with models and query methods.
π§βπ» Using in Go Code
import ( "context" "database/sql" "fmt" _ "github.com/lib/pq" "myapp/db/sqlc" ) dbConn, _ := sql.Open("postgres", "postgresql://root:root@localhost:5432/simple_bank?sslmode=disable") q := sqlc.New(dbConn) user, _ := q.CreateUser(context.Background(), "esha", "esha@email.com") fmt.Println(user)
π§Ή Handling Errors
If you see:
error: Dirty database version -1. Fix and force version.
Use:
migrate ... force 0
Then:
migrate ... up
β Tips
- Use
pgx/v5
for performance - Never edit applied migrations
- Use
.PHONY
Makefile targets to runsqlc generate
,migrate
, etc.
More
These annotations like :one
, :many
, :exec
, etc., are special sqlc
query tags that tell sqlc
:
- What kind of Go function to generate
- What kind of return value to expect
They appear in SQL comments just before each query:
-- name: MyFunctionName :<type> <SQL statement>
Letβs break them down:
π sqlc
Query Types Explained
Tag | Purpose | Return Type (Go) | Use When... |
---|---|---|---|
:one | Return exactly one row | (Type, error) | SELECT that should return a single record |
:many | Return multiple rows | ([]Type, error) | SELECT with multiple rows expected |
:exec | Execute query, no result rows | (error) | INSERT/UPDATE/DELETE without returning |
:execrows | Like :exec but also returns rows affected | (int64, error) | UPDATE/DELETE where you want affected row count |
:copyfrom | Use COPY FROM PostgreSQL bulk import | (int64, error) | For high-speed bulk insert |
π§ Examples
β
:one
-- name: GetUserByID :one SELECT id, username, email FROM users WHERE id = $1;
Generates:
func (q *Queries) GetUserByID(ctx context.Context, id int32) (User, error)
β
:many
-- name: ListUsers :many SELECT id, username, email FROM users ORDER BY id DESC LIMIT 10;
Generates:
func (q *Queries) ListUsers(ctx context.Context) ([]User, error)
β
:exec
-- name: DeleteUser :exec DELETE FROM users WHERE id = $1;
Generates:
func (q *Queries) DeleteUser(ctx context.Context, id int32) error
β
:execrows
-- name: UpdateEmail :execrows UPDATE users SET email = $2 WHERE id = $1;
Generates:
func (q *Queries) UpdateEmail(ctx context.Context, id int32, email string) (int64, error)
β
:copyfrom
-- name: CopyUsers :copyfrom COPY users (username, email) FROM STDIN;
Generates:
func (q *Queries) CopyUsers(ctx context.Context, r io.Reader) (int64, error)
Used for efficient bulk data import with a CSV reader or similar.
π§ Summary Cheat Sheet
You want to... | Use this |
---|---|
SELECT 1 row | :one |
SELECT many rows | :many |
INSERT/UPDATE/DELETE (no rows) | :exec |
UPDATE and get row count | :execrows |
COPY FROM for bulk insert | :copyfrom |
Top comments (0)