Пошаговое руководство по созданию бессерверного API Go

Благодаря недавней поддержке Go на AWS Lambda я начал возиться с созданием различных бессерверных веб-приложений. По мере того, как приложения становились более сложными (требующими более 1 или 2 маршрутов), компоновка проекта, локальная разработка и развертывание становились все более сложными. Узнав об этих болевых точках, я составил это пошаговое руководство для тех, кто заинтересован в быстром создании и запуске настоящего приложения.

Для тех, кто лучше всего учится, просматривая пример приложения, вы можете найти готовое приложение на GitHub.

Что мы будем строить

В этой статье вы узнаете, как создать шину событий и развернуть ее в AWS. Наш автобус для мероприятий будет иметь 3 маршрута, которые позволят вам:

  • POST /subscriptions - типы событий подписки для маршрутизации в конечную точку. Этот маршрут будет принимать параметры event_type и endpoint.
  • POST /events - Доставить данные о событии всем применимым конечным точкам подписки с помощью POST. Этот маршрут принимает event_type и payload.
  • DELETE /subscriptions/{id} - Удалить подписку по ID.

Чтобы гарантировать, что не каждый может использовать нашу шину событий, мы также добавим аутентификацию, проверив значение заголовка авторизации.

Как мы можем легко справиться со всем этим?

Мы будем использовать несколько интересных технологий для создания и развертывания нашей шины событий. AWS Lambda и AWS API Gateway будут использоваться для размещения и выполнения нашего кода. Для обработки аутентификации и простой рендеринга ответов для наших маршрутов мы будем использовать Lambda Go API Proxy для использования Gin Gonic.

Для локальной разработки мы можем использовать AWS SAM CLI (далее мы будем называть его SAM local). Этот инструмент будет имитировать Lambda и API-шлюз, поэтому вы можете взаимодействовать с приложением, нажимая localhost: 3000, вместо того, чтобы выполнять развертывание для просмотра каждого изменения.

Для развертывания нашего приложения мы будем использовать Serverless, который позволяет нам развертывать с помощью одной команды. Serverless упрощает определение маршрутов, параметров пути и переменных среды для использования в нашей живой среде через файл serverless.yml.

Структура проекта

API Gateway будет передавать детали HTTP-запроса в Lambda, который выполнит один двоичный файл для обработки входящих данных и передачи ответа. Чтобы удовлетворить эту архитектуру, наше веб-приложение будет состоять из 3 двоичных файлов, каждый из которых импортирует общий пакет Go. Все наши модели аутентификации, бизнес-логики и баз данных будут содержаться в пакете с подходящим названием eventbus.

Этот макет очень похож на стандартный макет проекта Go CMD и для очень сложных веб-приложений, безусловно, стоит проверить. Для целей этого проекта мы будем организовывать наши двоичные файлы по их общедоступным путям и действиям. endpoints/events/create/main.go будет обрабатывать POST /events маршрут и будет скомпилирован в bin/events/create.

Пакет eventbus

База данных

Данные - это любовь, данные - это жизнь, без данных в Интернете ничего не происходит. Учитывая эту мудрую пословицу, которую я только что придумал, давайте настроим наш код подключения к базе данных в eventbus/database.go. Мы будем использовать Gorm, диспетчер отношений объектов на основе Go, для обработки подключения к базе данных, моделей, запросов и миграций.

Создайте новый файл eventbus/database.go и добавьте следующий код:

package eventbus

import (
	"os"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/postgres"
)

var db *gorm.DB
var err error

func init() {
	db, err = gorm.Open(
		"postgres",
		"host="+os.Getenv("DATABASE_HOST")+
			" user="+os.Getenv("DATABASE_USER")+
			" dbname="+os.Getenv("DATABASE_NAME")+
			" password="+os.Getenv("DATABASE_PASSWORD")+
			" sslmode=disable"+
			" connect_timeout=5")

	if err != nil {
		panic(err)
	}

	db.LogMode(true)

	db.AutoMigrate(&Event{})
	db.AutoMigrate(&Subscription{})
}

Это довольно просто. Функция init() будет вызываться при запуске приложения. Мы откроем соединение с базой данных PostgreSQL и сделаем его доступным для всех других файлов в пакете eventbus, сохранив его в глобальной переменной db. Два вызова db.AutoMigrate создадут таблицы для размещения двух наших основных структур. В рамках данной статьи предполагается, что мы работаем с базой данных RDS PostgreSQL.

Давайте начнем использовать наше соединение с базой данных с создания структуры подписки! Если вы помните, в структуре подписки будет поле event_type и поле endpoint, которые оба будут строками. При добавлении gorm.Model в столбцы структуры подписки для id, created_at, updated_at и deleted_at будут автоматически добавлены в таблицу subscriptions. Поскольку мы будем запрашивать подписки до event_type, имеет смысл добавить индекс в это поле.

Создайте новый файл eventbus/subscriptions.go и добавьте следующий код:

package eventbus

import (
	"github.com/jinzhu/gorm"
)

type Subscription struct {
	gorm.Model
	EventType string `json:"event_type" sql:"index"`
	Endpoint  string `json:"endpoint"`
}

Подписки

Выглядишь круто. Поскольку мы планируем поддерживать маршрут для создания подписок и маршрут для удаления подписок, мы должны добавить функции для обработки этого. Обе эти функции просты.

CreateSubscription принимает две строки, тип события и конечную точку, и заполняет новую структуру подписки, вставляет ее в базу данных и возвращает ее.

DeleteSubscription примет идентификатор в виде строки, вытащит запись из базы данных, удалит ее, а затем вернет подписку. В случае, если запись не была найдена, будет возвращена пустая структура подписки.

Добавьте следующий код в eventbus/subscriptions.go.

func CreateSubscription(eventType string, endpoint string) Subscription {
	subscription := Subscription{
		EventType: eventType,
		Endpoint:  endpoint,
	}

	db.Create(&subscription)

	return subscription
}

func DeleteSubscription(id string) Subscription {
	var subscription Subscription
	db.First(&subscription, id)
	db.Delete(&subscription)
	return subscription
}

События

Аналогом структуры Subscription будет наша структура Event. Событие будет состоять из EventType и Payload, которые мы будем хранить в виде строк.

Создайте новый файл eventbus/events.go и добавьте следующий код:

package eventbus

import (
	"github.com/jinzhu/gorm"
)

type Event struct {
	gorm.Model
	EventType string `json:"event_type"`
	Payload   string `json:"payload"`
}

Поскольку мы будем поддерживать только создание маршрута для событий, нам нужно будет добавить только одну дополнительную функцию. Функция CreateEvent принимает тип события и полезную нагрузку как строки. Эти параметры будут использоваться для заполнения новой структуры события, которая затем будет сохранена в базе данных и возвращена.

Добавьте следующий код в eventbus/events.go.

func CreateEvent(eventType string, payload string) Event {
	event := Event{
		EventType: eventType,
		Payload:   payload,
	}

	db.Create(&event)
	startDeliveries(&event)
	return event
}

Доставка

Проницательный читатель заметит, что startDeliveries, который вызывается в предыдущем блоке кода, еще не написан. Основная функция шины событий - доставлять события подписчикам. Таким образом, давайте добавим бизнес-логику, передающую данные о событиях всем применимым подписчикам.

Создайте новый файл eventbus/deliveries.go и добавьте следующий код:

package eventbus

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

func startDeliveries(event *Event) {
	var subscriptions []Subscription
	db.Where("event_type = ?", event.EventType).Find(&subscriptions)

	for _, subscription := range subscriptions {
		deliverEvent(event, subscription.Endpoint)
	}
}

type Payload struct {
	Payload string `json:"payload"`
}

func deliverEvent(event *Event, path string) {
	payload := Payload{
		Payload: event.Payload,
	}
	data, _ := json.Marshal(payload)

	req, _ := http.NewRequest("POST", path, bytes.NewBuffer(data))
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{
		Timeout: time.Second * 30,
	}

	resp, _ := client.Do(req)

	fmt.Println(resp)
}

Наша функция startDeliveries, которая принимает указатель на событие, будет запрашивать в базе данных всех подписчиков на тип события. Затем мы переберем возвращенные подписки и передадим событие и конечную точку подписки в функцию deliverEvent.

Функция deliverEvent берет строку данных события, помещает ее в структуру Payload и отправляет в конечную точку подписки. Чтобы наше приложение не зависало на неотвечающих конечных точках, мы добавим 30-секундный тайм-аут при выполнении исходящего запроса. Наконец, ответ будет напечатан, чтобы мы могли увидеть, чем ответила конечная точка.

Маршрутизация и аутентификация

Имея в наличии нашу бизнес-логику, пора подумать о том, как мы будем управлять входящими запросами. Как упоминалось ранее, я счел полезным использовать AWS Lambda Go Api Proxy вместе с Gin для обработки входящих параметров, аутентификации и рендеринга ответов. Когда вызывается одна из наших лямбда-функций, мы создаем глобальный экземпляр ginadapater.GinLambda и используем его для обработки всех входящих запросов от API Gateway. ginadapter принимает ginEngine, для которого, чтобы наш код оставался сухим, мы построим фабрику.

Наша фабрика должна будет создать gin.Engine три вещи: путь, метод HTTP и функция, которую мы хотим обработать запрос. Функция MountAuthorizedRoute инициализирует механизм, присоединяет обработчик аутентификации и настраивает механизм, используя путь, метод HTTP и функцию обработчика, которые мы предоставляем.

Обработчик аутентификации проверит, что входящий запрос содержит значение заголовка аутентификации, которое соответствует переменной среды. Если токен аутентификации отсутствует, будет возвращена ошибка, в которой говорится, что это необходимо. Если токен не совпадает, мы вернем сообщение об этом.

Создайте новый файл eventbus/routes.go и добавьте следующий код:

package eventbus

import (
	"os"

	"github.com/gin-gonic/gin"
)

func MountAuthorizedRoute(path string, method string, fn gin.HandlerFunc) *gin.Engine {
	engine := buildEngine()
	group := engine.Group("/")
	group.Use(authorizedHandler())
	setMethodHandlerForGroup(method, path, fn, group)
	return engine
}

func buildEngine() *gin.Engine {
	engine := gin.New()
	engine.Use(gin.Logger())
	engine.Use(gin.Recovery())
	return engine
}

func setMethodHandlerForGroup(method string, path string, fn gin.HandlerFunc, group *gin.RouterGroup) {
	switch method {
	case "post":
		{
			group.POST(path, fn)
		}
	case "delete":
		{
			group.DELETE(path, fn)
		}
	}
}

func respondWithError(code int, message string, c *gin.Context) {
	resp := map[string]string{"error": message}

	c.JSON(code, resp)
	c.Abort()
}

func authorizedHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.GetHeader("Authorization")

		if token == "" {
			respondWithError(401, "Authorization token required", c)
			return
		}

		if token != os.Getenv("AUTHENTICATION_TOKEN") {
			respondWithError(401, "Invalid Authorization token", c)
			return
		}

		c.Next()
	}
}

Поскольку этот файл не использует функциональные возможности других частей пакета eventbus, мы могли бы извлечь его в свой собственный routing пакет; однако для простоты я решил добавить его в пакет eventbus.

Похлопайте себя по плечу - основной пакет, который будут использовать наши лямбда-функции, готов!

Подключение маршрутов

После всей работы по созданию нашего основного пакета создание обработчиков API Gateway / Lambda будет казаться неприятным. Повторюсь, API Gateway передаст входящий HTTP-запрос в функцию Lambda, которая выполнит двоичный файл, который может обработать запрос и вернуть ответ. Поскольку у нас будет 3 маршрута, мы создадим 3 отдельных приложения, которые все относительно небольшие. Код для этих приложений будет находиться во вложенных папках endpoints/ в одном файле main.go, который будет монтировать маршрут, обрабатывать запросы и возвращать ответы.

Создать подписку

Первое конечное приложение, которое мы создадим, будет обрабатывать запросы на создание новых подписок. Функция main() привяжет функцию Handler для выполнения всеми входящими вызовами Lambda. В свою очередь, функция Handler будет использовать функцию MountAuthorizedRoute, которую мы определили в eventbus, для монтирования нашего маршрута / subscriptions к функции processRequest.

Создайте новый файл endpoints/subscriptions/create/main.go и добавьте следующий код:

package main

import (
	"net/http"

	"github.com/aleccarper/serverless-eventbus/eventbus"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/awslabs/aws-lambda-go-api-proxy/gin"
	"github.com/gin-gonic/gin"
)

var initialized = false
var ginLambda *ginadapter.GinLambda

func Handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	if !initialized {
		ginEngine := eventbus.MountAuthorizedRoute("/subscriptions", "post", processRequest)
		ginLambda = ginadapter.New(ginEngine)
		initialized = true
	}
	return ginLambda.Proxy(req)
}

type Input struct {
	EventType string `form:"event_type" json:"event_type" binding:"required"`
	Endpoint  string `form:"endpoint" json:"endpoint" binding:"required"`
}

func processRequest(c *gin.Context) {
	var input Input
	c.BindJSON(&input)
	subscription := eventbus.CreateSubscription(input.EventType, input.Endpoint)
	c.JSON(http.StatusCreated, subscription)
}

func main() {
	lambda.Start(Handler)
}

Для меня путь импорта - github.com/aleccarper/serverless-eventbus/eventbus, но вам нужно будет изменить его на относительный путь к вашему пакету eventbus.

При выполнении processRequest ему будет передана информация о входящем запросе через параметр *gin.Context. Мы свяжем входящий JSON с новой структурой ввода, а затем передадим event_type и endpoint функции CreateSubscriptions, которую мы создали в пакете eventbus.

После того, как структура подписки будет создана и возвращена CreateSubscriptions, мы вернем ее как JSON обратно в gin.Context со статусом HTTP 201.

Удалить подписки

Приложение, которое будет обрабатывать удаление подписок, будет почти идентично приложению для создания подписок. Создайте новый файл endpoints/subscriptions/delete/main.go и скопируйте / вставьте код, который мы написали для создания подписки. Вы можете продолжить и удалить структуру Input.

Нам нужно будет изменить путь и метод HTTP, используемые при монтировании ginEngine. Изменять:

ginEngine := eventbus.MountAuthorizedRoute("/subscriptions", "post", processRequest)

to:

ginEngine := eventbus.MountAuthorizedRoute("/subscriptions/:id", "delete", processRequest)

и обновите processRequest до:

func processRequest(c *gin.Context) {
	subscription := eventbus.DeleteSubscription(c.Param("id"))
	c.JSON(http.StatusOK, subscription)
}

Создать События

Последнее приложение будет обрабатывать создание событий, и оно также будет почти идентично двум предыдущим. Создайте новый файл endpoints/subscriptions/delete/main.go и скопируйте / вставьте код, который мы написали для приложения для создания подписки.

Нам нужно будет изменить путь и метод HTTP, используемые при монтировании ginEngine. Измените монтажный код на:

ginEngine := eventbus.MountAuthorizedRoute("/events", "post", processRequest)

и обновите Input и processRequest, чтобы:

type Input struct {
	EventType string `form:"event_type" json:"event_type" binding:"required"`
	Payload   string `form:"payload" json:"payload" binding:"required"`
}

func processRequest(c *gin.Context) {
	var input Input
	c.BindJSON(&input)
	event := eventbus.CreateEvent(input.EventType, input.Payload)
	c.JSON(http.StatusCreated, event)
}

И на этом мы закончили писать код! Давайте раскрутим этого плохого парня локально, чтобы мы могли протестировать нашу шину событий перед ее развертыванием.

Запуск нашей шины событий локально

Команда AWS собрала фантастический инструмент для локальной разработки приложений Lambda / API Gateway под названием AWS SAM CLI, и мы будем его использовать.

На момент написания этой статьи существует ошибка в текущем выпуске CLI, которая не позволяет запускать двоичные файлы Go. Чтобы избежать этого, вы можете установить предыдущую версию интерфейса командной строки через NPM, запустив npm install -g aws-sam-local .

Настройка SAM CLI

SAM использует файл конфигурации, который описывает все ваши лямбда-функции. Вы можете использовать этот файл для сопоставления URL-адресов с двоичными файлами и определения других параметров, таких как переменные среды.

Создайте новый файл template.yaml и добавьте следующий код:

AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: 
  An example RESTful service
Resources:
  EventsCreate:
    Type: AWS::Serverless::Function
    Properties:
      Handler: main
      CodeUri: ./bin/events/create.zip
      Runtime: go1.x
      Timeout: 30
      Environment:
        Variables:
          DATABASE_USER:
          DATABASE_PASSWORD:
          DATABASE_HOST:
          DATABASE_NAME:
          AUTHENTICATION_TOKEN:
      Events:
        GetRates:
          Type: Api
          Properties:
            Path: /events
            Method: post
  SubscriptionsCreate:
    Type: AWS::Serverless::Function
    Properties:
      Handler: main
      CodeUri: ./bin/subscriptions/create.zip
      Runtime: go1.x
      Timeout: 30
      Environment:
        Variables:
          DATABASE_USER:
          DATABASE_PASSWORD:
          DATABASE_HOST:
          DATABASE_NAME:
          AUTHENTICATION_TOKEN:
      Events:
        GetRates:
          Type: Api
          Properties:
            Path: /subscriptions
            Method: post
  SubscriptionsDelete:
    Type: AWS::Serverless::Function
    Properties:
      Handler: main
      CodeUri: ./bin/subscriptions/delete.zip
      Runtime: go1.x
      Timeout: 30
      Environment:
        Variables:
          DATABASE_USER:
          DATABASE_PASSWORD:
          DATABASE_HOST:
          DATABASE_NAME:
          AUTHENTICATION_TOKEN:
      Events:
        GetRates:
          Type: Api
          Properties:
            Path: /subscriptions/{id}
            Method: delete

Переменные среды

Вы заметите, что переменные среды в файле конфигурации пусты - это сделано намеренно, так как вы захотите зафиксировать файл шаблона в системе управления версиями. К счастью, мы можем динамически загружать переменные среды из файла env.json при запуске приложения SAM. Файл env.json можно добавить в .gitignore.

Создайте новый файл env.json и добавьте следующий код:

{
  "EventsCreate": {
    "AUTHENTICATION_TOKEN": "lemme in",
    "DATABASE_USER": "mydatabaseuser",
    "DATABASE_PASSWORD": "mydatabasepassword",
    "DATABASE_HOST": "mydatabasehouse",
    "DATABASE_NAME": "mydatabasename"
  },
  "SubscriptionsCreate": {
    "AUTHENTICATION_TOKEN": "lemme in",
    "DATABASE_USER": "mydatabaseuser",
    "DATABASE_PASSWORD": "mydatabasepassword",
    "DATABASE_HOST": "mydatabasehouse",
    "DATABASE_NAME": "mydatabasename"
  },
  "SubscriptionsDelete": {
    "AUTHENTICATION_TOKEN": "lemme in",
    "DATABASE_USER": "mydatabaseuser",
    "DATABASE_PASSWORD": "mydatabasepassword",
    "DATABASE_HOST": "mydatabasehouse",
    "DATABASE_NAME": "mydatabasename"
  }
}

Измените все переменные подключения к базе данных, чтобы они соответствовали вашему экземпляру RDS.

Makefile

Прежде чем мы сможем запустить SAM, нам нужно будет собрать и заархивировать наши приложения для конечных точек. Самый простой способ сделать это - использовать Makefile, поэтому мы можем просто запустить make в любое время, когда будет внесено изменение. Большинство IDE также поддерживают выполнение файла, такого как make-файл, при каждом сохранении файла.

Создайте новый файл Makefile и добавьте следующий код:

build:
	dep ensure
	env GOOS=linux go build -ldflags="-s -w" -o main endpoints/events/create/main.go
	mkdir -p bin/events
	zip bin/events/create.zip main
	mv main bin/events/create
	env GOOS=linux go build -ldflags="-s -w" -o main endpoints/subscriptions/create/main.go
	mkdir -p bin/subscriptions
	zip bin/subscriptions/create.zip main
	mv main bin/subscriptions/create
	env GOOS=linux go build -ldflags="-s -w" -o main endpoints/subscriptions/delete/main.go
	mkdir -p bin/subscriptions
	zip bin/subscriptions/delete.zip main
	mv main bin/subscriptions/delete

Давайте разберем эти команды.

dep ensure запустит диспетчер пакетов Go, чтобы загрузить все отсутствующие пакеты, которые мы используем. Зависимости будут сохранены в каталоге vendor. dep похож на bundler для тех, кто знаком с Ruby, и hex для тех, кто работал с Elixir.

После dep ensure есть 3 похожих блока - по одному для каждого из наших конечных приложений. Каждый из этих блоков будет:

  • Скомпилируйте исходный код в файле main.go и сохраните его в корневом каталоге проекта. В go build передается несколько параметров, чтобы обеспечить выполнение файла в среде Linux, которую будут использовать как SAM, так и Lambda. Бинарный файл выводится в корневой проект как «основной».
  • Затем создается каталог, который мы будем использовать для сопоставления двоичных файлов с URL-адресами.
  • Затем мы заархивируем двоичный файл конечной точки и сохраним его во вновь созданном каталоге. Этот zip-файл используется SAM.
  • Наконец, «основной» двоичный файл перемещается в тот же каталог и переименовывается. Бинарный файл используется Serverless и развертывается в AWS.

Запуск SAM

Наконец-то мы наконец-то можем создать наше бессерверное приложение и запустить его локально!

Запустите make && sam local start-api --env-vars env.json, чтобы создать двоичные файлы конечных точек и запустить службу локального шлюза API SAM.

Когда SAM запустится, вы увидите, что все 3 наши конечные точки были смонтированы и доступны через localhost: 3000. Используя cURL, мы можем попасть в эти конечные точки и проверить их ответы:

curl -X POST \
  http://localhost:3000/subscriptions \
  -H 'Authorization: lemme in' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \  -d '{
    "event_type": "new_user",
    "endpoint": "https://gewgle.com"
}'
{"ID":12,"CreatedAt":"2018-06-19T03:28:43.819217562Z","UpdatedAt":"2018-06-19T03:28:43.819217562Z","DeletedAt":null,"event_type":"new_user","endpoint":"https://gewgle.com"}
curl -X POST \
  http://localhost:3000/events \
  -H 'Authorization: lemme in' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{
    "event_type": "new_user",
    "payload": "[email protected]"
}'
{"ID":22,"CreatedAt":"2018-06-19T03:20:20.530358392Z","UpdatedAt":"2018-06-19T03:20:20.530358392Z","DeletedAt":null,"event_type":"new_user","payload":"[email protected]"}
curl -X DELETE \
  http://localhost:3000/subscriptions/11 \
  -H 'Authorization: lemme in' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json'
{"ID":12,"CreatedAt":"2018-06-19T03:28:43.819218Z","UpdatedAt":"2018-06-19T03:28:43.819218Z","DeletedAt":null,"event_type":"new_user","endpoint":"https://gewgle.com"}

Развертывание

Подобно SAM, Serverless - сервис, который мы будем использовать для автоматизации развертывания, - использует файл конфигурации для сопоставления двоичных файлов с URL-адресами.

Чтобы избежать наличия переменных среды в нашем файле конфигурации, мы сначала установим наши переменные в AWS с помощью Parameter Store. Используйте следующие команды, чтобы установить переменные среды:

aws ssm put-parameter --name authentication --type String --value "lemme in"
aws ssm put-parameter --name database_user --type String --value mydatabaseuser
aws ssm put-parameter --name database_password --type String --value mydatabasepassword
aws ssm put-parameter --name database_host --type String --value mydatabasehost
aws ssm put-parameter --name database_name --type String --value mydatabasename

Теперь, когда у нас загружены переменные env, мы можем собрать бессерверный файл конфигурации.

Создайте новый файл serverless.yml и добавьте следующий код:

service: serverless-eventbus

provider:
  name: aws
  runtime: go1.x

package:
 exclude:
   - ./**
 include:
   - ./bin/**

functions:
  EventsCreate:
    handler: bin/events/create
    events:
      - http:
          path: /events
          method: post
    environment:
      AUTHENTICATION_TOKEN: ${ssm:authentication}
      DATABASE_USER: ${ssm:database_user}
      DATABASE_PASSWORD: ${ssm:database_password}
      DATABASE_HOST: ${ssm:database_host}
      DATABASE_NAME: ${ssm:database_name}
  SubscriptionsCreate:
    handler: bin/subscriptions/create
    events:
      - http:
          path: /subscriptions
          method: post
    environment:
      AUTHENTICATION_TOKEN: ${ssm:authentication}
      DATABASE_USER: ${ssm:database_user}
      DATABASE_PASSWORD: ${ssm:database_password}
      DATABASE_HOST: ${ssm:database_host}
      DATABASE_NAME: ${ssm:database_name}
  SubscriptionsDelete:
    handler: bin/subscriptions/delete
    events:
      - http:
          path: /subscriptions/{id}
          method: delete
    environment:
      AUTHENTICATION_TOKEN: ${ssm:authentication}
      DATABASE_USER: ${ssm:database_user}
      DATABASE_PASSWORD: ${ssm:database_password}
      DATABASE_HOST: ${ssm:database_host}
      DATABASE_NAME: ${ssm:database_name}

Установив переменные среды и настроив бессерверный файл конфигурации, мы можем продолжить развертывание.

Введите serverless deploy, и вы должны получить следующий результат

Плавник

Готово - полноценное бессерверное веб-приложение. Я надеюсь, что это руководство помогло связать для вас бессерверное пространство API. Если у вас есть вопросы или комментарии, оставьте сообщение ниже!

Вам нравится писать отличный код на Ruby или Elixir? Пойдем со мной поработать в TaxJar.