With Amazon DynamoDB you can also store entire JSON-formatted documents as single DynamoDB items. In this blog post I show you how this works in combination with AWS AppSync.
DynamoDB
In the following example I store multilingual translations in the database.
id | name | description |
---|---|---|
street-view | Street View | { "de" : { "S" : "Beschreibung" }, "en" : { "S" : "Description" } } |
The item in the table has an id
, which is the primary key. name
and description
are attributes. The description
attribute is a nested attribute.
This example is very simplified. You can also make nested attributes more complex. But please note the limit for nested attributes (📖 see the section Limits
).
GraphQL Schema
You can use the AWSJSON
scalar type, but this unnecessarily limits the capabilities of GraphQL. Instead, map the JSON within the schema.
For reasons of clarity, I describe only the mutation.
input MultilingualDescriptionInput {
en: String!
de: String!
}
input PutAppInput {
name: String!
description: MultilingualDescriptionInput!
}
type Mutation {
createApp(input: PutAppInput!): App
}
AppSync Mapping Template
In the request mapping template I use the utility helpers of AWS AppSync.
Take a look at the toMapValuesJson
helper:
$util.dynamodb.toMapValuesJson(Map)
returns the DynamoDB attribute value as a JSON encoded string.
{
"version" : "2018-05-29",
"operation" : "PutItem",
"key" : {
"id" : $util.dynamodb.toDynamoDBJson($ctx.args.input.name.replace(" ", "-").toLowerCase()))
},
"attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args.input)
}
Let's look at this in detail:
$ctx.args.input.description
is a JSON object and looks like
{
"de" : "Beschreibung",
"en" : "Description"
}
The $util.dynamodb.toMapValuesJson
helper maps the JSON object to
{
"de" : {
"S" : "Beschreibung"
},
"en" : {
"S" : "Description"
}
}
The response template returns the result as JSON.
$util.toJson($ctx.result)
Client-Side Mutation
The CREATE_APP
mutation expects two parameters. The name
of the type String
and the description
of the user-defined scalar type MultilingualDescriptionInput
(📖 see the section GraphQL Schema
).
mutation CREATE_APP($name: String!, $description: MultilingualDescriptionInput!) {
createApp(input: {
name: $name,
description: $description
}) {
id
name
description {
de
en
}
}
}
You can now access the individual fields in the return value. Of course this also works for your GraphQL queries.
Application
For completeness still the application code. 🤓
import React from 'react'
import { useForm } from 'react-hook-form'
import { useMutation } from '@apollo/react-hooks'
import { CREATE_APP } from './graphql/mutations.gql'
const App = () => {
const [createApp, { data }] = useMutation(CREATE_APP)
const { register, handleSubmit } = useForm({ nativeValidation: true })
const onSubmit = (variables) => createApp({ variables })
return (
<form
style={{ display: 'flex', flexDirection: 'column', alignItems: 'start' }}
onSubmit={handleSubmit(onSubmit)}
>
<label>
Name:
<input
type='text'
name='name'
ref={register({ required: 'Please enter the name' })}
/>
</label>
<label>
Description DE:
<input
type='text'
name='description.de'
ref={register({ required: 'Please enter the description' })}
/>
</label>
<label>
Description EN:
<input
type='text'
name='description.en'
ref={register({ required: 'Please enter the description' })}
/>
</label>
<input type='submit' value='Save' />
</form>
)
}
export default App
⚠️ Limits
- The maximum item size in DynamoDB is 400 KB.
- DynamoDB supports nested attributes up to 32 levels deep.
—
If you have any kind of feedback, suggestions or ideas - feel free to comment this post!
Top comments (0)