<h2>O que é SQLC e Por que Você Deveria Usar</h2>
<p>SQLC é uma ferramenta que gera código Go tipado automaticamente a partir de suas queries SQL. Em vez de escrever manualmente structs, funções de mapeamento e tratamento de erros, você define suas queries SQL e o SQLC cuida de gerar todo o código repetitivo de forma segura e eficiente. Isso reduz bugs, melhora a manutenibilidade e elimina a famosa "magia" das ORMs tradicionais.</p>
<p>A principal vantagem é a <strong>segurança de tipo em tempo de compilação</strong>. Se você refatorar uma coluna no banco de dados ou mudar o tipo de um parâmetro, o SQLC vai gerar código que não compila até que você atualize suas queries — nada de descobrir erros em produção. Além disso, SQLC não interpreta SQL em tempo de execução como ORMs fazem; gera código Go puro, resultando em performance superior e sem overhead de reflexão.</p>
<h3>Por que não apenas usar uma ORM?</h3>
<p>ORMs como GORM são convenientes, mas abstraem SQL demais. Você perde controle fino sobre suas queries, performance fica imprevisível, e aprender a "linguagem" da ORM adiciona complexidade. SQLC pega o melhor dos dois mundos: você escreve SQL direto (controle total) e recebe código Go seguro e tipado automaticamente.</p>
<h2>Instalação e Configuração Inicial</h2>
<h3>Instalando o SQLC</h3>
<p>Primeiro, instale o SQLC em sua máquina. Visite <a href="https://docs.sqlc.dev/en/latest/overview/install.html" target="_blank" rel="noopener noreferrer">https://docs.sqlc.dev/en/latest/overview/install.html</a> para o método mais recente. Para a maioria dos usuários:</p>
<pre><code class="language-bash">go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest</code></pre>
<p>Verifique se está funcionando:</p>
<pre><code class="language-bash">sqlc version</code></pre>
<h3>Configurando seu projeto</h3>
<p>Crie um arquivo <code>sqlc.yaml</code> na raiz do seu projeto:</p>
<pre><code class="language-yaml">version: "2"
sql:
- engine: "postgres"
queries: "./queries"
schema: "./schema"
gen:
go:
out: "./internal/db"
package: "db"</code></pre>
<p>Esta configuração diz ao SQLC: leia as migrations em <code>./schema</code>, leia as queries em <code>./queries</code> e gere código Go em <code>./internal/db</code> com o package <code>db</code>. Crie os diretórios necessários:</p>
<pre><code class="language-bash">mkdir -p queries schema internal/db</code></pre>
<h3>Esquema do banco de dados</h3>
<p>Crie <code>schema/001_initial.sql</code> com uma estrutura de exemplo:</p>
<pre><code class="language-sql">CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id),
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);</code></pre>
<p>Este é apenas um exemplo de schema que usaremos nas próximas seções.</p>
<h2>Escrevendo Queries e Gerando Código</h2>
<h3>Estruturando suas queries</h3>
<p>Crie <code>queries/users.sql</code>. SQLC usa comentários especiais para identificar tipos de operação:</p>
<pre><code class="language-sql">-- name: GetUser :one
SELECT id, name, email, created_at FROM users WHERE id = $1;
-- name: ListUsers :many
SELECT id, name, email, created_at FROM users ORDER BY created_at DESC;
-- name: CreateUser :one
INSERT INTO users (name, email) VALUES ($1, $2)
RETURNING id, name, email, created_at;
-- name: DeleteUser :exec
DELETE FROM users WHERE id = $1;
-- name: UpdateUser :exec
UPDATE users SET name = $2, email = $3 WHERE id = $1;</code></pre>
<p>Os comentários especiais definem:</p>
<ul>
<li><code>-- name: NomeDaFuncao</code>: o nome da função que será gerada</li>
<li><code>:one</code>: retorna uma única linha (um struct)</li>
<li><code>:many</code>: retorna múltiplas linhas (slice de structs)</li>
<li><code>:exec</code>: apenas executa (DELETE, UPDATE sem RETURNING)</li>
</ul>
<p>Crie também <code>queries/posts.sql</code>:</p>
<pre><code class="language-sql">-- name: CreatePost :one
INSERT INTO posts (user_id, title, content) VALUES ($1, $2, $3)
RETURNING id, user_id, title, content, created_at;
-- name: GetPostsByUserID :many
SELECT id, user_id, title, content, created_at FROM posts
WHERE user_id = $1 ORDER BY created_at DESC;
-- name: GetPost :one
SELECT id, user_id, title, content, created_at FROM posts WHERE id = $1;</code></pre>
<h3>Gerando o código</h3>
<p>Execute:</p>
<pre><code class="language-bash">sqlc generate</code></pre>
<p>Se tudo estiver correto, você verá novos arquivos em <code>internal/db/</code>. Examine-os:</p>
<pre><code class="language-go">// internal/db/models.go
package db
import (
"time"
)
type User struct {
ID int32
Name string
Email string
CreatedAt time.Time
}
type Post struct {
ID int32
UserID int32
Title string
Content string
CreatedAt time.Time
}</code></pre>
<pre><code class="language-go">// internal/db/users.sql.go (parcial)
package db
import (
"context"
)
const getUser = `-- name: GetUser :one
SELECT id, name, email, created_at FROM users WHERE id = $1
`
func (q *Queries) GetUser(ctx context.Context, id int32) (User, error) {
row := q.db.QueryRowContext(ctx, getUser, id)
var i User
err := row.Scan(&i.ID, &i.Name, &i.Email, &i.CreatedAt)
return i, err
}</code></pre>
<p>Perceba: <strong>código seguro, tipado e sem reflexão</strong>. Os parâmetros e retornos são verificados em tempo de compilação.</p>
<h2>Usando SQLC em uma Aplicação Real</h2>
<h3>Configurando a conexão com o banco</h3>
<p>Crie <code>main.go</code>:</p>
<pre><code class="language-go">package main
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
"yourmodule/internal/db"
)
func main() {
// Substitua pelos seus dados de conexão
connStr := "postgres://user:password@localhost:5432/yourdb?sslmode=disable"
sqldb, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
defer sqldb.Close()
// Testa a conexão
if err := sqldb.Ping(); err != nil {
log.Fatal(err)
}
// Cria a instância de Queries
queries := db.New(sqldb)
// Seu código aqui
runExamples(context.Background(), queries)
}
func runExamples(ctx context.Context, q *db.Queries) {
// Criar um usuário
user, err := q.CreateUser(ctx, db.CreateUserParams{
Name: "João Silva",
Email: "joao@example.com",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Usuário criado: %d - %s\n", user.ID, user.Name)
// Buscar um usuário
fetchedUser, err := q.GetUser(ctx, user.ID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Usuário encontrado: %s (%s)\n", fetchedUser.Name, fetchedUser.Email)
// Listar todos os usuários
users, err := q.ListUsers(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total de usuários: %d\n", len(users))
for _, u := range users {
fmt.Printf(" - %s: %s\n", u.Name, u.Email)
}
// Criar um post
post, err := q.CreatePost(ctx, db.CreatePostParams{
UserID: user.ID,
Title: "Meu Primeiro Post",
Content: "Este é o conteúdo do meu primeiro post com SQLC!",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Post criado: %d\n", post.ID)
// Buscar posts do usuário
posts, err := q.GetPostsByUserID(ctx, user.ID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Posts do usuário: %d\n", len(posts))
for _, p := range posts {
fmt.Printf(" - %s\n", p.Title)
}
}</code></pre>
<h3>Tratamento de erros com SQLC</h3>
<p>SQLC retorna erros padrão do Go. Para diferenciar entre "não encontrado" e outros erros:</p>
<pre><code class="language-go">import "database/sql"
user, err := q.GetUser(ctx, 999) // ID que não existe
if err != nil {
if err == sql.ErrNoRows {
fmt.Println("Usuário não encontrado")
} else {
log.Fatal(err)
}
}</code></pre>
<h2>Recursos Avançados e Boas Práticas</h2>
<h3>Queries parametrizadas com IN</h3>
<p>Para queries com múltiplos IDs, SQLC suporta slices:</p>
<pre><code class="language-sql">-- name: GetUsersByIDs :many
SELECT id, name, email, created_at FROM users WHERE id = ANY($1);</code></pre>
<p>Chamado assim:</p>
<pre><code class="language-go">users, err := q.GetUsersByIDs(ctx, []int32{1, 2, 3})</code></pre>
<h3>Transações</h3>
<p>SQLC funciona perfeitamente com transações padrão do Go:</p>
<pre><code class="language-go">tx, err := sqldb.BeginTx(ctx, nil)
if err != nil {
log.Fatal(err)
}
defer tx.Rollback()
// Cria uma instância de Queries com a transação
qtx := q.WithTx(tx)
user, err := qtx.CreateUser(ctx, db.CreateUserParams{
Name: "Maria",
Email: "maria@example.com",
})
if err != nil {
return err
}
post, err := qtx.CreatePost(ctx, db.CreatePostParams{
UserID: user.ID,
Title: "Post dentro de transação",
Content: "Conteúdo",
})
if err != nil {
return err
}
return tx.Commit()</code></pre>
<h3>Estruturando seu projeto</h3>
<p>Uma estrutura bem organizada com SQLC:</p>
<pre><code>projeto/
├── main.go
├── sqlc.yaml
├── go.mod
├── go.sum
├── schema/
│ └── 001_initial.sql
├── queries/
│ ├── users.sql
│ └── posts.sql
└── internal/
├── db/
│ ├── models.go
│ ├── users.sql.go
│ └── posts.sql.go
└── handlers/
└── user_handler.go</code></pre>
<p>Mantenha suas queries organizadas por domínio e não misture lógica Go com lógica SQL.</p>
<h2>Conclusão</h2>
<p>SQLC resolve um dos maiores problemas na programação Go: gerar código de acesso a dados que é seguro, eficiente e sem boilerplate. Você ganhou três aprendizados principais neste artigo: <strong>(1) SQLC gera código tipado a partir de SQL, eliminando erros de mapeamento em tempo de compilação</strong>; <strong>(2) o fluxo é simples — defina schema, escreva queries com comentários especiais, execute <code>sqlc generate</code></strong>; <strong>(3) o código gerado é Go puro, não há abstrações mágicas, resultando em performance previsível e fácil debugging</strong>.</p>
<p>Use SQLC quando você quer controle fino sobre SQL, performance máxima e segurança de tipo. Não use quando precisar de migrations automáticas ou quando trabalhar com vários bancos de dados diferentes na mesma aplicação (embora seja possível com configuração extra).</p>
<h2>Referências</h2>
<ul>
<li><a href="https://docs.sqlc.dev/" target="_blank" rel="noopener noreferrer">SQLC Official Documentation</a></li>
<li><a href="https://github.com/sqlc-dev/sqlc" target="_blank" rel="noopener noreferrer">SQLC GitHub Repository</a></li>
<li><a href="https://docs.sqlc.dev/en/latest/tutorials/getting-started.html" target="_blank" rel="noopener noreferrer">Getting Started with SQLC - Official Tutorial</a></li>
<li><a href="https://pkg.go.dev/database/sql" target="_blank" rel="noopener noreferrer">Go database/sql Package Documentation</a></li>
<li><a href="https://github.com/lib/pq" target="_blank" rel="noopener noreferrer">PostgreSQL Go Driver - pq Documentation</a></li>
</ul>
<p><!-- FIM --></p>