Building Modern Serverless APIs with CDK, Python, and GraphQL (Part 5)

Building Modern Serverless APIs with CDK, Python, and GraphQL (Part 5)

Designing Event Driven Applications

This is the 5th and final part of this series.

In Part 1, we gave a brief overview of the concept of event-driven architectures, coupling, and defined all AWS services needed to build the API.

In Part 2, we created a new CDK project, and added CDK resources for our GraphQL API, alongside a GraphQL Schema, an SQS queue, a DynamoDB table, and an SNS Topic.

In Part 3, we created resources and lambda functions for the step functions workflow.

In the fourth Part, we elaborated more on the step functions workflow, added SNS and deployed the code to the cloud.

API TESTING WITH GRAPHBOLT

When it comes to testing GraphQL APIs, there are a couple of options you can use. The very first one is the AWS AppSync Console. It comes loaded with stuff like

  • Authentication

  • All Queries, Mutations, and Subscriptions

  • API Testing Interface

  • View and edit VTL templates, Functions, and Pipelines

  • And a lot more.

Irrespective of all its pros, it has a couple of cons that slow down developer productivity and have you thinking about alternatives. For example

  • Console Time out. I don't like signing into the console every 20 minutes of inactivity. This timeout can be increased to 60 minutes in the AWS settings. But security-wise, this isn't ideal. I don't need to expose my entire AWS Console because I'm testing an AppSync API.

  • Debugging your endpoint from AppSync is no fun. You need to open up the endpoint in Cloudwatch and search for the error through a hundred logs. This is one of the main reasons why observability platforms are on the rise every single day. Searching through Cloudwatch logs is stressful.

There are API clients out there that make managing, testing, and debugging GraphQL APIs Fun.

Say Hi to Graphbolt.

GRAPHBOLT

GraphBolt is a one-stop shop for everything related to AppSync to help you manage, build, test, and debug AppSync APIs.

The query client allows you to execute queries and mutations, just like with Postman or Insomnia. But it is Tailored for AppSync. Meaning, things like authentication (Cognito, API key, etc) are built-in and auto-detected. You only have to choose the right one. API keys are auto-detected and you only need to choose one vs to go copy and paste it in postman.

Let's take a quick look at its interface

alt text

At the top of the image, GraphBolt displays the auto-detected AWS Profile you've configured on your computer. I've highlighted it with a yellow rectangle. You can click the dropdown to see all detected AWS profiles.

Top right-hand side, there's a lock 🔒 sign highlighted in pink. Used in configuring authentication.

On the left-hand side of the image, I've highlighted 3 icons with a blue rectangle. The first icon is the Query Client. It provides an interface for building and running Graphql endpoints.

The second icon is the Query Inspector. It provides an interface for debugging. The third icon is Mapping Template Designer. It provides an interface to build and evaluate VTL templates and javascript resolvers.

On the right-hand side of the image, I've highlighted the bug icon with a red rectangle. That icon also takes you to the query inspector screen, to debug the API endpoint you just ran.

Then we have the green, purple, and black rectangles.

The green rectangle is for building the query.

Purple is for providing variables to a query.

Black is for displaying the response to the request when ran. It contains the body and header of the response.

Clicking on the bug icon takes you to this screen. (Query Inspector)

alt text

It has the Resolvers, Query, Request Headers, Response Headers, and Raw logs for the GraphQl Query you just ran.

I've highlighted an eye icon on the right, we'll see its use later.

With GraphBolt, you don't need to open up CloudWatch in order to debug your API. Just execute a request. Seeing something wrong? Click the 🪲 button (top right-hand side) and you’re taken to the query inspector screen.

There, you can visualize all the resolvers that were executed. All errors will be highlighted in red.

By clicking on the 👁 of any resolver, you can dig in and instantly see the $context object, the compiled mapping template, and the response from the data source.

Finally, you will also see the result of the resolver (returned value).

This allows you to understand what is going on.

We'll definitely be using GraphBolt to test this and subsequent APIs.

So the first endpoint I want to test is the postOrder endpoint. I want to place an order and see if it gets executed successfully or not. I expect a message to be sent to SQS, the postOrder function should poll and start a step functions workflow, and the email should be sent to confirm if payment was successful or not.

Let's check to see if we have a valid API key first. Click on the lock icon

alt text

The key is valid. If you don't have a valid key or no key at all, go to the settings menu in the AppSync console and create one.

GraphBolt would automatically detect the key once it has been created.

I'll make a request with this input. I want 6 cartons of pizza from Dominos Pizza.

    name:"Pizza",
    quantity:6,
    restaurantId:"Dominos Pizza"

alt text

Then hit send.

Log into AWS Console and navigate to the postOrder lambda functions CloudWatch logs. You should see a log message from SQS. The postOrder function polled the message from SQS.

alt text

If you look in detail at the message, you would see the input we sent to AppSync in the body of the message.

{
   "Records":[
      {
         "messageId":"80ddd9e2-740c-4c20-b6ce-c141382a21c8",
         "receiptHandle":"AQEBzS2lYHfSFDM79iWKIYQ3tydLNRuZWs7BqQlRTXwXMf6lBjigjjOIpXSBds6f9qhgTvOIwWc7kA4OmauYPBWkgxNFdVmx6ktmO6ph5MrJTGmWHM2cAQ45TQeKQn1tBjtI/ilOFeKghLasFnsqNVTN+8phsdbUI1ZOFfMqeALk9B2gvYIroQDLpjZpUgZsCSwA0xIoF+9fyWPO24Yax3XkRT0AneYiy08Ckwy6/RR2M+o6uceC+4XjxlzMuV16yhuuxtQdIiE6gBh5v9wYJYp5haZHUZFd0jwJLgkjOMInytIO249X4/eLlHTUWL/iVUJ3OQt9J7EObHBCYrfH5ARXsyc/oPW7B4A/RNWgO2AAvKYlLVw0sDWpRIynbJml46B+nY59ho+wc8vn5jcAmOMFZA==",
         "body":"{\"input\": {\"name\": \"Pizza\", \"quantity\": 6, \"restaurantId\": \"Dominos Pizza\"}}",
         "attributes":{
            "ApproximateReceiveCount":"1",
            "SentTimestamp":"1676395715258",
            "SenderId":"AROAR5S2TJZSTGYER2WGB:send-sqs-function",
            "ApproximateFirstReceiveTimestamp":"1676395715261"
         },
         "messageAttributes":{

         },
         "md5OfBody":"fe25d90fe8123ea670065bc94209c114",
         "eventSource":"aws:sqs",
         "eventSourceARN":"arn:aws:sqs:us-east-2:132260253285:sqs-queue",
         "awsRegion":"us-east-2"
      }
   ]
}

Next, open up the step functions workflow and see how the workflow played out.

alt text

alt text

alt text

From the workflow, we see that the payment was successful.

Now, we expect to receive a SUCCESS email

alt text

If you keep hitting the send button in GraphBolt, you'll receive a mix of SUCCESS and FAILED emails.

Be sure to also open up DynamoDB and check on the saved data.

alt text

Before destroying or clearing up all created resources for your application. Yes, you have to destroy the application once you are done, in order to not rack up unnecessary AWS Bills.

Test other queries like getAllOrders or getSingleOrder.

get all orders

alt text

getSingleOrder

alt text

What Next

We've barely scratched the surface when it comes to building Event-Driven Applications. I urge you to take this app as a starting point and build out something close to production-grade. I left a lot of room for improvement in the app.

For example, in the step functions workflow, you can easily take out a couple of the lambda functions and create a direct service integration between step functions and DynamoDB.

Step functions support >200 AWS Services.

Also, in the complete_order and cancel_failed_order lambda functions, we perform a DynamoDB update and then send an email through SNS.

Writing to the database and sending the email aren’t in one transactional scope.

What if the email fails to send when you've already done the DynamoDB update?

These are sweet edge cases you can resolve.

So instead of having lambda update DynamoDB as well as send an email, you can update DynamoDB directly in step functions, then use DynamoDB streams to connect to an SNS topic, through an event bridge pipe.

If you try this out, please do let me know how it goes.

Conclusion

In this tutorial series, we looked at how to create an Event Driven Application using AWS Services. We built the api, using AppSync, Graphql and python, then tested with GraphBolt. We assumed you have a basic understanding of step functions and suggested a couple of articles where you could step up in case you've never heard of step functions before or you've heard, but never used it.

I really do hope you enjoyed this piece. If you did, do leave a comment or a like.

Happy Coding ✌🏾

References