---
title: "Установка и настройка sqlc.yaml"
description: "Настройка sqlc.yaml: структура проекта, version \"2\", движки postgresql, mysql и sqlite, пути schema и queries, gen.go с package, out и sql_package, emit-опции."
course: "sqlc: типобезопасный код из SQL-запросов"
url: "https://golangmaster.ru/courses/sqlc/lessons/02-nastrojka/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 20
---

# Установка и настройка sqlc.yaml

*Курс «sqlc: типобезопасный код из SQL-запросов», урок 2 из 25. ~20 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/sqlc/lessons/02-nastrojka/*

sqlc поставляется одной командой (разбирали в [первом уроке](/courses/sqlc/lessons/01-znakomstvo/)), но запустить его сразу после установки не выйдет: без конфига генератор не знает ни движка, ни путей к SQL, ни куда писать Go-код. Настройка sqlc живёт в одном файле — sqlc.yaml. Соберём структуру проекта трекера задач, пройдём по каждому ключу sqlc yaml: version "2", движки, папки schema и queries, блок gen.go — и заглянем в сгенерированные db.go и models.go, чтобы понимать, за что отвечает каждая строка конфига.

sqlc — инструмент конфигурационный до мозга костей. У него нет флагов вида --schema или --out, которые приходилось бы передавать при каждом запуске: и генерация, и проверки читают один и тот же файл sqlc.yaml из корня проекта. Это решение со следствиями, и полезно их осознать сразу. Во-первых, конфиг — единственная карта проекта: если что-то в слое данных выглядит не так, чинится оно почти всегда в sqlc.yaml или в файлах, на которые он указывает. Во-вторых, конфиг коммитится в репозиторий и у всей команды становится одинаковым: у соседа по проекту генерация не может тихо работать «с другими настройками». В-третьих, история изменений слоя данных лежит в git рядом с историей запросов — diff по sqlc.yaml читается как описание того, что изменилось в проекте.

Проверьте, что сам бинарник на месте: команда `sqlc version` печатает версию установленного генератора. Если команда не найдена — вернитесь к установке из первого урока. Дальше речь про содержимое конфига.

### Структура проекта под sqlc

```go
tracker/
├── go.mod
├── sqlc.yaml
├── db/
│   ├── migrations/
│   │   ├── 001_init.sql
│   │   └── 002_priority.sql
│   ├── queries/
│   │   └── todos.sql
│   └── sqlc/
│       ├── db.go
│       ├── models.go
│       └── todos.sql.go
└── main.go
```

Три зоны. db/migrations — миграции, источник схемы: файл 001 создаёт таблицу todos, 002 добавляет priority из домашки первого урока. db/queries — аннотированные SQL-запросы. db/sqlc — сгенерированный вывод, и эту папку никто не создаёт руками: её пишет sqlc generate. Приложение импортирует db/sqlc как обычный пакет Go, а главный файл main.go живёт выше и о генерации ничего не знает. Ничто не мешает назвать папки иначе — имена задаются в конфиге, дальше увидим как.

### Файл sqlc.yaml: как sqlc его находит и читает

При запуске `sqlc generate` генератор ищет файл конфига в текущем каталоге: основное имя — sqlc.yaml, допустим и вариант sqlc.json, если JSON вам ближе. Отсюда первое правило гигиены: команды sqlc запускают из корня проекта, там же, где лежит go.mod. Все пути внутри конфига — относительные, и отсчитываются они от папки, в которой лежит сам sqlc.yaml, а не от того, где вы стояли в терминале. Пока конфиг один и живёт в корне, разницы не видно; когда захотите выделить генерацию в подкаталог, это знание спасёт вечер.

Формат — обычный YAML: отступы значимы, комментарии начинаются с решётки, строки-значения я всегда беру в кавычки. Кавычки необязательны, но дешёвы: они гарантируют, что yaml прочитает значение строкой, а не числом или логическим значением. Мелочь, которая раз в жизни сэкономит час: случается она ровно с одним ключом, и о нём — через минуту.

### sqlc init: стартовая заготовка

```go
version: "2"
sql:
  - schema: "query.sql"
    queries: "query.sql"
    engine: "postgresql"
    gen:
      go:
        package: "tutorial"
        out: "tutorial"
```

Команда `sqlc init` из первого урока пишет в текущем каталоге заготовку конфига, похожую на эту: schema и queries указывают на один файл query.sql, генерация — в пакет tutorial. Это шаблон для быстрого старта, а не готовый проект: первой деловой правкой обычно становится разнос схемы, запросов и вывода по папкам, как в дереве выше. Обратите внимание на первую строку: version: "2" — в кавычках. Уберёте кавычки — yaml превратит значение в число, а sqlc ждёт строку, и конфиг не примется: ошибка будет выглядеть загадочно, хотя виновата одна пара кавычек. Версия "1" — легаси старого формата, новые проекты начинаются с "2".

### Рабочий sqlc.yaml трекера задач

```go
version: "2"
sql:
  - engine: "postgresql"
    schema: "db/migrations"
    queries: "db/queries"
    gen:
      go:
        package: "db"
        out: "db/sqlc"
        sql_package: "database/sql"
```

Ключ version: "2" — формат конфига. Под ключом sql прячется список конфигураций генерации: пока их одна. Внутри записи: engine — диалект SQL; schema — где лежит схема, у нас папка миграций; queries — где лежат запросы; gen.go — что и куда генерировать: package — имя Go-пакета, out — папка вывода, sql_package — API, под который писать код. sql_package: "database/sql" — значение по умолчанию, строку можно было и не писать; я пишу её явно, чтобы выбор драйвера был виден в конфиге, а не в голове.

### schema, queries и engine: что читает генератор

schema и queries гибче, чем кажется: значение может быть одним файлом, папкой или списком путей. Мы в первом уроке писали схему в один schema.sql — это тоже законный вариант для маленького проекта. Когда проект растёт, на место файла приходит папка миграций, и sqlc читает её файлы в алфавитном порядке, складывая итоговую схему: поэтому миграции нумеруют (001_init.sql, 002_priority.sql). Встроена поддержка файлов популярных инструментов миграций — golang-migrate, goose, sql-migrate, tern: служебные комментарии этих инструментов sqlc распознаёт и пропускает, беря из файлов только SQL. Со схемой из живой базы работаем ближе к концу курса.

Каждый файл запросов из папки queries превращается в одноимённый файл генерации: todos.sql даст todos.sql.go с методами по всем аннотациям из него. Дописали в проекте сущность «проекты» — добавили db/queries/projects.sql и получили projects.sql.go, не трогая существующее.

engine выбирает диалект, по которому парсится и схема, и запросы: postgresql, mysql или sqlite. От движка зависит всё, что связано с языком SQL: типы колонок (BIGSERIAL знает только postgresql), плейсхолдеры параметров ($1 у PostgreSQL, вопрос у MySQL и SQLite — факт из первого урока), допустимые функции. Драйвер pgx имеет смысл только рядом с postgresql-движком.

### Проверка: engine и диалект

**Вопрос:** В sqlc.yaml по ошибке указан engine: "mysql", а schema.sql написан по правилам PostgreSQL: CREATE TABLE с BIGSERIAL и плейсхолдерами $1. Что произойдёт при sqlc generate?

- Код сгенерируется: sqlc пропустит незнакомые типы и подставит их как есть
- ✅ Генерация упадёт с ошибкой парсинга: mysql-движок не знает типов и плейсхолдеров Postgres
- sqlc сам определит диалект по содержимому файлов и переключится на postgresql
- Сгенерируется код с ошибками, которые поймает компилятор Go

engine — это правило чтения всего SQL в проекте: и схемы, и запросов. В диалекте MySQL нет BIGSERIAL, а плейсхолдеры там — вопрос, а не $1, поэтому парсер упадёт с ошибкой и указанием места. Автоопределения диалекта нет: что написано в engine, то и читается. Компилятор Go тут ни при чём — до генерации кода дело не доходит.

### gen.go: package, out и sql_package

Блок gen.go отвечает на два вопроса: какой Go-код писать и куда его класть. package — имя генерируемого пакета, оно попадает в первую строку каждого файла. По конвенции его делают равным последнему элементу out: пакет db в папке db/sqlc — вариант понятно, но частая практика. out — папка вывода, sqlc создаст её сам и затрёт содержимое при каждой генерации. Отсюда эргономический совет: вывод держите изолированно (db/sqlc, internal/db), чтобы рука не тянулась что-то подправить в сгенерированном файле.

sql_package выбирает API, под который пишет генератор. По умолчанию — database/sql: код работает с любым драйвером, зарегистрированным в стандартном интерфейсе. Для PostgreSQL можно указать pgx/v5 (или pgx/v4) — тогда код пишется поверх нативного API pgx. Это тот самый выбор «одной строкой» из первого урока. Важно понимать границу ответственности: конфиг меняет только сгенерированный код, а сам драйвер в проект ставится отдельно через go get — с подключением и DSN это делается в приложении, как разбирали в уроке про [подключение базы в GORM](/courses/gorm/lessons/02-podklyuchenie/) — там та же система: код приложения и драйвер настраиваются вне зависимости от того, кто написал SQL.

### Один конфиг, два API

```go
# вариант 1: стандартная библиотека (по умолчанию)
gen:
  go:
    package: "db"
    out: "db/sqlc"
    sql_package: "database/sql"

# вариант 2: нативный pgx
gen:
  go:
    package: "db"
    out: "db/sqlc"
    sql_package: "pgx/v5"
```

Разница в одной строке, но сгенерированный код получится разным: другой интерфейс DBTX, другой набор импортов, другой тип для NULL (sql.NullString у database/sql против pgtype у pgx — эту пару упоминали в первом уроке, детали в девятом). Меняется и то, что приложение передаёт в db.New: *sql.DB для первого варианта, *pgxpool.Pool для второго. Поэтому переключение sql_package — не только правка конфига, но и правка точки подключения.

### Сгенерированный db.go: что внутри

```go
// db/sqlc/db.go
// Code generated by sqlc. DO NOT EDIT.

package db

import (
        "context"
        "database/sql"
)

type DBTX interface {
        ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
        PrepareContext(context.Context, string) (*sql.Stmt, error)
        QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
        QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}

func New(db DBTX) *Queries {
        return &Queries{db: db}
}

type Queries struct {
        db DBTX
}

func (q *Queries) WithTx(tx *sql.Tx) *Queries {
        return &Queries{
                db: tx,
        }
}
```

Вот где живёт DBTX — интерфейс, о котором говорил первый урок: четыре метода, и под него подходит и *sql.DB, и *sql.Tx, и соединение из пула. db.New оборачивает соединение в структуру Queries, WithTx подменяет его транзакцией (транзакциям посвящён одиннадцатый урок). Точная картина может слегка отличаться между версиями sqlc, но состав файла стабилен: интерфейс, конструктор, хранилище соединения. Шапка DO NOT EDIT — не украшение, а предупреждение с последствиями, о них ниже.

### Сгенерированный models.go: таблица todos

```go
// db/sqlc/models.go
// Code generated by sqlc. DO NOT EDIT.

package db

type Todo struct {
        ID       int64
        Title    string
        Done     bool
        Priority int32
}
```

Структура по нашей таблице, поля — в порядке колонок итоговой схемы: id, title, done, priority. Соответствия типов мы уже читали в первом уроке: BIGSERIAL стал int64, TEXT — string, BOOLEAN — bool; новая колонка priority с типом INTEGER превратилась в int32. Никаких json-тегов и методов здесь нет — их добавляют опции генерации, о них чуть ниже. Пока конфиг не меняется, перегенерация даёт байт-в-байт тот же файл: детерминизм — часть контракта.

> **Сгенерированный код не редактируют.** Правка db.go, models.go или todos.sql.go руками — сделка с авансом: следующий sqlc generate молча затрёт изменения, а вы узнаете об этом в худший момент. Нравится не всё в сгенерированном коде — чините источник: схему, запросы или конфиг. У генератора есть механические способы поймать расхождение ожиданий и фактического вывода, разберём их ближе к концу курса, а дисциплина «не трогать папку out» нужна уже сегодня.

### Типичные ошибки конфига

Три классических падения генерации, и все три лечатся за минуту, если знать их в лицо. Первая — неверный путь: опечатка в schema или queries, или путь записан от другого места. sqlc ищет пути относительно папки с sqlc.yaml, и ошибка приходит в виде сообщения о том, что каталог или файл не найден, с указанием пути, — сверьте его с деревом проекта. На Windows пишите обычные прямые слэши: YAML внутри кавычек трактует обратный слэш как начало спецпоследовательности. Вторая — не тот движок: схема в диалекте одной СУБД, engine — другой (тест выше). Третья — незаполненный gen.go: без out или package sqlc откажется генерировать, требуя явно сказать, куда и под каким именем писать пакет.

Четвёртая ошибка — поведенческая, не конфигурационная: править сгенерированное вместо источника. Запомните направление потока: sqlc.yaml и .sql-файлы — это вход, db/sqlc — выход, и всё, что вы хотите изменить в выходе, начинается со входа.

### emit-опции: тонкая настройка генерации

Рядом с package, out и sql_package в gen.go живут переключатели с префиксом emit — они досыпают в генерацию дополнительные детали. Три самых ходовых. emit_json_tags: true добавляет json-теги в структуры models.go — если модели уезжают в JSON-ответы API, я включаю эту опцию всегда: писать теги руками рядом со сгенерированным файлом — путь к расхождению. emit_interface: true просит ещё один файл, querier.go, — интерфейс со всеми сгенерированными методами, который *Queries реализует целиком; в тестах такой интерфейс подменяется фейком, поэтому в сервисах с приличным тестовым слоем опция нужна. emit_prepared_queries: true генерирует код с подготовленными выражениями — но я её не включаю: выигрыш на повторном парсинге запросов редко окупает усложнение кода; передумал бы при тысячах однотипных запросов в секунду на постоянных соединениях. Есть и другие переключатели — от пустых срезов вместо nil до суффиксов имён файлов — полный список в документации sqlc; принцип тот же: включать только то, что действительно нужно, иначе генерация обрастает деталями, которые никто не использует.

### Впишите emit-опции

Два переключателя генерации: json-теги в моделях и интерфейс всех методов в querier.go. Восстановите их имена.

```go
gen:
  go:
    package: "db"
    out: "db/sqlc"
    ___: true   # json-теги в структурах models.go
    ___: true   # querier.go с интерфейсом всех методов
```

Подсказка: Имена начинаются с emit_ и дословно описывают, что дополнительно сгенерировать: json-теги — json_tags, интерфейс — interface.

Правильные ответы в пропусках: `emit_json_tags`, `emit_interface`

### Предскажите генерацию: что появится в db.go?

```go
version: "2"
sql:
  - engine: "postgresql"
    schema: "db/migrations"
    queries: "db/queries"
    gen:
      go:
        package: "db"
        out: "db/sqlc"
        sql_package: "pgx/v5"
```

**Что выведет программа?**

- Импорты context и database/sql, DBTX с методами ExecContext и QueryContext
- ✅ Импорты pgx/v5 (и pgconn), DBTX с методами Exec, Query и QueryRow
- Никаких импортов: pgx подключается в рантайме
- Импорт gorm.io/gorm — sql_package работает и через GORM

sql_package задаёт API, под который пишется весь сгенерированный код. Для pgx/v5 интерфейс DBTX строится на нативных методах pgx — Exec, Query, QueryRow — с типами pgconn.CommandTag и pgx.Rows, и database/sql в файлах не участвует вовсе. Первый вариант отвечает конфигу с sql_package: "database/sql". О рантайме sqlc ничего не знает — он вообще в рантайме не участвует (первый урок).

### Соберите цикл изменения схемы

Добавляем колонку priority в трекер. Расставьте шаги в рабочем порядке.

Правильный порядок строк:

1. `дописать миграцию: файл 002_priority.sql с ALTER TABLE`
2. `запустить sqlc generate — схема читается из файлов, models.go получит Priority int32`
3. `исправить вызовы, где компилятор поймал разъехавшиеся типы`
4. `применить миграцию к живой базе: goose up или golang-migrate`
5. `прогнать тесты и поднять сервис`

### Проверка: диагностика ошибки

**Вопрос:** sqlc generate падает, в сообщении фигурирует путь db/queris: no such file or directory. Папка с запросами называется db/queries. Где проблема?

- В main.go: неверный DSN подключения к базе
- ✅ В sqlc.yaml: опечатка в значении queries — указан путь db/queris
- В таблице todos: нет колонки queris
- В go.mod: не подключён модуль db/queris

Путь из сообщения об ошибке ищите в sqlc.yaml: генератор не нашёл каталог, который конфиг назвал папкой запросов. DSN и живая база тут ни при чём — sqlc читает схему и запросы из файлов, до базы ему дела нет. Колонка в ошибке отсутствующих путей не фигурирует, а go.mod sqlc не использует.

### Карточки: конфиг sqlc

- **version: "2"** — Формат конфига sqlc. Значение — строка в кавычках: без них yaml прочтёт число, и конфиг не примется. Версия "1" — легаси.
- **sql (список конфигураций)** — Каждый элемент списка — отдельная генерация: engine, schema, queries, gen. Один элемент — обычный проект, несколько — разные базы или пакеты.
- **engine** — Диалект чтения SQL: postgresql, mysql или sqlite. Определяет парсинг типов и плейсхолдеров; pgx имеет смысл только с postgresql.
- **gen.go** — Настройки Go-вывода: package — имя пакета, out — папка генерации, sql_package — API (database/sql по умолчанию, pgx/v5). Плюс emit-опции.
- **db.go и models.go** — Сгенерированные файлы: в db.go — DBTX, New и WithTx, в models.go — структуры таблиц. Шапка DO NOT EDIT: правится источник, не вывод.
- **emit-опции** — Переключатели деталей генерации: emit_json_tags — json-теги в моделях, emit_interface — querier.go с интерфейсом методов, emit_prepared_queries — подготовленные выражения.

### Что дальше по курсу

Конфиг собран и проверен, папки проекта разложены, сгенерированный код перестал быть чёрным ящиком. Третий урок — первые запросы и генерация целиком: пишем выборки для трекера, запускаем sqlc generate и читаем выход построчно. Полный план из 25 уроков — на [странице курса](/courses/sqlc/).

Пока слой данных не собран, HTTP-слогу есть где подождать: когда генерация заработает, сгенерированные методы естественно лягут в обработчики веб-фреймворка — например, в [курсе Gin](/courses/gin/) мы писали ровно такие обработчики, только слой данных там держала рука автора.

## Коротко: что запомнить

- sqlc.yaml — единственная карта проекта: и generate, и vet читают один конфиг из корня репозитория.
- version: "2" — строкой в кавычках; версия "1" — легаси для старых проектов.
- Запись в списке sql: engine задаёт диалект, schema и queries — источники (файл, папка миграций или список путей), gen.go — вывод.
- gen.go: package — имя пакета, out — папка генерации, sql_package — database/sql по умолчанию или pgx/v5 одной строкой.
- db.go содержит DBTX, New и WithTx; models.go — структуры таблиц; оба файла перезаписываются при каждой генерации — руками их не правят.
- emit-опции добавляют детали: emit_json_tags (json-теги), emit_interface (querier.go), emit_prepared_queries.
- Типичные падения конфига: опечатка в пути, чужой диалект в engine, незаполненный gen.go.

## Домашнее задание

Соберите конфиг с нуля для трекера задач: сделайте папки db/migrations и db/queries, перенесите схему todos из первого урока в миграцию 001_init.sql, добавьте 002_priority.sql с колонкой priority, запустите sqlc init и доведите заготовку до движка postgresql с папками из урока, генерацией в db/sqlc. После успешного sqlc generate уроните его трижды специально: уберите кавычки у version, смените engine на mysql, сделайте опечатку в queries. Прочитайте каждое сообщение об ошибке — и откатите поломки, вернув генерацию в зелёное состояние.

## Вопросы и ответы по уроку

### Где sqlc ищет файл sqlc.yaml?

В каталоге, из которого запускается команда, — поэтому sqlc generate гоняют из корня проекта. Допустимо и имя sqlc.json. Все пути внутри конфига относительны папке, где лежит сам файл конфига.

### sql_package: database/sql или pgx/v5 — что выбрать?

database/sql — стандартная библиотека и портабельность: код работает с любым зарегистрированным драйвером. pgx/v5 — нативный API PostgreSQL: скорость и pgtype-типы. Выбор — одна строка в gen.go, но вместе с ним меняется тип соединения, который приложение передаёт в db.New.

### Можно ли держать в одном конфиге две базы?

Да: список sql принимает несколько записей, у каждой свои engine, schema, queries и gen.go с собственным out. Так репозиторий генерирует пакеты и для PostgreSQL, и, скажем, для SQLite со статистикой — не мешая друг другу.
