Рубрики
Uncategorized

Как создать API без сервера с помощью базы данных с использованием AWS CDK

Serverless дает нам огромные возможности для быстрее отправлять новые цифровые продукты, бесплатно в … Tagged с помощью сервера, AWS, API, DevOps.

API без сервера с CDK (2 серии деталей)

Serverless дает нам огромные возможности для того, чтобы в начале быстрее отправлять новые цифровые продукты. Наиболее классический способ использования Server Bless — это API, поддерживаемый AWS Lambdas. Этот блог будет направлять предоставление и развертывание приложения с использованием версии AWS CDK версии 2. Однако, когда я начал его реализовать, начали появляться проблемы. Я бы сказал о них, изображая процесс и код. Serverless даст возможность бесплатно иметь полную настройку API.

Прежде всего, что мы строим? Это будет API -шлюз, поддерживаемый AWS Lambdas. Они будут читать и записывать элементы в таблице DynamoDB. Итак, вот мы, создав Crud с API REST. Можно найти репозиторий GitHub Здесь Анкет

API Gateway -> AWS Lambda -> DynamoDB

Чтение отлично подходит для многих вещей. Так что наши предметы будут книги! Модель будет такой:

{
   "title": "name of the book>",
   "author": "",
   "yearPublished": "",
   "isbn": ""
}

Реализация

В этом демонстрационном приложении мы используем Dynamo DB. Это база данных NOSQL, где AWS управляет инфраструктурой. Он работает как волшебное заклинание с функциями Lambda. Это приложение будет выполнять классические операции CRUD.

import { DocumentClient } from 'aws-sdk/clients/dynamodb';
import { Book } from '../models/book';
import uuid from 'uuid';

const dynamo = new DocumentClient();

export async function create(table: string, book: Book) {
  const params = {
    TableName: table,
    Item: {
      id: uuid.v4(),
      ...book
    }
  }
  const dbResponse = await dynamo.put(params).promise();
  return params.Item;
}

Старт будет связан с написанными элементами в таблице. TypeScript позволяет иметь сильно напечатанный объект. Единственное, что будет здорово иметь максимально уникальную, это UUID. В этом случае получение книги от идентификатора будет простым. Операция удаления будет очень похожа.

export async function get(table: string, id: string) {
  const params = {
    TableName: table,
    Key: {
      id
    }
  };

  const dbResponse = await dynamo.get(params).promise();
  return dbResponse.Item;
}

export async function deleteItem(table: string, id: string) {
  const params = {
    TableName: table,
    Key: {
      id
    }
  }
  await dynamo.delete(params).promise();
}

Было бы здорово проверить созданные элементы. Здесь я использую операцию «сканирования», которая проходит через всю таблицу. Для демонстрационных целей этого должно быть достаточно. Когда это возможно, нужно использовать «запрос».

export async function list(table: string): Promise {
  const params = {
    TableName: table,
  }
  const dbResponse = await dynamo.scan(params).promise();
  if (dbResponse.Items) {
    return dbResponse.Items;
  }
  throw new Error('Cannot get all books');
}

Самый сложный запрос — обновить книгу из базы данных. Он должен иметь параметры и значения для редактирования.

export async function update(table: string, id: string, book: Book) {
  const params = {
    TableName: table,
    Key: {
      id,
    },
    ExpressionAttributeValues: {
      ':title': book.title,
      ':author': book.author,
      ':yearPublished': book.yearPublished,
      ':isbn': book.isbn
    },
    UpdateExpression: 'SET title = :title, ' +
        'author = :author, yearPublished = :yearPublished, isbn = :isbn',
    ReturnValues: 'UPDATED_NEW',
  }
  await dynamo.update(params).promise();
}

Инъекция зависимости — это первое осложнение, которое происходит с Lambdas. Если кто -то упаковывает его в AWS с помощью CDK или CloudFormation, не будет никаких зависимостей. Я использую TypeScript в проекте, поэтому, теоретически, запуск создаст рабочий файл JS:

npm i -D typescript
tsc init
tsc

Однако это не так просто. Здесь есть две проблемы:

  • Упаковка дополнительных файлов с функциональностью, потому что наличие Lambda с реализацией внутри одного файла — плохая практика
  • Упаковка внешних зависимостей. В нашем случае это будет Uuid.

Я попытался упаковать только SRC/функции/создать. Бег Lambda даст это:

Вот почему следующим логическим шагом будет выяснить, как упаковать код Lambda вместе с зависимостями и не загружать 100 МБ node_modules. Я решил использовать WebPack для этого. Он правильно делает задание и позволит запустить только эту команду после установки: WebPack

Функции основного обработчика Lambdas будут так же просты, как вызов файла DynamoDB с необходимыми операциями. Например, создание элемента было бы таким.

import { APIGatewayProxyEventV2, Callback, Context } from 'aws-lambda';
import { create } from '../connectors/dynamo-db-connector';

export async function handler (event: APIGatewayProxyEventV2, context: Context, callback: Callback) {
  if (typeof event.body === 'string') {
    const bookItem = JSON.parse(event.body);
    const createBook = await create(process.env.table as string, bookItem);
    const response = {
      statusCode: 201,
    }
    callback(null, response);
  }
  callback(null, {
    statusCode: 400,
    body: JSON.stringify('Request body is empty!')
  });
}

Можно найти обработчиков для других операций в Репозиторий Анкет

Инфраструктура

Как упоминалось ранее, CDK будет отвечать за обеспечение инфраструктуры. Я буду использовать только один стек с DynamoDB, 5 Lambdas, REST API, разрешениями для подключений к таблице и интеграциями для конечных точек. Мы поместим приложение инфраструктуры в отдельную папку/инфра.

Чтобы создать DynamoDB с помощью Lambda, который его называют, понадобятся готовые типичные конструкции для DynamoDB, Lambda и разрешений. Код расположен на одном уровне выше, поэтому важно выразить его так. Хорошая вещь здесь в том, что CDK ошибся, когда кто -то называет «синтезирование» шаблона с неправильными параметрами. После этого Lambda понадобится разрешение на размещение элемента в DynamoDB. Можно сделать это, добавив одну строку кода.

const dynamoTable = new ddb.Table(this, 'BookTable', {
  tableName: 'BookStorage',
  readCapacity: 1,
  writeCapacity: 1,
  partitionKey: {
    name: 'id',
    type: ddb.AttributeType.STRING,
  },
})

const createBookFunction = new lambda.Function(this, 'CreateHandler', {
  runtime: lambda.Runtime.NODEJS_14_X,
  code: lambda.Code.fromAsset('../code'),
  handler: 'create.handler',
  environment: {
    table: dynamoTable.tableName
  },
  logRetention: RetentionDays.ONE_WEEK
});

dynamoTable.grant(createBookFunction, 'dynamodb:PutItem')

После этого функция может быть прикреплена к API. Во -первых, мы инициализируем Restapi. Затем мы добавляем ресурс, чтобы учесть, что у него есть путь, например, post/.

const api = new apigw.RestApi(this, `BookAPI`, {
  restApiName: `book-rest-api`,
});

const mainPath = api.root.addResource('books');

const createBookIntegration = new apigw.LambdaIntegration(createBookFunction);

mainPath.addMethod('POST', createBookIntegration);

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

Тестирование конечных точек

После манипуляций, упомянутых в предыдущих разделах, мы можем попробовать API, упомянутый в этой статье. Давайте начнем со списка всех книг. Результат должен быть пустым.

curl --location --request GET 'https://.execute-api..amazonaws.com/prod/books'
RESPONSE:
Status: 200
[]

После этого давайте создадим пару книг.

curl --location --request POST 'https://.execute-api..amazonaws.com/prod/books' \
--header 'Content-Type: application/json' \
--data-raw '{
"title": "Life of PI",
"author": "Yann Martel",
"yearPublished": "2000",
"isbn": "0-676-97376-0"
}'

RESPONSE:
Status: 201

А также еще один:

curl --location --request POST 'https://.execute-api..amazonaws.com/prod/books' \
--header 'Content-Type: application/json' \
--data-raw '{
"title": "Simulacra and Simulation",
"author": "Jean Baudrillard",
"yearPublished": "0-472-06521-1",
"isbn": "0-676-97376-0"
}'

RESPONSE:
Status: 201

Теперь мы можем позвонить в список, чтобы убедиться, что все книги находятся на их месте.

curl --location --request GET 'https://.execute-api..amazonaws.com/prod/books'

RESPONSE:
Status: 200
[{
   "isbn": "0-676-97376-0",
   "id": "617d6b3e-ce6d-4e8d-a10f-05d6703ad7ac",
   "yearPublished": "2000",
   "title": "Life of PI",
   "author": "Yann Martel"
}, {
   "isbn": "0-676-97376-0",
   "id": "2a8251ee-73ee-4717-8f6f-0f11dd2b861f",
   "yearPublished": "0-472-06521-1",
   "title": "Simulacra and Simulation",
   "author": "Jean Baudrillard"
}]

Lambda добавил книги, и список также работает. Однако в базе данных есть ошибка. Я заметил, что «Жизнь Пи» была опубликована в 2001 году, а не в 2000 году. Итак, нам нужно позвонить в конечную точку обновления.

curl --location --request PUT 'https://y55xcv8jmc.execute-api.eu-west-1.amazonaws.com/prod/books/617d6b3e-ce6d-4e8d-a10f-05d6703ad7ac' \
--header 'Content-Type: application/json' \
--data-raw '{
  "isbn": "0-676-97376-0",
  "yearPublished": "2001",
  "title": "Life of PI",
  "author": "Yann Martel"
}'

Status: 200

Еще один звонок в список покажет, что книга была успешно обновлена.

[{
  "isbn": "0-676-97376-0",
  "id": "617d6b3e-ce6d-4e8d-a10f-05d6703ad7ac",
  "yearPublished": "2001",
  "author": "Yann Martel",
  "title": "Life of PI"
}, {
  "isbn": "0-676-97376-0",
  "id": "2a8251ee-73ee-4717-8f6f-0f11dd2b861f",
  "yearPublished": "0-472-06521-1",
  "title": "Simulacra and Simulation",
  "author": "Jean Baudrillard"
}]

Давайте удалим одну из книг, позвонив в конечную точку Delete.

curl --location --request DELETE 'https://.execute-api..amazonaws.com/prod/books/2a8251ee-73ee-4717-8f6f-0f11dd2b861f'
Status: 200

После вызова его несколько раз с разными идентификаторами, можно получить пустой ответ для списка книг. Это то, что мы ожидаем здесь.

В этой статье я показал, как развернуть API без сервера с базой данных NOSQL с использованием AWS Lambda, DynamoDB и API Gateway, развернутой CDK. У Lambdas есть все разрешения на операции CRUD. Это может быть образцом проекта для более сложной настройки. Кроме того, я использовал WebPack, чтобы иметь все зависимости узел. Будущая работа будет включать в себя Codepipeline для CI/CD, подключенного к крючке GitHub. Спасибо, что прочитали эту статью!

Хотите узнать больше о AWS, Serverless и CDK? Подпишитесь на мой блог, где я регулярно публикую по этим темам.

API без сервера с CDK (2 серии деталей)

Оригинал: «https://dev.to/grenguar/how-to-build-serverless-api-with-database-using-aws-cdk-4i2d»