ConfigsApr 13, 2026·3 min read

sqlc — Generate Type-Safe Go Code from SQL

sqlc generates fully type-safe Go code from your SQL queries. Write SQL, run sqlc generate, and get Go functions with proper types for parameters and results — no ORM, no reflection, just compile-time safe database access.

TL;DR
sqlc parses your SQL queries and schema, then generates Go code with proper types for parameters and results.
§01

What it is

sqlc is a code generation tool that reads your SQL schema and query files, validates them, and produces type-safe Go functions. Instead of using an ORM or embedding SQL strings with manual type assertions, you write plain SQL and let sqlc generate the Go structs and methods. The generated code has zero runtime reflection and catches type errors at compile time.

sqlc is built for Go developers who prefer writing raw SQL over ORM abstractions. It supports PostgreSQL, MySQL, and SQLite, and integrates into standard Go build pipelines.

§02

How it saves time or tokens

sqlc eliminates the boilerplate of hand-writing Go structs for query results and scanning rows into typed variables. A single sqlc generate command produces all the data access code from your SQL files. Schema changes are caught at generation time rather than at runtime, reducing debugging cycles. The generated code is straightforward and readable, so there is no ORM magic to debug.

§03

How to use

  1. Install sqlc: go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest or brew install sqlc.
  2. Create a sqlc.yaml config file and write your SQL schema and query files with sqlc annotations like -- name: GetUser :one.
  3. Run sqlc generate to produce Go source files with typed functions for each query.
§04

Example

-- schema.sql
CREATE TABLE users (
  id   SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  email TEXT NOT NULL
);

-- query.sql
-- name: GetUser :one
SELECT id, name, email FROM users WHERE id = $1;

-- name: ListUsers :many
SELECT id, name, email FROM users ORDER BY name;

-- name: CreateUser :one
INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *;

After running sqlc generate, you get Go functions like GetUser(ctx, id) returning a typed User struct.

§05

Related on TokRepo

§06

Common pitfalls

  • sqlc validates queries against your schema at generation time, so your schema file must be kept in sync with your actual database.
  • Complex dynamic queries with conditional WHERE clauses are harder to express in sqlc; consider using sqlc for standard CRUD and a query builder for dynamic filters.
  • sqlc's MySQL support is less mature than PostgreSQL; some MySQL-specific syntax may not parse correctly.

Frequently Asked Questions

How does sqlc compare to GORM and other Go ORMs?+

sqlc generates code from SQL you write, giving you full control over queries with compile-time type safety. ORMs like GORM generate SQL from Go structs, which can produce unexpected queries. sqlc has zero runtime reflection, while ORMs use reflection heavily.

What databases does sqlc support?+

sqlc supports PostgreSQL (the most mature), MySQL, and SQLite. Each engine has its own SQL parser that validates your queries against the schema.

Can sqlc handle migrations?+

sqlc reads your schema files but does not run migrations itself. You use a separate migration tool like golang-migrate or Atlas and point sqlc at the resulting schema.

Does sqlc work with transactions?+

Yes. The generated code accepts a database interface that works with both *sql.DB and *sql.Tx, so you wrap calls in a transaction using standard Go database/sql patterns.

Is sqlc suitable for large codebases?+

Yes. sqlc scales well because each query generates a single function. Large projects organize queries into multiple SQL files and use sqlc's package configuration to split generated code across Go packages.

Citations (3)

Discussion

Sign in to join the discussion.
No comments yet. Be the first to share your thoughts.

Related Assets