Настройка AWS SAM для Serverless-бэкенда

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка AWS SAM для Serverless-бэкенда
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Настройка AWS SAM для Serverless-бэкенда

AWS SAM (Serverless Application Model) — это официальный AWS-инструмент для определения serverless-ресурсов. Он расширяет CloudFormation упрощённым синтаксисом: вместо 50 строк CloudFormation для Lambda + API Gateway + IAM Role пишется 10 строк SAM. На выходе — полноценный CloudFormation стек, который версионируется и откатывается.

В отличие от Serverless Framework, SAM — это AWS-нативный инструмент без абстракции над другими провайдерами. Это его преимущество: полная совместимость с CloudFormation, нативная интеграция с AWS CDK, прямая работа с SAM CLI для локального тестирования с реальными service mocks.

Установка и первый проект

# macOS
brew tap aws/tap
brew install aws-sam-cli

# Linux
pip install aws-sam-cli

sam --version  # SAM CLI, version 1.x

# Инициализация проекта
sam init --runtime nodejs20.x --dependency-manager npm --app-template hello-world --name my-backend

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

my-backend/
├── template.yaml           # SAM-шаблон (расширенный CloudFormation)
├── samconfig.toml          # конфиг деплоя (регион, стек, S3 bucket)
├── src/
│   ├── handlers/
│   │   ├── api.ts
│   │   ├── auth.ts
│   │   └── worker.ts
│   └── shared/
│       ├── db.ts
│       └── response.ts
├── events/                 # тестовые события для sam local invoke
│   ├── api-get.json
│   └── api-post.json
└── __tests__/
    └── unit/

template.yaml — полная конфигурация

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: My Web Backend

Globals:
  Function:
    Runtime: nodejs20.x
    Architectures: [arm64]  # Graviton2 — до 20% дешевле x86
    MemorySize: 512
    Timeout: 10
    Environment:
      Variables:
        NODE_ENV: !Ref Stage
        TABLE_NAME: !Ref MainTable
        ALLOWED_ORIGIN: !Ref AllowedOrigin
    Layers:
      - !Ref DepsLayer
  Api:
    Cors:
      AllowOrigin: !Sub "'${AllowedOrigin}'"
      AllowHeaders: "'Content-Type,Authorization,X-Request-Id'"
      AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"

Parameters:
  Stage:
    Type: String
    Default: dev
    AllowedValues: [dev, staging, prod]
  AllowedOrigin:
    Type: String
    Default: 'http://localhost:3000'
  JwtSecret:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /my-backend/jwt-secret

Resources:
  # HTTP API (v2) — дешевле REST API ~71%
  ApiGateway:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: !Ref Stage
      Auth:
        DefaultAuthorizer: LambdaAuthorizer
        Authorizers:
          LambdaAuthorizer:
            AuthorizerPayloadFormatVersion: '2.0'
            FunctionArn: !GetAtt AuthFunction.Arn
            Identity:
              Headers: [Authorization]
            EnableSimpleResponses: true

  # Основная Lambda
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/api.handler
      Events:
        AnyRoute:
          Type: HttpApi
          Properties:
            ApiId: !Ref ApiGateway
            Method: ANY
            Path: /api/{proxy+}
            Auth:
              Authorizer: LambdaAuthorizer
        HealthCheck:
          Type: HttpApi
          Properties:
            ApiId: !Ref ApiGateway
            Method: GET
            Path: /health
            Auth:
              Authorizer: NONE
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref MainTable
        - S3ReadPolicy:
            BucketName: !Ref AssetsBucket
      Environment:
        Variables:
          JWT_SECRET: !Ref JwtSecret
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: es2022
        Sourcemap: true
        EntryPoints: [src/handlers/api.ts]
        External: ['@aws-sdk/*']

  # Authorizer Lambda
  AuthFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/auth.handler
      Policies:
        - SSMParameterReadPolicy:
            ParameterName: my-backend/jwt-secret
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: es2022
        EntryPoints: [src/handlers/auth.ts]

  # Фоновый воркер — SQS триггер
  WorkerFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/worker.handler
      MemorySize: 256
      Timeout: 300
      Events:
        SQSEvent:
          Type: SQS
          Properties:
            Queue: !GetAtt WorkerQueue.Arn
            BatchSize: 10
            FunctionResponseTypes: [ReportBatchItemFailures]
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref MainTable
        - SQSSendMessagePolicy:
            QueueName: !GetAtt WorkerQueue.QueueName
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        EntryPoints: [src/handlers/worker.ts]

  # Lambda Layer для общих зависимостей
  DepsLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: !Sub '${AWS::StackName}-deps'
      ContentUri: layer/
      CompatibleRuntimes: [nodejs20.x]
      CompatibleArchitectures: [arm64]
    Metadata:
      BuildMethod: nodejs20.x

  # DynamoDB
  MainTable:
    Type: AWS::DynamoDB::Table
    DeletionPolicy: Retain  # не удалять при sam delete
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - { AttributeName: pk, AttributeType: S }
        - { AttributeName: sk, AttributeType: S }
        - { AttributeName: gsi1pk, AttributeType: S }
      KeySchema:
        - { AttributeName: pk, KeyType: HASH }
        - { AttributeName: sk, KeyType: RANGE }
      GlobalSecondaryIndexes:
        - IndexName: gsi1
          KeySchema:
            - { AttributeName: gsi1pk, KeyType: HASH }
            - { AttributeName: sk, KeyType: RANGE }
          Projection: { ProjectionType: ALL }
      TimeToLiveSpecification:
        { AttributeName: ttl, Enabled: true }

  # SQS Queue
  WorkerQueue:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 360  # > Lambda timeout
      RedrivePolicy:
        deadLetterTargetArn: !GetAtt DLQ.Arn
        maxReceiveCount: 3

  DLQ:
    Type: AWS::SQS::Queue
    Properties:
      MessageRetentionPeriod: 1209600  # 14 дней

  # S3
  AssetsBucket:
    Type: AWS::S3::Bucket
    Properties:
      CorsConfiguration:
        CorsRules:
          - AllowedOrigins: [!Ref AllowedOrigin]
            AllowedMethods: [GET, PUT]
            AllowedHeaders: ['*']
            MaxAge: 3600

Outputs:
  ApiUrl:
    Value: !Sub 'https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/${Stage}'
  TableName:
    Value: !Ref MainTable
  BucketName:
    Value: !Ref AssetsBucket

Handler и authorizer

// src/handlers/auth.ts — Lambda Authorizer (simple response format)
import type { APIGatewayRequestSimpleAuthorizerHandlerV2 } from 'aws-lambda';
import { verify } from 'jsonwebtoken';

export const handler: APIGatewayRequestSimpleAuthorizerHandlerV2 = async (event) => {
  const token = event.headers?.authorization?.replace('Bearer ', '');

  if (!token) return { isAuthorized: false };

  try {
    const payload = verify(token, process.env.JWT_SECRET!) as Record<string, unknown>;
    return {
      isAuthorized: true,
      context: {
        userId: String(payload.sub),
        role: String(payload.role ?? 'user'),
      },
    };
  } catch {
    return { isAuthorized: false };
  }
};

// src/shared/response.ts
export const ok = (data: unknown, statusCode = 200) => ({
  statusCode,
  body: JSON.stringify(data),
  headers: { 'Content-Type': 'application/json' },
});

export const err = (message: string, statusCode = 400) => ({
  statusCode,
  body: JSON.stringify({ error: message }),
  headers: { 'Content-Type': 'application/json' },
});

samconfig.toml — конфиг деплоя

version = 0.1

[default.deploy.parameters]
stack_name = "my-backend-dev"
s3_bucket = "my-backend-sam-artifacts-eu-west-1"
s3_prefix = "my-backend"
region = "eu-west-1"
confirm_changeset = false
capabilities = "CAPABILITY_IAM"
parameter_overrides = "Stage=dev AllowedOrigin=http://localhost:3000"

[prod.deploy.parameters]
stack_name = "my-backend-prod"
s3_bucket = "my-backend-sam-artifacts-eu-west-1"
region = "eu-west-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
parameter_overrides = "Stage=prod AllowedOrigin=https://my-site.com"

Локальная разработка

# Сборка
sam build

# Локальный API с моком DynamoDB (через Docker)
sam local start-api --env-vars env.json --port 3001

# env.json для локального запуска
# {
#   "ApiFunction": {
#     "TABLE_NAME": "local-table",
#     "JWT_SECRET": "dev-secret"
#   }
# }

# Вызов конкретной функции
sam local invoke ApiFunction --event events/api-get.json

# Логи в реальном времени
sam logs --stack-name my-backend-dev --tail

Деплой

# Первый деплой с guided setup
sam deploy --guided

# Последующие — по конфигу
sam build && sam deploy

# Prod
sam build && sam deploy --config-env prod

# Откат
aws cloudformation rollback-stack --stack-name my-backend-prod

Сроки

Базовый SAM-бэкенд с одним Lambda + HTTP API + DynamoDB — 1–2 дня. Полноценная структура с authorizer, SQS worker, S3, SSM-секретами и samconfig для нескольких окружений — 4–5 дней. Добавление к существующему проекту (если уже есть логика в Express или Fastify) — 3–5 дней на адаптацию под Lambda handler.