Перейти к основному содержимому

API Gateway

Это пример простой функции на TypeScript, которая принимает в теле запроса параметр name и возвращает json вида {"message": "Hello, Username!"}

Архитектура примера

архитектурная диаграмма

Функция развернута в Yandex Cloud и доступна через API Gateway.

Код

import {Http} from '@yandex-cloud/function-types/dist/src/http';

// RequestBody interface represents the structure of the request body.
interface RequestBody {
name: string; // The name property of the request body.
}

// The handler function is an asynchronous function that handles HTTP events.
// It takes a Http.Event object as a parameter and returns a Promise that resolves to a Http.Result object.
export async function handler(event: Http.Event): Promise<Http.Result> {

// Parse the body of the event into a RequestBody object.
const body = JSON.parse(event.body || '{}') as RequestBody;

// Create a response object with a message property.
const response = {
message: `Hello, ${body.name || 'World'}!`
}

// Return a Http.Result object with a status code of 200, a 'Content-Type' header set to 'application/json',
// and the body set to the JSON stringified version of the response object.
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(response)
};

}

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

Для развертывания этой функции в Yandex Cloud вы можете воспользоваться Terraform описанным в примере.

Function

Сперва нам нужно скомпилировать функцию в JavaScript. Для этого мы можем использлвать null_resource и local-exec провиженер.

resource "null_resource" "build_typescript" {
provisioner "local-exec" {
command = "cd ../function && npm run build"
}
triggers = {
always_run = timestamp()
}
}

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

Для этого мы сначала определяем ресурс archive_file для создания zip-архива с функцией.

data "archive_file" "function_files" {
output_path = "./function.zip"
source_dir = "../dist"
type = "zip"
depends_on = [
null_resource.build_typescript
]
}

А затем ресурс yandex_function для создания функции в Yandex Cloud, куда передаем zip-архив с функцией.

resource "yandex_function" "test_function" {
name = "api-gateway-demo"
user_hash = data.archive_file.function_files.output_sha256
runtime = "nodejs18"
entrypoint = "main.handler"
memory = "128"
execution_timeout = "10"
service_account_id = yandex_iam_service_account.sa_serverless.id
content {
zip_filename = data.archive_file.function_files.output_path
}
}

IAM

Для того чтобы API Gateway могла вызывать функцию, нам нужно создать Service Account и выдать ему права на вызов функции. Для этого используем ресурс yandex_iam_service_account и yandex_resourcemanager_folder_iam_binding. Подробнее о ресурсах можно прочитать в документации.

resource "yandex_iam_service_account" "sa_serverless" {
name = "sa-serverless-test"
folder_id = var.folder_id
}

resource "yandex_resourcemanager_folder_iam_binding" "sa_serverless" {
for_each = toset([
"serverless.functions.invoker",
])
role = each.value
folder_id = var.folder_id
members = [
"serviceAccount:${yandex_iam_service_account.sa_serverless.id}",
]
}

API Gateway

Теперь посмотрим на ресурс yandex_serverless_api_gateway для создания API Gateway. Для того чтобы сформировать его содержимое мы используем функцию templatefile, которая осуществляет подстановку значений в шаблон. Подробнее о функции templatefile можно прочитать в документации.

Вот так выглядит шаблон API Gateway спецификации в формате OpenAPI:

openapi: "3.0.0"
info:
version: 1.0.0
title: Test API
paths:
/demo:
# HTTP method
# YC API Gateway supports GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE and additionally
# extension that allows to capture any HTTP method: x-yc-apigateway-any-method
# Additional info: https://cloud.yandex.ru/docs/api-gateway/concepts/extensions/any-method
post:
operationId: demo
# OpenAPI specification extensions
# `x-yc-apigateway-integration` is used to specify the function to be called
# Additional info: https://cloud.yandex.ru/docs/api-gateway/concepts/extensions/cloud-functions
x-yc-apigateway-integration:
type: cloud_functions
function_id: ${test_function_id}
service_account_id: ${sa_id}
payload_format_version: "1.0"
# `x-yc-apigateway-validator` is used to specify the request and response validation
# Additional info: https://cloud.yandex.ru/docs/api-gateway/concepts/extensions/validator
x-yc-apigateway-validator:
validateRequestBody: true
validateResponseBody: true
# Request body schema
# Docs: https://spec.openapis.org/oas/latest.html#request-body-object
requestBody:
required: true
content:
application/json:
schema:
type: object
required: ["name"]
properties:
name:
type: string
additionalProperties: false
# Response body schema
# Docs: https://spec.openapis.org/oas/latest.html#responses-object
responses:
'200':
content:
application/json:
schema:
type: object
properties:
message:
type: string
description: Success

Вместо ${test_function_id} и ${sa_id} подставляются значения из ресурсов yandex_function.test_function.id и yandex_iam_service_account.sa_serverless.id.

resource "yandex_api_gateway" "api_gateway" {
name = "api-gateway"
spec = templatefile("./api-gateway.yaml", {
test_function_id = yandex_function.test_function.id
sa_id = yandex_iam_service_account.sa_serverless.id
})
}

Запуск

Теперь, когда функция развернута, мы можем вызвать ее с помощью curl:

API_GATEWAY_ENDPOINT=$(terraform output -raw api_gateway_endpoint)
curl -XPOST \
"https://$API_GATEWAY_ENDPOINT/demo" \
-d '{"name": "test"}' \
-H "Content-Type: application/json"

В ответ мы получим json:

{"message":"Hello, test!"}