Когда пишете GraphQL API на Go, рано или поздно сталкиваетесь с проблемой N+1. Резолвер запрашивает пользователя для каждого поста отдельно — и вместо одного запроса к базе получаете сотню.
graph-gophers/dataloader решает именно это.Что делает библиотека
Она собирает отдельные запросы в батч и выполняет их одним вызовом. Параллельно кэширует результаты на время жизни загрузчика. Это реализация Facebook DataLoader для Go, с поддержкой дженериков начиная с v7.
Как это работает
Вы описываете одну батч-функцию, которая принимает список ключей и возвращает список результатов. Загрузчик сам накапливает ключи из параллельных вызовов
Load(), вызывает вашу функцию один раз и раздаёт ответы обратно через thunk.Thunk — это функция, которую возвращает
Load(). Она блокирует выполнение до тех пор, пока батч не завершится, и отдаёт значение или ошибку.Пример использования
Допустим, нужно загружать пользователей по ID. Без батчинга каждый резолвер делает свой запрос. С
dataloader все запросы в рамках одного окна объединяются:batchFn := func(ctx context.Context, ids []int) []*dataloader.Result[*User] {
users := fetchUsersByIDs(ctx, ids) // один запрос к БД
results := make([]*dataloader.Result[*User], len(ids))
for i, id := range ids {
results[i] = &dataloader.Result[*User]{Data: users[id]}
}
return results
}
loader := dataloader.NewBatchedLoader(batchFn)
// вызывается из разных горутин или резолверов
thunk := loader.Load(ctx, 42)
user, err := thunk()Все вызовы
Load() в одном временном окне попадут в один батч — fetchUsersByIDs выполнится один раз.По умолчанию используется встроенный in-memory кэш. Он рассчитан на короткий цикл жизни — один HTTP-запрос. Если кэш не нужен совсем, есть
NoCache:loader := dataloader.NewBatchedLoader(batchFn, dataloader.WithCache(&dataloader.NoCache[int, *User]{}))Можно передать свою реализацию интерфейса
Cache, например, на основе Redis.Если вы пишете GraphQL-сервер на Go и у вас есть резолверы, которые загружают связанные сущности — библиотека избавит от N+1 без ручного переписывания логики. Работает и без GraphQL: подходит для любых сценариев, где нужно объединять параллельные запросы за данными.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
viper — самая частая рекомендация для конфигурации в Go. Он умеет читать файлы, переменные окружения, удалённые источники и устанавливать дефолты. Звучит удобно, но на практике создаёт проблемы.В чём проблема
Viper тянет большое дерево зависимостей. Методы
GetString, GetInt и подобные не типизированы — значения возвращаются в рантайме без проверки на этапе компиляции. Ключи конфига разбросаны по коду как строковые литералы. При нетривиальном использовании поведение при слиянии и приоритетах конфигов требует чтения исходников.Что использовать
Структура с тегами и
yaml.NewDecoder:type Config struct {
DatabaseURL string `yaml:"database_url"`
Kafka KafkaConfig `yaml:"kafka"`
Server ServerConfig `yaml:"server"`
}
func Load(path string) (*Config, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
var cfg Config
if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}Переменные окружения добавляются вручную через
os.Getenv там, где это нужно.Что получаем
Полностью типизированный конфиг. Никаких строковых ключей. Граф зависимостей, который можно проверить за секунды. Код, который вы полностью контролируете.
Для JSON-конфига подход тот же, только с
encoding/json. Пакет gopkg.in/yaml.v3 — единственная внешняя зависимость, и та минимальная.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👾3
Библиотека Go-разработчика | Golang
📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека Go-разработчика #GoGiggle
📰 Дайджест недели
Кто прочитал, тот завтра на работу не пойдёт!
— ИИ-интервью, архетипы египетских богов и симуляция роли гендиректора
— oapi-codegen v2.7.0
— Почтовый клиент прямо в терминале
— Вышли патч-релизы Go 1.26.3 и 1.25.10
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoLive
Кто прочитал, тот завтра на работу не пойдёт!
— ИИ-интервью, архетипы египетских богов и симуляция роли гендиректора
— oapi-codegen v2.7.0
— Почтовый клиент прямо в терминале
— Вышли патч-релизы Go 1.26.3 и 1.25.10
📍 Навигация: Вакансии • Задачи • Собесы
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
🛠 mise для Go-разработчиков: версии, окружение и задачи в одном файле
В Go-проектах часто встречается такая ситуация: один сервис требует Go 1.21, другой уже на 1.22, а на CI стоит что-то третье. Переключаться руками неудобно,
Установка
Для zsh и fish команда активации аналогична, только меняется шелл.
Пример mise.toml для Go-проекта
Установить Go нужной версии и сразу запустить сборку:
Как переключаться между версиями Go
Глобально поставить одну версию, а в конкретном проекте использовать другую:
Внутри папки проекта это переопределяется через
Проверить, какая версия активна прямо сейчас:
Почему это лучше Makefile
С
Если в проекте несколько сервисов
➡️ Репозиторий
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction
В Go-проектах часто встречается такая ситуация: один сервис требует Go 1.21, другой уже на 1.22, а на CI стоит что-то третье. Переключаться руками неудобно,
GOPATH и GOROOT легко запутать, а Makefile со временем превращается в набор магических команд, которые понимает только автор.mise решает это без лишних движений: один файл mise.toml в корне проекта фиксирует версию Go, переменные окружения и команды сборки.Установка
curl https://mise.run | sh
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
Для zsh и fish команда активации аналогична, только меняется шелл.
Пример mise.toml для Go-проекта
[tools]
go = "1.22"
[env]
GOFLAGS = "-mod=vendor"
APP_ENV = "development"
DATABASE_URL = "postgres://localhost/myapp_dev"
[tasks.build]
description = "Собрать бинарник"
run = "go build -o bin/app ./cmd/app"
[tasks.test]
description = "Запустить тесты"
run = "go test ./..."
[tasks.lint]
description = "Запустить golangci-lint"
run = "golangci-lint run"
[tasks.run]
description = "Запустить сервер"
depends = ["build"]
run = "./bin/app"
Установить Go нужной версии и сразу запустить сборку:
mise install
mise run build
Как переключаться между версиями Go
Глобально поставить одну версию, а в конкретном проекте использовать другую:
mise use --global go@1.22
Внутри папки проекта это переопределяется через
mise.toml. mise сам выставит правильный PATH при входе в директорию — никаких export GOROOT руками.Проверить, какая версия активна прямо сейчас:
mise current go
Почему это лучше Makefile
Makefile не знает ничего про версии инструментов и переменные окружения. Разработчик клонирует репозиторий, запускает make build и получает ошибку, потому что у него Go 1.20, а проект требует 1.22.С
mise картина другая: mise install сам скачивает нужную версию Go, выставляет переменные и после этого mise run build работает так же, как на машине соседа и на CI.Если в проекте несколько сервисов
mise поддерживает вложенные конфиги. В каждом подкаталоге со своим mise.toml будет своя версия Go и своё окружение. Глобальный конфиг при этом остаётся как запасной вариант.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤4👾1
🐳 Контейнер от root — это не норма
Пользователь в Docker-контейнере по умолчанию root.
Если сервис скомпрометирован и атакующий получает доступ внутрь контейнера, запуск от root существенно расширяет поверхность атаки.
Как исправить
В distroless-образах пользователь
В Alpine-образах пользователя нужно создать вручную:
Дополните защиту read-only корневой файловой системой — в pod spec Kubernetes или в Docker Compose:
Это лишает атакующего возможности записывать файлы в контейнер даже при наличии RCE-уязвимости.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoDeep
Пользователь в Docker-контейнере по умолчанию root.
Если сервис скомпрометирован и атакующий получает доступ внутрь контейнера, запуск от root существенно расширяет поверхность атаки.
Как исправить
В distroless-образах пользователь
nonroot уже есть из коробки:FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
USER nonroot:nonroot
CMD ["/server"]
В Alpine-образах пользователя нужно создать вручную:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
Дополните защиту read-only корневой файловой системой — в pod spec Kubernetes или в Docker Compose:
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
Это лишает атакующего возможности записывать файлы в контейнер даже при наличии RCE-уязвимости.
📍 Навигация: Вакансии • Задачи • Собесы
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤5👍3
Миграции в проекте рано или поздно превращаются в хаос. Кто-то забыл накатить скрипт, у кого-то локально другая версия схемы, а в проде вообще непонятно что.
goose — это инструмент для миграций на Go, который работает и как CLI, и как библиотека.Что умеет
goose поддерживает Postgres, MySQL, SQLite, ClickHouse, MSSQL, Spanner, YDB и ещё несколько баз. Миграции пишутся на SQL или на Go. Есть поддержка embed.FS, чтобы вшивать миграции прямо в бинарник. Можно применять миграции не по порядку, подставлять переменные окружения в SQL-файлы и запускать отдельные миграции без версионирования.
Если бинарник получается слишком большим, можно собрать без лишних драйверов:
go build -tags='no_postgres no_mysql no_sqlite3 no_ydb' -o goose ./cmd/goose
Как работать
Создаём первую миграцию:
goose create add_users_table sql
Goose сгенерирует файл вида
20250511120000_add_users_table.sql. Внутри два блока:-- +goose Up
CREATE TABLE users (
id int NOT NULL,
name text,
email text,
PRIMARY KEY(id)
);
-- +goose Down
DROP TABLE users;
Всё, что после
-- +goose Up, выполняется при накатывании миграции. Всё после -- +goose Down — при откате.Применяем все миграции:
goose postgres "user=postgres dbname=mydb sslmode=disable" up
Откатываем последнюю:
goose postgres "user=postgres dbname=mydb sslmode=disable" down
Смотрим статус:
goose postgres "user=postgres dbname=mydb sslmode=disable" status
Чтобы не передавать драйвер и строку подключения каждый раз, можно задать переменные окружения или положить их в
.env файл:GOOSE_DRIVER=postgres
GOOSE_DBSTRING=postgres://admin:admin@localhost:5432/mydb
GOOSE_MIGRATION_DIR=./migrations
Миграции на Go
Если SQL не хватает, миграции можно писать на Go:
package migrations
import (
"database/sql"
"github.com/pressly/goose/v3"
)
func init() {
goose.AddMigration(Up, Down)
}
func Up(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin' WHERE username='root';")
return err
}
func Down(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='root' WHERE username='admin';")
return err
}
Встраивание миграций в бинарник
С Go 1.16 можно вшить SQL-файлы миграций прямо в приложение через
embed.FS:package main
import (
"database/sql"
"embed"
"github.com/pressly/goose/v3"
)
//go:embed migrations/*.sql
var embedMigrations embed.FS
func main() {
var db *sql.DB
// настройка подключения к БД
goose.SetBaseFS(embedMigrations)
if err := goose.SetDialect("postgres"); err != nil {
panic(err)
}
if err := goose.Up(db, "migrations"); err != nil {
panic(err)
}
}
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥8❤3👾1
resty делает HTTP-запросы в Go удобнее. Но платите вы за это абстракцией поверх того, что стандартная библиотека уже умеет хорошо.Resty скрывает детали реализации. Логика ретраев, поведение таймаутов, политика редиректов — всё это настраивается через API Resty, а не через
http.Transport. Когда что-то ломается в проде, вы отлаживаете через два слоя вместо одного.Что использовать
Тонкая обёртка вокруг
net/http. Пишется один раз:type Client struct {
base string
http *http.Client
headers map[string]string
}
func (c *Client) Get(ctx context.Context, path string, out any) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.base+path, nil)
if err != nil {
return err
}
for k, v := range c.headers {
req.Header.Set(k, v)
}
resp, err := c.http.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(out)
}
http.Transport настраивается напрямую. Таймауты выставляются на http.Client. Ретраи с экспоненциальным бэкоффом добавляются именно там, где нужны.Для межсервисного взаимодействия, внутренних API и интеграций с внешними сервисами этой обёртки более чем достаточно. Поведение предсказуемо, отладка прямолинейна, зависимостей нет.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13😁1🤔1
Иногда нужно дать пользователям или операторам возможность менять поведение программы без пересборки — конфиги не всегда справляются, а встраивать Lua кажется чужеродным.
let-go предлагает другой вариант: язык в духе Clojure, написанный на Go и встраиваемый прямо в приложение.
Что это такое
let-go — это компилятор байткода и виртуальная машина для языка, который вплотную копирует Clojure. Реализован на Go, распространяется как обычный модуль.
Это не замена оригинального Clojure и не инструмент для его форматирования, а именно встраиваемый язык расширения.
Статус проекта — WIP, но уже работающий: автор решал на нём задачи Advent of Code 2022.
Что уже есть
Из реализованного: макросы с syntax quote, деструктуризация, функции с несколькими арностями, атомы, каналы и go-блоки в стиле
core.async, регулярные выражения, простые пространства имён json, http и os, REPL с подсветкой синтаксиса и автодополнением, базовый nREPL-сервер совместимый с Calva.Установка и запуск
go install github.com/nooga/let-go@latest
Запустить REPL:
let-go
Выполнить выражение напрямую:
let-go -e '(+ 1 1)'
Запустить файл:
let-go test/hello.lg
Запустить файл, а потом открыть REPL:
let-go -r test/simple.lg
Как выглядит код
Синтаксис — чистый Clojure. Пример с каналами и go-блоками:
(def ch (chan 10))
(go (>! ch "hello from goroutine"))
(println (<! ch))
Деструктуризация и работа со списками:
(let [[a b & rest] [1 2 3 4 5]]
(println a b rest))
; => 1 2 (3 4 5)
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5😁2
🔥 Знакомьтесь с экспертом Proglib.academy: Эмиль Сатаев
Эмиль — эксперт с 8-летним опытом в разработке, который специализируется на внедрении LLM и агентных подходов в реальные коммерческие сервисы. Он точно знает, как проектировать архитектуру так, чтобы ИИ-функции работали стабильно в связке с внешними системами.
🏃♀️ Уже 14 мая Эмиль проведет открытый вебинар!
Обсудим самую «больную» тему: «Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало».
🗓 Когда: 14 мая в 19:00 (Мск)
Почему Эмиля стоит послушать:
🟣 8+ лет в разработке (Backend и Frontend)
🟣 Международный исследовательский опыт
🟣 Преподаватель-практик
🟣 Мастер интеграции AI в Backend
🔗 Зарегистрироваться на вебинар
Эмиль — эксперт с 8-летним опытом в разработке, который специализируется на внедрении LLM и агентных подходов в реальные коммерческие сервисы. Он точно знает, как проектировать архитектуру так, чтобы ИИ-функции работали стабильно в связке с внешними системами.
Обсудим самую «больную» тему: «Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало».
🗓 Когда: 14 мая в 19:00 (Мск)
Почему Эмиля стоит послушать:
Прошел путь от фулстека до Backend Platform Developer в SMIT.Studio.
Работал исследователем в Институте ИИ НИУ ВШЭ и в Национальном университете Сингапура (NUS).
Ведет семинары в НИУ ВШЭ, в том числе по проектированию и разработке агентских систем.
Его главная суперсила — умение правильно встраивать LLM через API, выстраивать workflow и агентную логику в сложных распределенных системах.
🔗 Зарегистрироваться на вебинар
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱6👍3❤1
Go долго жил без дженериков. Если нужно было написать одну и ту же функцию для
int и float64, приходилось копировать код или использовать interface{} с приведением типов. В версии 1.18 это наконец исправили.Разберём, что такое дженерики в Go, как они работают и зачем нужны type constraints.
Зачем нужны дженерики
Представьте, что вам нужна функция поиска минимума. Без дженериков пришлось бы писать отдельную версию для каждого типа:
func MinInt(a, b int) int {
if a < b { return a }
return b
}
func MinFloat(a, b float64) float64 {
if a < b { return a }
return b
}С дженериками всё сводится к одной функции:
func Min[T cmp.Ordered](a, b T) T {
if a < b { return a }
return b
}Теперь
Min работает с любым типом, который поддерживает сравнение. Вызывается так же просто, как обычная функция. Go сам определяет тип по аргументам:
fmt.Println(Min(3, 5)) // 3
fmt.Println(Min(3.14, 2.71)) // 2.71
fmt.Println(Min("a", "b")) // "a"
Синтаксис
Типовой параметр указывается в квадратных скобках перед обычными аргументами.
После имени параметра идёт constraint, которое определяет, какие типы допустимы:
func Print[T any](value T) {
fmt.Println(value)
}Здесь
T any означает, что T может быть чем угодно. Можно использовать несколько типовых параметров:
func Pair[T, U any](first T, second U) (T, U) {
return first, second
}Встроенные ограничения
Go предлагает несколько готовых constraint'ов.
any принимает любой тип. Подходит, когда вам не нужны никакие операции над значением, кроме передачи и хранения.comparable принимает типы, которые можно сравнивать через == и !=. Полезно для поиска по слайсу или использования в качестве ключа map.func Contains[T comparable](slice []T, target T) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}cmp.Ordered из пакета cmp (Go 1.21+) принимает типы с поддержкой операторов <, >, <=, >=. Это все числовые типы и строки.func Max[T cmp.Ordered](values ...T) T {
max := values[0]
for _, v := range values[1:] {
if v > max {
max = v
}
}
return max
}Свои ограничения
Можно создавать собственные constraint'ы через интерфейсы с перечислением типов:
type Number interface {
int | int8 | int16 | int32 | int64 |
float32 | float64
}
func Sum[T Number](values []T) T {
var sum T
for _, v := range values {
sum += v
}
return sum
}Оператор
~ позволяет принимать не только базовые типы, но и все типы, основанные на них. Если у вас есть
type MyInt int, то constraint ~int примет и MyInt:type Integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
type MyInt int
func Double[T Integer](v T) T {
return v * 2
}
var x MyInt = 5
Double(x) // работает, потому что MyInt основан на intConstraint'ы можно комбинировать. Если нужен тип, который одновременно поддерживает сравнение и имеет метод
String(), это тоже реализуемо:type OrderedStringer interface {
cmp.Ordered
fmt.Stringer
}Дженерики убирают дублирование кода без потери типобезопасности. В Go они реализованы лаконично, без сложной иерархии типов.
📍 Навигация: Вакансии • Задачи • Собесы
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15❤4⚡1
Backend-разработчик (Go, PostgreSQL) — до 200 000 ₽, удалённо по Москве
Golang разработчик (Senior) — от 350 000 ₽, удалённо по Москве
Senior Golang Developer — до 280 000 ₽, удаленно
#GoWork
Please open Telegram to view this post
VIEW IN TELEGRAM
👨💻 Урок проведёт руководитель курса «Go-разработчик. Продвинутый уровень» Юра Рубаха. На вебинаре сможете задать любые вопросы по программе, чтобы убедиться, что курс вам подходит.
Урок проходит в преддверии старта курса «Go-разработчик. Продвинутый уровень». Если вы хотите писать предсказуемые и эффективные сервисы — подключайтесь.
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱3❤1
Если вы работали с Go и вам нужно было вызвать C-библиотеку, то вы наверняка сталкивались с Cgo.
Cgo работает, но тянет за собой целый набор проблем. Нужен C-компилятор на каждой целевой платформе. Кросс-компиляция превращается в боль. Сборка замедляется. Бинарники раздуваются.
purego решает всё это, позволяя вызывать C-функции из чистого Go.Откуда взялся проект
Библиотека выросла из игрового движка Ebitengine. Его авторы портировали движок на чистый Go для Windows, что позволило кросс-компилировать на Windows с любой ОС одной командой
GOOS=windows. purego родился, чтобы принести тот же подход на macOS, Linux и другие платформы.Что даёт purego
Без Cgo отпадает необходимость в C-компиляторе. Вы можете собирать проект под другую платформу, просто задав
GOOS и GOARCH. Сборка кешируется целиком как обычный Go-проект и работает быстрее. Бинарники становятся меньше, потому что Cgo генерирует обёртку на C для каждого вызова, а purego этого не делает.Ещё
purego умеет загружать символы из shared-библиотек в рантайме. Это можно использовать как систему плагинов или для FFI-вызовов в библиотеки на других языках, скомпилированные в .so / .dylib / .dll.purego работает и при CGO_ENABLED=1. Это значит, что можно портировать проект с Cgo на purego постепенно, не переписывая всё разом.Как это выглядит в коде
API минимальный. Вы открываете библиотеку через
Dlopen, затем регистрируете Go-функцию, которая будет вызывать C:package main
import (
"fmt"
"runtime"
"github.com/ebitengine/purego"
)
func getSystemLibrary() string {
switch runtime.GOOS {
case "darwin":
return "/usr/lib/libSystem.B.dylib"
case "linux":
return "libc.so.6"
default:
panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS))
}
}
func main() {
libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
panic(err)
}
var puts func(string)
purego.RegisterLibFunc(&puts, libc, "puts")
puts("Calling C from Go without Cgo!")
}
Обратите внимание на
RegisterLibFunc. Вы объявляете переменную с нужной Go-сигнатурой, а purego привязывает её к C-функции по имени. Никаких // #cgo директив, никаких .h файлов.Когда стоит использовать
purego подходит, если вам нужно вызывать C-библиотеку из Go и при этом важна простота сборки и кросс-компиляция. Типичные сценарии — работа с системными библиотеками, графические движки, аудио, нативные SDK.Если ваш проект уже плотно завязан на Cgo и работает на одной платформе, смысла переезжать может не быть. Но если вы начинаете новый проект или хотите избавиться от зависимости на C-тулчейн,
purego стоит попробовать.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥3
🧑💻 Gorilla Mux переоценён. Go справляется сам
Что изменилось в Go 1.22
Стандартный
Параметр пути читается через
Когда нужна внешняя библиотека
Если реально нужны сложные цепочки middleware, субрутеры или маршруты с регулярными выражениями — возьмите
Почему не Gorilla
Gorilla Mux поддерживается, но его дни как разумного дефолта прошли. Вы добавляете зависимость ради функций, которые теперь есть в стандартной библиотеке.
Для нового проекта на Go 1.22+: начните с
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction
gorilla/mux годами был стандартным выбором для HTTP-роутинга в Go. Он добавлял параметры пути, роутинг по методам и поддержку middleware поверх скромного ServeMux. Но Go 1.22 закрыл большинство этих пробелов.Что изменилось в Go 1.22
Стандартный
ServeMux теперь поддерживает методы и параметры пути прямо в паттерне маршрута:mux := http.NewServeMux()
mux.HandleFunc("GET /deposits/{walletID}", handleGetDeposit)
mux.HandleFunc("POST /deposits", handleCreateDeposit)
mux.HandleFunc("DELETE /deposits/{walletID}", handleDeleteDeposit)
Параметр пути читается через
r.PathValue("walletID"). Роутинг по методу встроен. Для большинства REST API, внутренних сервисов и обработчиков вебхуков этого достаточно.Когда нужна внешняя библиотека
Если реально нужны сложные цепочки middleware, субрутеры или маршруты с регулярными выражениями — возьмите
github.com/go-chi/chi/v5. Он легче Gorilla, хорошо компонуется и следует идиомам Go.Почему не Gorilla
Gorilla Mux поддерживается, но его дни как разумного дефолта прошли. Вы добавляете зависимость ради функций, которые теперь есть в стандартной библиотеке.
Для нового проекта на Go 1.22+: начните с
net/http. Если не хватает — возьмите chi. Gorilla Mux нужен только в очень специфических случаях или при поддержке старого кода.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12👏3❤2