W jaki sposób zapisać i odczytać dane z DynamoDB bezpośrednio przy pomocy API Gateway

W jaki sposób zapisać i odczytać dane z DynamoDB bezpośrednio przy pomocy API Gateway

Artykuł ukazał się pierwotnie na blogu Chmurowiska.

Cały świat mówi i pisze o serverless. Wiele osób kojarzy ten termin z takimi usługami jak AWS Lambda lub Azure Functions. A serverless to nie tylko usługi FaaS. To „sposób myślenia” i masa innych usług, z których możemy korzystać w chmurach bez konieczności konfigurowania, zarządzania i dbania o infrastrukturę, która jest pod spodem. W tym artykule pokażę w jaki sposób zapisać i odczytać dane z DynamoDB bezpośrednio przy pomocy API Gateway. Bez pośrednictwa funkcji Lambda. API Gateway może jednak służyć jako brama dla wielu innych usług w AWS.

Scenariusz

Tworzymy jakąś aplikację i potrzebujemy miejsce, w którym możemy zapisać jakieś informacje na temat jej działania. Mogą to być zarówno jakieś wyjątki z naszej aplikacji, jak i na przykład komentarze od naszych użytkowników. Wysyłając dane do AWS skorzystamy z usługi API Gateway, która może działać miedzy innymi jako proxy do innych zasobów w AWS. Do wysyłki danych użyjemy API REST, a metoda, która dostarczy dane do DynamoDB będzie wyglądała następująco:

Resource: /issue
HTTP Method: POST
HTTP Request Body:
{
  "applicationId":   "applicationId",
  "userName": "Przemek",
  "issue":  "Example exception."
}

W dalszej części stworzymy także metodę GET, która zwróci do klienta dane zapisane w DynamoDB:

Resource: /issue/{applicationId}
HTTP Method: GET

DynamoDB

W pierwszym kroku utworzymy w usłudze DynamoDB tabelę, w której będziemy przechowywali dane. Nazwa tabeli nie jest istotna. Ja utworzyłem tabelę o nazwie appissues. Jako Primary key zastosujemy, exceptionid, którego typ to String. Resztę parametrów możecie zostawić bez zmian.

Będziemy potrzebowali możliwości pobrania danych dla konkretnej aplikacji, utworzymy więc index dla applicationid. Po utworzeniu przez AWS naszej tabeli przechodzimy więc do zakładki Indexes i tworzymy nowy indeks.

Resztę także możemy pozostawić bez zmian.

API Gateway

W kolejnym kroku przygotujemy już nasze endpointy w usłudze API Gateway. Zanim jednak zaczniemy, musimy utworzyć rolę, która umożliwi sięganie do zapisów w tabeli DynamoDB. Dla uproszczenia możecie utworzyć rolę z podpiętą polityką AmazonDynamoDBFullAccess. Warto dodać także politykę AmazonAPIGatewayPushToCloudWatchLogs. Umożliwi ona zapis logów do usługi CloudWatch.

Po utworzeniu roli zapiszcie sobie jej ARN, przyda się za chwilę.

OK, możemy już przejść do samej usługi API Gateway i utworzyć nowe API. Wystarczy API typu Regional.

Tworzymy w naszym API nowe Resource i nazywamy je issue.

POST

Możemy już przygotować metodę do wysyłania danych do naszej tabeli w DynamoDB. W tym celu tworzymy w naszym resource metodę POST. Musimy ją także skonfigurować.

  1. Wybieramy AWS Service jako Integration type.
  2. Jako region wybieramy ten, w którym mamy naszą tabelę DynamoDB.
  3. AWS Service to oczywiście DynamoDB.
  4. HTTP Method ustawiamy na POST.
  5. W pole Action wpisujemy PutItem, czyli metodę, która będzie zapisywała na naszej tabeli dane.
  6. Execution role to ARN roli, którą utworzyliśmy wcześniej.

Resztę ponownie pozostawiamy bez zmian i klikamy Save.

Mamy już naszą metodę. Musimy jednak jakoś zmienić format danych otrzymywanych od użytkownika, czyli coś takiego:

{
  "applicationId":   "applicationId",
  "userName": "Przemek",
  "issue":  "Example exception."
}

na format, który zrozumie DynamoDB.

W tym celu użyjemy tak zwanego mapowania. Klikamy w Integration request i rozwijamy sekcję Mapping templates. Dodajemy nowe Mapping template. Jako Content-Type wpisujemy application/json.

Poniżej, w polu edycyjnym wklejamy kawałek kodu napisanego w VTL. Nie zapomnijcie oczywiście po wpisaniu nazwy swojej tabeli DynamoDB.

{ 
    "TableName": "<YOUR TABLE NAME>",
    "Item": {
        "exception_id": {
                "S": "$context.requestId"
               },
         "application_id": {
                "S": "$input.path('$.applicationId')"
                },
         "user_name": {
                "S": "$input.path('$.userName')"
            },
         "issue": {
                "S": "$input.path('$.issue')"
            }
    }
}

Jako exceptionid, czyli nasz Primary Key wykorzystamy requestId.

Więcej o tym jak mapować dane w API Gateway możecie przeczytać tutaj.

Możemy teraz przetestować naszą metodę POST i sprawdzić czy przesłane dane zapiszą się w DynamoDB. W menu Actions wybieramy Deploy API, tworzymy nowy stage i czas na wykonanie pierwszego requestu. Przykładowe wywołanie curl poniżej.

curl -X POST https://<URL OF YOUR API>/<STAGE>/issue -H 'Content-Type: application/json' -H 'cache-control: no-cache' -d '{ "applicationId": "your-application-id", "userName": "AWS User", "issue": "I love this blog post" }'

Możecie użyć oczywiście dowolnego narzędzia, którym wyślecie request.

W tym momencie, w naszej tabeli powinien pojawić się pierwszy wpis. Sprawdźmy.

Możecie wykonać jeszcze kilka requestów z różnymi applicationId. Będzie więcej danych dla metody…

GET

Wracamy do usługi API Gateway. Wybieramy nasze resource i dodajemy nowe.

Jako nazwę wpisujemy applicationId, a jako Resource Path „{applicationid}”. Będzie to parametr, za pomocą którego wybierzemy konkretną aplikację.

Tworzymy teraz metodę GET. Ustawienia są podobne do poprzedniej metody POST. Zwróćcie uwagę tylko na dwie rzeczy:

  1. Jako HTTP method ponownie wybieramy POST.
  2. Jako Action tym razem QUERY.

Ponownie musimy dodać mapowanie. Jednak tym razem nie tylko dla wejścia, ale także dla wyjścia. Musimy zamienić dane otrzymane z DynamoDB na JSON-a, którego zwrócimy do użytkownika. Klikamy więc w Integration Response i dodajemy mapowanie tak samo jak w poprzedniej metodzie. Tym razem wykorzystujemy jednak poniższy kod:

#set($inputRoot = $input.path('$'))
{
    "issues": [
        #foreach($elem in $inputRoot.Items) {
            "issueId": "$elem.issue_id.S",
            "applicationId": "$elem.application_id.S",
            "userName": "$elem.user_name.S",
            "message": "$elem.message.S"
        }#if($foreach.hasNext),#end
	#end
    ]
}

Abu zmapować dane otrzymane w requeście, klikamy w Integration request, włączamy mapowanie i wklejamy poniższy kod:

{
    "TableName": "<YOUR TABLE NAME>",
    "IndexName": "<YOUR-INDEX-NAME>",
    "KeyConditionExpression": "application_id = :v1",
    "ExpressionAttributeValues": {
        ":v1": {
            "S": "$input.params('applicationid')"
        }
    }
}

Pamiętajcie oczywiście o uzupełnieniu o nazwy tabeli oraz stworzonego wcześniej indeksu w DynamoDB.

Zapisujemy wszystkie zmiany, robimy deployment API i testujemy meetodę GET.

curl -X GET https://<URL OF YOUR API>/<STAGE>/issue/<your-application-id> -H 'cache-control: no-cache'

Nie zapomnijcie oczywiście o podmianie URLa do API, oraz o wstawieniu do niego własnego identyfikatora aplikacji. W moim przypadku wygląda to następująco:

curl -X GET  https://gtuufuccsh.execute-api.eu-west-1.amazonaws.com/blog/issue/your-application-id  -H 'cache-control: no-cache'

Jeżeli wykonaliście wszystko poprawnie, rezultat powinien być podobny do poniższego.

Metodę GET możecie zresztą przetestować w dowolnej przeglądarce.

Podsumowanie

W artykule zobaczyliśmy w jaki sposób „podłączyć” się do DynamoDB bezpośrednio za pomocą usługi API Gateway. Bez pośrednictwa funkcji Lambda. Możliwość ta nie jest oczywiście ograniczona tylko do DynamoDB. Możemy podobnie pracować np. z S3, Kinesis czy SNS.

Nie zawsze będzie to możliwe. Często nie wystarczy nam prosty zapis i odczyt danych i tam gdzie będziemy potrzebowali skomplikowanej logiki takie rozwiązanie się nie sprawdzi.

Ale warto mieć je na uwadze. Powinno przyśpieszyć nasze API, a my zapłacimy mniej za usługi w Amazon Web Services.

Comments are closed.