Subscriptions for React-Native with Appsync and Lambda Resolvers

Photo by Christopher Burns on Unsplash

At getsby we are developing an app that lets you order from your seat in a restaurant. To accomplish this, we are creating point of sale (POS) integrations that support real time data. We chose Appsync for our API as it offers a managed GraphQL back-end with real time subscription support via an MQTT implementation. To keep our lock-in to a minimum we did not want to switch from our existing relational database to Dynamodb, which meant we had to write our own resolvers and mapping templates using lambda functions.

To develop our API we used the serverless-graphql library provided by the serverless framework. It lets you locally develop, test (with some extra setup) and push changes to the cloud. We extended the RDS example that only supported queries at the time to support mutations and later support subscriptions as well.

from the serverless-graphql documentation [02.06.18]

Having the API all set up we moved on to implementing the react-native client. Using Apollo and the higher order component (HOC) compose this went smoothly for queries and mutations alike. The biggest issue we faced was the lack of documentation on the subscription end. We wanted to subscribe to a specific POS system when an addOrder mutation was fired. We did not want to subscribe to all POS systems.

The simplest form of a subscription would be to subscribe to all of the mutation events. In the example below every mutation addOrder triggers an event for the subscribed clients.

type Subscription {                        
addedOrder: Order
@aws_subscribe(mutations: ["addOrder"])
}

In order to only subscribe to a subset of the addOrder mutations we have to pass a input variable to addedOrder.

type Subscription {                        
addedOrder(pos_uuid: String!): Order
@aws_subscribe(mutations: ["addOrder"])
}

Above we added the input variable pos_uuid and made it compulsory. The important part, that was not clear to us from the documentation is that in order for the subscription addedOrder to be triggered, a mutation addOrder must be triggered that returns a variable with the same name and value as our subscription input variable. See example below:

Creating the subscription:

subscription subscribeToOrders {
addedOrder(pos_uuid: "1234") {
order_uuid
price
name
}
}

The subscription is not triggered when executing the mutation:

mutation createOrder {
addOrder(pos_uuid: "1234") {
order_uuid
price
name
}
}

But it is triggered if we add pos_uuid to the mutation return value

mutation createOrder {
addOrder(pos_uuid: "1234") {
pos_uuid
price
name
}
}

This is quite counter intuitive and we still have to wrap our head around why only the mutation return value is considered by the subscription and not the mutation input variable. We now have to consider making the return value pos_uuid mandatory for addOrder mutations if we want third party POS systems to integrate properly with our API.

Would love to hear of your experience in the comments.

If you like you can follow me on Twitter.