Как создать простой REST API с Clojure
Вступление
Сегодня веб-сервисы могут варьироваться по объему от базового API с несколькими таблицами в БД до крупномасштабных систем, поддерживающих сотни миллионов пользователей, с массивными кодовыми базами и временем отклика, настроенным с точностью до миллисекунды. В зависимости от поставленной задачи современный разработчик имеет широкий выбор инструментов с десятками популярных языков и фреймворков, каждый со своим особым взглядом на то, как что-то делать для решения заданного набора проблем.
Среди этих языков - Clojure, все более популярный диалект Lisp, ориентированный на JVM, Microsoft CLR, движки JavaScript и другие платформы. Lisp существует уже невероятные 60 лет и был разработан в Массачусетском технологическом институте для реализации Lambda Calculus. Clojure основывается на Lisp, используя уже существующие среды выполнения (такие как JVM) и добавляя отличные функции, такие как потокобезопасные типы данных для параллелизма и взаимодействие Java VM.
В этой статье мы рассмотрим, как реализовать JSON REST API с Clojure и PostgreSQL для хранения коллекции друзей с именем, псевдонимом и занятие и получить список друзей обратно из БД.
Копия исходного кода этого проекта доступна здесь, на GitHub.
Обзор
Clojure - это язык функционального программирования (FP) с интересным и мощным синтаксисом, который может показаться незнакомым, если исходить из процедурного или объектно-ориентированного мышления. FP является декларативным, что означает, что код определяется с точки зрения того, что нужно выполнить, а не того, как это сделать.
Это отражение того, как FP эволюционировал непосредственно из области математики, что привело к очень чистому и мощному языку, который уменьшает побочные эффекты, препятствуя изменению состояния (изменчивость).
В этой статье основное внимание будет уделено практическому аспекту создания и запуска API, поэтому мы начнем с настройки среды и проверки нашего .env.development
файла по умолчанию с переменными конфигурации проекта.
Конфигурация
Для запуска этого проекта требуется следующая настройка:
- Clojure (с пивом:
brew install clojure
) - Leiningen (с пивом:
brew install leinengen
) - PostgreSQL
Чтобы инициализировать базу данных, выполните команду $ ./db_create.sh
, которая создаст базу данных clojure_pg_example
и friends
таблицу. Информация о подключении к базе данных, а также порт, на котором будет работать API, содержатся в файле .env.development:
Этот файл будет использоваться пакетом dotenv для инициализации переменных среды для локальной разработки, что позволит развернуть этот проект в соответствии с Фактором III (Конфигурация) из Принципов 12-факторного приложения.
Определение проекта
Конфигурация проекта содержится в project.clj:
В дополнение к стандартному имени проекта, описанию и другой информации в этом файле определены зависимости проекта. Добавлены пакеты для настройки HTTP-сервера, обработки JSON, загрузки переменных среды, подключения к базе данных и других рутинных задач.
В :profiles
был добавлен дополнительный профиль:dev {:main clojure-example.core/-dev-main}
, который заставит наше приложение запускать функцию -dev-main
вместо -main
при разработке. Это может быть полезно при настройке таких функций, как автоматическая горячая перезагрузка, которую мы вскоре рассмотрим.
Точка входа в приложение
Выполнение программы начинается в src / clojure_example / core.clj:
В верхней части файла определяется пространство имен с ns
и импортируются библиотеки, которые будут использоваться путем передачи их в :require
, включая те, которые требуются для настройки HTTP-сервера и функций обработчика для конечных точек маршрута.
Макрос aptly-nameddefroutes
определяет маршруты API:
GET /
(повторить запрос)GET /friends
(получить список друзей из БД)POST /friends
(сохранить запись друга в БД)
Две функции -main
и -dev-main
являются доступными функциями точки входа для приложения, в зависимости от того, запущено ли приложение в рабочем режиме или в режиме разработки. Внутри -dev-main
функция wrap-reload
используется для обертывания функций API в обработчике часов, который выполняет горячую перезагрузку любых изменений, внесенных в исходный файл на диске, который используется API. Это чрезвычайно полезно и экономит много времени при создании и тестировании приложения. В остальном функции точки входа такие же, app-routes
обертывание промежуточным программным обеспечением для значений по умолчанию с использованием значений по умолчанию для создания API, а затем, в свою очередь, обертывание этих функций в оболочках JSON для синтаксического анализа тела запроса и форматирования вывода ответа. Далее мы проверим обработчики маршрутов.
Обработчики маршрутов
Функции обработчика маршрута находятся в src / clojure_example / lib / routes.clj:
В этом файле три функции, которые соответствуют трем app-routes
, определенным в предыдущем файле:
echo-route
(дословно повторить объект запроса)get-friends-route
(просто вызываетapi/get-friends
)add-friend-route
(вызываетapi/add-friend
с параметрами запроса JSON)
Отсюда легко увидеть, как чистый синтаксис Clojure может сохранить ваш код аккуратным и легким для чтения (как только вы (осмотритесь (все круглые скобки))).
Внутренний API
Сама логика API содержится в src / clojure_example / lib / api.clj:
Первая функция get-friends
не принимает параметров и просто вызывает db/select
с именем таблицы :friends
и массивом ключевых слов для идентификатора, имени, псевдонима и занятия в качестве полей, возвращаемых из запроса.
Вторая функция add-friend
деструктурирует его аргумент на компоненты, которые затем повторно собираются в переменную record
, которая передается вместе с именем таблицы :fields
в db/insert
функцию. Результирующий объект с другом id
и другими данными возвращается для отправки в качестве ответа обработчиком маршрута.
Уровень базы данных
Последний файл в нашем примере проекта - src / clojure_example / lib / db.clj:
Этот файл импортирует dotenv для доступа к переменным среды в дополнение к JDBC, который мы будем использовать для связи с экземпляром PostgreSQL. Соединение определяется с помощью определения -db
с использованием переменных среды для информации о соединении.
Служебная функция concat-fields
использует clojure.string/join
для объединения элементов в массиве вместе с ,
разделителем для упаковки параметров в строку, разделенную запятыми, которая будет использоваться в запросе SQL.
Функция insert
вызывает jdbc/insert
, передавая целевой :table
и record
для вставки. Объект результата будет заключен в массив, поэтому функция first
используется для извлечения результата из массива с одним элементом, который затем возвращается обработчику маршрута для отправки обратно в виде ответа JSON.
Функция select
вызывает jdbc/query
с именем :table
и SQL-запрос, состоящий из оператора выбора, запрашивающего fields
(которые отформатированы для SQL с использованием вышеупомянутой функции concat-fields
) из указанного :table
и возврата полученного списка записей обратно в маршрут. обработчик для отправки клиенту в виде ответа JSON.
Тест-драйв
Чтобы протестировать API, запустите сервер с lien run server
, который получит необходимые зависимости и напечатает сообщение о том, что сервер работает на порту, указанном в файле среды. Служебный сценарий ./post.sh
для удобства предоставлен в виде простой оболочки curl:
$ ./post.sh '{"name":"Sam","nickname":"X","occupation":"ninja"}'
Это вернет результирующий объект с предоставленными данными, а также id
и метку времени создания, подтверждающую успешное завершение нашей операции POST. Чтобы получить список ранее добавленных друзей:
$ curl localhost:3000/friends
Это вернет массив объектов в формате JSON с именем, псевдонимом и описанием, тремя полями, запрошенными в запросе к базе данных, описанном ранее.
Заключение
У Clojure довольно крутая кривая обучения из-за его неумолимых корней в математике, но при правильном использовании получаемые программы надежны, просты в обслуживании, невероятно быстры и отзывчивы. Возможность загружать и запускать новый код на лету без перезапуска JVM - это лишь одна из мощных функций этого языка, которая также требует более высокой заработной платы, чем любой другой язык с 2019 года, согласно в этот отчет о переполнении стека.
В этой статье показано, как простой готовый к работе с облаком REST API в комплекте с базой данных PostgreSQL можно быстро собрать в Clojure с помощью всего нескольких строк чистого, надежного кода.
Спасибо за чтение и удачи в следующем проекте!
Кеннет Рейли (8_bit_hacker) технический директор LevelUP