DEV Community

Daniel Bayerlein
Daniel Bayerlein

Posted on

How to store JSON in Amazon DynamoDB using AWS AppSync

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
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

Let's look at this in detail:

$ctx.args.input.description is a JSON object and looks like

{
  "de" : "Beschreibung",
  "en" : "Description"
}
Enter fullscreen mode Exit fullscreen mode

The $util.dynamodb.toMapValuesJson helper maps the JSON object to

{
  "de" : {
    "S" : "Beschreibung"
  },
  "en" : {
    "S" : "Description"
  }
}
Enter fullscreen mode Exit fullscreen mode

The response template returns the result as JSON.

$util.toJson($ctx.result)
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

⚠️ 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)