Создание службы RESTful API в Go без повторяющихся шаблонов
Есть много материалов о том, как писать сервисы, где сначала нужно выбрать какой-то фреймворк для использования, потом идет разводка обработчиков, конфигов, логов, хранилища и т.д., не говоря уже о том, чтобы где-то развернуть этот сервис. Мы пишем сервисы уже довольно давно, и чаще всего вы просто хотите пропустить весь этот утомительный процесс склейки вещей и просто написать какой-нибудь полезный код.
Вот почему мы создали инструмент под названием Mify — это генератор шаблонов инфраструктуры с открытым исходным кодом, который поможет вам создать сервис, используя лучшие практики, используемые на сегодняшний день. Итак, в этом уроке мы покажем, как создать простой сервис с помощью Mify на классическом примере — приложение для работы с задачами.
Предпосылки
- Установить Go 1.18+, Докер
- Получите Mify с нашего GitHub: https://github.com/mify-io/mify
- Установите Postman или curl для проверки конечных точек
Прежде чем приступить к этому руководству, вот ссылка на полный пример: https://github.com/mify-io/todo-app-example.
Создание проекта
Сначала нам нужно инициализировать рабочее пространство проекта, это будет место, где будет создана внутренняя служба, но вы также можете создать больше служб и внешних интерфейсов. Запустите эти команды:
$ mify init todo-app $ cd todo-app
После входа в новую рабочую область запустите:
$ mify add service todo-backend
Теперь это создаст шаблон Go для вашего бэкэнда. Вот упрощенное дерево рабочей области со всеми сгенерированными файлами:
. ├── go-services │ ├── cmd │ │ ├── dev-runner │ │ │ └── main.go │ │ └── todo-backend │ │ ├── Dockerfile │ │ └── main.go │ ├── go.mod │ ├── go.sum │ └── internal │ ├── pkg │ │ └── generated │ │ ├── configs │ │ │ └── ... │ │ ├── consul │ │ │ └── ... │ │ ├── logs │ │ │ └── ... │ │ └── metrics │ │ └── ... │ └── todo-backend │ ├── app │ │ ├── request_extra.go │ │ ├── router │ │ │ └── router.go │ │ └── service_extra.go │ └── generated │ ├── api | | └── ... │ ├── app │ │ └── ... │ ├── apputil │ │ └── ... │ └── core │ └── ... ├── schemas │ └── todo-backend │ ├── api │ │ └── api.yaml │ └── service.mify.yaml └── workspace.mify.yaml
Mify примерно следует макету из https://github.com/golang-standards/project-layout, который, несмотря на название репозитория, не является стандартным, но довольно распространенным для сервисов Go. В internal/pkg/generated
есть общие библиотеки для конфигураций, журналов и метрик, которые можно повторно использовать для нескольких сервисов. Каталог вашего сервиса находится в internal/todo-backend
.
На данный момент этот сервис довольно пустой, поэтому нам нужно добавить к нему API.
Определение API
Mify позволяет вам определить API со схемой OpenAPI — таким образом вы получите готовые обработчики без написания большого количества шаблонов. Более того, Mify также генерирует структурированные журналы и метрики, на написание которых вручную ушли бы недели.
Вы можете найти схему OpenAPI для todo-backend в файле schemas/todo-backend/api/api.yaml
. Каталог Schemas в корне рабочей области — это место, где хранятся все конфигурации сервисов, относящиеся к Mify.
Давайте создадим простой CRUD API для вашего бэкенда todo:
POST /todos
для добавления новых заметок о задачах.PUT,GET,DELETE /todos/{id}
для их обновления, получения и удаления.
Вот как будет выглядеть ваша схема OpenAPI для этого API:
Замените предыдущую схему этой и запустите mify generate
. Вы можете запускать его каждый раз, когда обновляете схему, и он будет восстанавливать все измененные данные.
Создание и тестирование
Теперь, когда мы добавили обработчики, есть что протестировать, так что давайте сделаем это. В терминале внутри рабочей области Mify перейдите в каталог go-services
и запустите службу:
$ cd go-services $ go mod tidy $ go run ./cmd/todo-backend
Вы должны увидеть такие журналы запуска:
Вы можете увидеть порт службы в сообщении журнала starting api server
, скопировать его в Postman и попробовать вызвать какой-нибудь обработчик API:
Вы можете видеть, что обработчик ничего не вернул, чего и следовало ожидать, поскольку, как следует из ошибки, он еще не реализован.
Добавление моделей и фиктивного хранилища
Давайте начнем добавлять логику в этот сервис. В этом уроке мы будем следовать простой структуре, подобной DDD (Domain-driven design).
Во-первых, нам нужно создать модель todo-заметки, мы поместим ее в пакет domain
.
in go-services/internal/todo-backend/domain/todo.go
:
Это также хорошее место для определения интерфейса для хранилища, что полезно для отделения логики постоянства от приложения. В этом руководстве мы будем использовать фиктивное хранилище в памяти, но Mify также поддерживает Postgres, который мы можем добавить позже в следующей статье. Поместим хранилище в go-services/internal/todo-backend/storage/todo_mem.go
:
Прежде чем переходить к обработчикам, мы также должны реализовать прикладной уровень, чтобы отделить логику хранения, но в этом примере этот уровень будет просто прокси для него.
internal/todo-backend/application/todo.go
:
На этом все по логике, осталось добавить ее в обработчики.
Реализация обработчиков
Mify генерирует заглушки обработчика в go-services/internal/todo-backend/handlers/<path/to/api>/service.go
. В нашем случае это будут
go-services/internal/todo-backend/handlers/todos/service.go
для метода POST и
go-services/internal/todo-backend/handlers/todos/id/service.go
для других.
Вот пример заглушки метода POST:
Теперь давайте реализуем все обработчики.
go-services/internal/todo-backend/handlers/todos/service.go
Не забудьте обновить импорт:
import ( "net/http" "strconv" "example.com/namespace/todo-app/go-services/internal/todo-backend/domain" "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/api" "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/apputil" "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/core" "example.com/namespace/todo-app/go-services/internal/todo-backend/handlers" )
go-services/internal/todo-backend/handlers/todos/id/service.go
А вот и импорт для них:
import ( "errors" "fmt" "net/http" "strconv" "example.com/namespace/todo-app/go-services/internal/todo-backend/domain" "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/api" "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/apputil" "example.com/namespace/todo-app/go-services/internal/todo-backend/generated/core" "example.com/namespace/todo-app/go-services/internal/todo-backend/handlers" "example.com/namespace/todo-app/go-services/internal/todo-backend/storage" )
Логика обработчика довольно проста, мы просто конвертируем сгенерированные модели OpenAPI в наше приложение и обратно, и чтобы избежать дублирования в коде, вот хелпер для создания ответа TodoNode, который используется в этих реализациях:
go-services/internal/todo-backend/handlers/common.go
:
Тестирование снова
Наконец-то сервис полностью реализован и мы можем его протестировать!
Сначала мы можем добавить новую заметку todo с запросом POST:
Проверьте, добавлен ли он с помощью запроса GET:
Обновите его с помощью запроса PUT:
Удали это:
И снова запустите GET, чтобы проверить, был ли он удален:
Что дальше
В этой статье еще много чего не описано, например:
- Постоянное хранилище, такое как Postgres,
- Конфигурация,
- промежуточное ПО аутентификации,
- Развертывание в облаке.
Большая часть этого материала описана в наших документах, так что ознакомьтесь с ними: https://mify.io/docs, но следите за обновлениями для следующих статей.