AppSync Resolver Optimization by Removing Unnecessary GetItem's
Tags: appsync, aws, dynamodb, serverless
Published: 2020-09-04 22:33:44

---
title: AppSync Resolver Optimization by Removing Unnecessary GetItem's
published: true
date: 2020-08-16 00:00:00 UTC
tags: appsync, aws, dynamodb, serverless
canonical_url: https://www.goingserverless.com/blog/appsync-resolver-optimization-by-removing-unnecessary-getitems
---

I had a front end developer comment that one of API's was slow. The developer was trying download all of the products and their related image. After a quick investigation it turned out we had hit the dreaded N+1 problem.

Using AppSync's new context info object we've been able to apply a simple optimization that has significantly reduced the number of DynamoDB queries our API was making.

<!-- more -->

A simplified version of the schema would look something like this:

```
type Image {
  id: ID!
  url: String
}

type Product {
  id: ID!
  name: String
  image: Image
}

type Query {
  listImages: [Image]
  listProducts: [Products]
}
```

The AppSync resolvers for `Query.listProducts` and `Query.listImages` both performed a DynamoDB `Query` allowing one request to return multiple records. The `Product.image` resolver used a `GetItem` to fetch the image for each product.

In my head the developer would use a query like this

```
query ListProductsWithImage {
  listProducts {
    id
    name
    image {
      id
      url
    }
  }
}
```

You might notice a problem here. While `listProducts` can be performed with a single DynamoDB operation I'm performing another DynamoDB operation for every record that it returns. The N+1 problem.

It turned out that the developer was actually using two queries and joining the data in the application. The first downloaded all of the images

```
query ListImages {
  listImages {
    id
    url
  }
}
```

The second all of the products

```
query ListProducts {
  listProducts {
    id
    name
    image {
      id
    }
  }
}
```

While this could have been efficient it wasn't because the `Product.image` resolver was still performing a `GetItem` for each product returned by `Query.listProducts`.

This was crazy because we store the `Image` `id` in the `imageId` attribute as part of the `Product` record in DynamoDB.

One solution would be to expose the `imageId` in `Product` but this is discouraged in GraphQL schema design because it exposes the internal implementation details to an external client making it harder to change the implementation later.

With the new `$ctx.info` object and early returning I can now optimize my resolvers for this use case by adding the following code at the top of the `Product.image` request resolver.

```
#if ($ctx.info.selectionSetList.size() == 1 && $ctx.info.selectionSetList[0] == "id")
  #return({ "id": "$ctx.source.imageId" })
#end
```

If `id` is the only `image` field requested it will now return early, before the `GetItem` operation, using `$ctx.source.imageId` as the `id`.

This small optimization improves API performance and saves money by removing unnecessary `GetItem` requests without exposing the internal implementation but still allows the client to fetch additional fields if required.