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!"}