- Hey, folks. I'm Alex DeBrie.
And today, we're gonna talk about using DynamoDB
with your GraphQL API.
Now, a lot of DynamoDB advice
recommends using single table design
for your DynamoDB applications,
and there's some debate about how well
that works with GraphQL.
In this video, we're gonna show how to use
single table design with your GraphQL API.
The key trade off to know here is this is gonna add
a little bit more complexity in your resolvers,
but you're gonna have better performance on your queries.
So we're gonna build this using AWS AppSync.
I'm gonna show the key points in the AWS Console,
but if you wanna see how this is done
using infrastructure as code,
go ahead and check out the repo
that's accompanying this project.
Before we get started,
I just wanna talk about the guiding application
that we're gonna use for this example.
Imagine we have some sort of SAS blog hosting platform.
So users can come in, they can set up a site,
so we have a site entity in our application.
Once they've created a site, they can blog on that site,
add lots of different posts,
so there's a one-to-many relationship
between sites and posts.
Also, when other users and readers
come and look at those posts,
they can comment on those posts if they want.
So we have a one-to-many relationship
between posts and comments.
Now, the key point here
is even though we have three separate entities,
they're all gonna be going into that single DynamoDB table
that we're gonna be using for this application.
All right, the first thing we're gonna do
is create that DynamoDB table.
So head to the DynamoDB section of the AWS Console
and click that Create table button to get started.
We need to give our table a name.
Since we're just using a single table here,
we'll just give it a name like AppSyncSingleTable.
And then, we're gonna choose the primary key for our table.
Since we're using single table design,
we'll use that generic PK, SK values
for our partition key and our sort key there.
We'll keep most of the settings the same.
The only thing we wanna change here
is go to On-demand mode,
so we don't really need to worry
about our capacity for this,
and scroll down and hit that Create table button.
We'll just give it a second
and wait for our table to be created.
And as soon as it's created,
we can go on and create our AppSync API.
To do that, we wanna head to the AWS AppSync section
of the AWS Console.
Go there and click that Create API button.
There are a few different wizards and guides,
ways we can set up our schema,
but we already have a schema
so we'll just do the Build from scratch option
and click Start.
Next thing we need to do is give our API a name,
so we'll give it the name SingleTableBlog,
and hit that Create button.
So now, we've created our API.
What we need to do is edit our schema.
So let's click that Edit schema button.
And we'll just paste in our schema
that we already have defined for that.
I wanna take a quick look at that.
You can see the schema root
which has both query and mutation on it.
Within our query, you can see we have
a few different top level queries:
getSite, getPostsForSite,
things that are in common in our application.
We also have the different mutations that we'll have:
createSite, createPost, createComment, things like that.
Additionally, we have all of our types defined.
You can hit Save Schema to save that schema to your API.
One last thing I wanna do here, head over to Data Sources,
and we're gonna register our DynamoDB table we created.
So we create our DynamoDB table.
We wanna register it for use here.
We'll give it a name of DynamoDBTable,
and indicate that it's of type DynamoDB table.
We'll look up the region that we created it in,
and then we should be able to see
our AppSyncSingleTable table.
Once you've done that, hit that Create button
and you're good to go.
You've now registered your table within your API
and you can use it in a resolver.
Let's go create one of these resolvers now.
So what I wanna do is head back
to my schema that I've already entered.
And if you look at your schema on the right hand side,
there's a Resolver section,
where you can attach different resolvers
to different fields in your schema.
I'm gonna scroll down to the mutation resolver
and do that createSite,
sort of the top-level resolver in my application.
I'll configure a data source
and because I'm going directly to a DynamoDB table,
I'll use what's called VTL templates.
So this is a templating language
to interact directly with my DynamoDB table.
If you don't like VTL,
you can use a lambda function directly instead
and work with your DynamoDB table that way.
But I'm gonna be working with VTL
in this particular example.
So I'll paste in
both the request mapping and the response mapping.
One thing, if you look at the request mapping,
just notice the key we're using there.
We're using PK and SK for that
primary key value for our item.
We also have a type attribute.
These are common DynamoDB single table
design principles here, so we'll just do that.
We'll save this resolver here.
And we've configured our first resolver
on our DynamoDB table.
I wanna set up one more resolver for our schema
because it's a little more complex,
and it's gonna show off that single table design.
So let's go down and find our site type in our schema.
You can see that it has some properties,
id, name, domain,
but it also has this posts property,
which is of type PostConnection,
which is gonna include a cursor
plus a post array of other posts.
So what that includes is
if someone comes and gets our site,
they can also fetch the post with it.
We're gonna show how to use single table design
with our GraphQL schema here.
So let's go set up our getSite resolver here.
We'll find that query and attach a resolver,
so DynamoDBTable.
Again, we have to do that request mapping template,
response mapping template.
I'm gonna put in the request mapping template here.
One thing to note is just look at the top there.
We're doing some interesting work
where we're looking at the selection set list.
So this is basically saying
what attributes are they looking at
in this particular resolver,
and if it contains posts, we're gonna fetch more items.
We're gonna fetch 11 items, so the site plus 10 posts.
Whereas if we're fetching just the site,
then we'll just get that one site item.
Likewise, let's go down into
the response mapping template section down here.
This is complex as well.
At the beginning, we're saying,
"Hey, if we don't get anything back, just return that."
But if we do get some items back,
now we need to iterate through that result set
and sort of deserialize those, figure out what they are.
If it's a site, we'll set that as our site item.
If it's of type post, we'll add it to this post array.
And then later on, we'll add those posts
to our site as an attribute,
include both the cursor and those posts,
and then return it back to our calling client.
So again, the VTL is a little more complex here,
but you can still use those single table principles
with AppSync with GraphQL and DynamoDB.
I'm gonna set the rest of the resolver up on my own time,
but there's one last thing I wanna do.
Let's set up x-ray on our GraphQL API, so go to Settings,
and then you can scroll down and click Enable X-ray,
and save those settings.
And what x-ray is gonna give you is just visibility
into what's happening on a particular request.
So you can see all the different resolvers that were hit,
all the different APIs that were called
as part of a request.
All right, with x-ray configured,
with all our resolvers set up,
it's time to actually run some queries.
So let's go over to the left-hand side here,
click that Queries button.
And what this is, inside AppSync
you get a little query browser
that you can run some queries directly in there.
So the first thing we wanna do is create our first site.
We'll do that createSite mutation
and just create a blog for awsamazon.com.
So we'll send that in.
We were able to create that site.
But a site is only as good as the posts that are on it,
so let's create a few posts as well.
We'll use that createPost mutation there
using that awsamazon.com domain,
and send in our first blog post.
Hurray, that succeeded, we got one blog post in there.
Let's do one more blog post,
'cause we don't want to have just one
as we're showing this.
So let's get a second blog post in there,
and we have our second post.
This was able to create a second post.
So now, that we've created these things,
let's actually query them back.
And that's where we're gonna see the power
of this single table design.
We'll do a getSite query here,
and let's just look at the structure of that query.
It's a multi-level query
where we're doing that getSite operation
and getting that site back.
But as part of that, we're also fetching the posts
that are related to that.
So we have a one-to-many relationship,
multiple levels of entities,
and you can see it returned all that data back to us
in a pretty quick and snappy and responsive way.
So now that we've seen that,
let's see what actually happened on that last query
'cause we gotta see the magic of single table design here.
So let's head to x-ray and it's got
a few different traces in here.
We're gonna find that, the most recent one,
to see our last query,
and just click on that particular trace.
If you scroll down to the bottom,
you can see all the different requests
that were happening as part of that.
And if you look in that,
it only makes one request to DynamoDB.
So even though we're getting
two different types of entities,
our site entity, our post entity, only making one request,
it's also a pretty quick operation.
This is about half the time that same operation,
that same GraphQL query took in our multi table post.
So it's quicker there because it's only making
that single request to DynamoDB.
I think this was able to show some of the trade offs
of using single table design with your GraphQL API.
On the one hand,
it added some complexity to our resolvers,
as we have to do look aheads in our queries
to see what fields they're fetching.
We also have to do some additional parsing
of that DynamoDB query result to deserialize
into the different item types.
On the other hand, our response had much lower latency
than doing it in a multi table sequential approach.
You need to think about what's best for your needs
and apply the right design patterns.
Be sure to check out the same application
using multiple DynamoDB tables for comparison.
Thanks for watching. Hope you enjoyed it.