Utilizing resolver mapping templates to auto-generate IDs and AWSDate’s in AppSync

document id uk driving license driving licence

Overview

When I first started using AppSync to create a GraphQL backend, it all seemed way too good to be true. You define your types, and AppSync handles creating your GraphQL schema for you. Although this is great in theory, there is one major drawback that left me stooped for a long time. That being, “how the h@ll do I auto-generate IDs and dates?”.

When the schema is generated for you, it makes some terrible assumptions about the Create<Table>Input types that can lead to some very bad practices if you’re new to programming or just haven’t created a fully-scaled backend before. In this article, I’m going to walk you through how to auto-generate an ID and Date so you no longer have to worry about sending some garbage ID and Date that is generated on the client-side.

💻 Let’s get started!💻

Fixing AppSync generated GraphQL

Let’s assume you created a resource for your backend service that manages Posts using AppSync. The following schema is an example of the GraphQL that be generated for you by AppSync:

type Post {
   id: ID!
   post_date: AWSDate!
   post_content: String!
   author_username: String!
}

input CreatePostInput {
   id: ID!
   post_date: AWSDate!
   post_content: String!
   author_username: String!
}
// .... more types

Before we can talk about how to generate ID’s, we need to clean the CreatePostInput type that AppSync generated for us. The two changes that you will want to make are :

  • Removing ID fields from the Create input types
  • Removing date created fields from Create input types

We want to do this is for two reasons: security and SWE consistency.

When we rely on the frontend apps to generate IDs / Dates for us, we have no idea where this information is really coming from and if it’s authentic. Could it be proxied? could the client be run through a debugger? As a result, most clients are very weak in terms of security, so we want to strip away these vital DB properties from create mutations as these weaknesses can be used to a hackers’ advantage.

We also remove these to create consistency across applications. When you ask the client to generate the ID, you have no idea what they’re actually sending back as an ID. Could it be a number? Could it be a username? Who knows? Consequently, you have to create extra input validation on the backend as well as ask the frontend team to write the implementation on their end. The same thing goes for date-strings. The backend has to make the validation and all frontends have to write implementations 🤢.


KEY TAKEAWAY: just generate IDs/creation dates on the backend to increase the security and establish good SWE practices.


Now that we have removed id and post_date, your CreatePostInput type will look something like this:

type Post {
   id: ID!
   post_date: AWSDate!
   content: String!
}

// updated to not accept an id or post_date
input CreatePostInput {
   content: String!
   author_username: String!
}

We can see that createPost mutation will only accept the post content and username, but the Post type still expects non-nil values for id and post_date. This means that it’s expected of us to generate the id and post_date variables so when posts are queried, their resolvers can return a valid Post object.


Auto-generated ID and AWSDate

Now that we’ve fixed the CreatePostInput type to not take in an ID and AWSDate, we need to generate these values before placing them in our DynamoDB table. If we don’t do this, then GraphQL will get mad at use nad throw an error when the Post type is returned from the createPost mutation. The reason GraphQL will throw an error is due to the fact that required id and post_date to not be null by using (!). Since we never assigned these values when creating our CreatePostInput type, they’re both currently null in the DynamoDB because they’re not assigned as an attribute in the request mapping template.

What is a resolver mapping template (RMT)?

Per AWS docs, [Resolver Mapping Templates] tell AWS AppSync how to translate an incoming GraphQL request into instructions for your backend data source, and how to translate the response from that data source back into a GraphQL response. They are written in Apache Velocity Template Language (VTL), which takes your request as input and outputs a JSON document containing the instructions for the resolver. You can use mapping templates for simple instructions, such as passing in arguments from GraphQL fields, or for more complex instructions, such as looping through arguments to build an item before inserting the item into DynamoDB.”.

In Layman’s terms, the resolver mapping template maps our GraphQL request to a type that the resolver can understand. Since the most common form of communication data is JSON, the resolver is returned the the GraphQL request structured as a JSON object.

Generating values in resolver mapping template

Now that we know what a resolver mapping template is, we need to add the functionality to auto-generate ID’s and dates. To do this, we need to go to the createPost mutation and click on the Resolver (PostTable) to the right of the mutation which can be seen below

AppSync Schema console

Once you click on the resolver, you will be taken to a resolver mapping template. Note that it will say request mapping template at the top (I believe that this is legacy from API gateway…) but it’s easiest to just think of it as the resolver mapping template for me so I don’t get confused. The mapping template will look something like this:

Resolver/Request mapping template for createPost()

Notice that your template will look a little bit different than mine, but that’s completely fine! Likely in your code, the id will be retrieved from the context.args which contains the inputs from our createPost endpoint. Since we did not include the id a parameter to the function, we need to autogenerate the id in our mapping template!

The main focus points that I want you to look at are in the key property for the returned JSON object. You can see id and post_date are both assigned using a $util function provided by AWS and not through the context. These are the two pieces of code that you will need to auto-generate your ID’s and Dates! 🍻

// Auto generate ID
$util.dynamodb.toDynamoDBJson($util.autoId())
// Auto generate Date (add string to format to your liking)
$util.dynamodb.toDynamoDBJson($util.time.nowFormatted("yyyy-MM-dd"))

In the code above, we’re simply saying “Utility functions, generate an ID, then the ID to DynamoDB JSON” and “Utility functions, generate a date using the current time and format it, then convert to DynamoDB JSON”. The reason we have to wrap the utility function result in the JSON converter is that our handler is expecting a JSON object. So, we need to make sure that what we pass to them is conforming to JSON standards! Pretty simple, huh? 👍🏽

Difference between key and attribute

I do want to bring one nuance in the example above to your attention. I have the post_date as a sorting key for my posts and not as a normal document attribute. If you’re not sorting by post_date (or enter some other sorting key/ secondary key), add the post_date attribute where the default values are being added in the photo! The reason you would add the attribute it where the default values are and not in a key section, is because DynamoDB is not expecting a post_date key and is expecting it to be a normal attribute.

Also, you can add the default values inside of the JSON object (just place it in without commas at the end). I personally prefer doing the mapping above the object, but it’s really up to you and how you want to structure the mapping object!


Wrap Up

As you can see, the resolver mapping template is very flexible and allows us to auto-generate values that we don’t want the user to have to pass in for security and SWE reasons. We also looked at some of the utility functions that AWS provides and how to use them to add default values/ auto-generated values to our mapped request.

Now that you have a basic understanding of RMT’s, try adding default values for various attributes in your collection(s). For instance, you might have an S3Metadata bucket, and you default the bucket name to some bucket that you have. The more practice you get with Apache Velocity and these template the better you will be with them!

Let me know if you have any tips or tricks with RMT’s and how you’re using them in your AppSync project down below! ⬇️

If you liked today’s content, make sure you subscribe to the newsletter down below and if you want to support my coffee addiction, help me out by buying me a coffee! It keeps me going to create more AWESOME FREE CONTENT FOR YOU! As always, thanks for taking the time to unwrap some bytes with me. Cheers! 🍻

Processing…
Success! You're on the list.

Leave A Comment