Pokémon Go and GraphQL with AWS Lambda

Quick guide on how to notify the nearest Pokémon Trainer with event-based triggers on Scaphold’s GraphQL platform.

Vince Ning
Scaphold
9 min readJul 13, 2016

--

Want to get notified every time a new rare Pokémon appears near you in Pokémon Go? Excited to have it join you in the ultimate nostalgia-filled quest to catch them all? Here’s a simple way to get started with GraphQL!

In this tutorial, we’ll create a Pokémon Go Clone app that will send Pokémon trainers push notification alerts whenever a new Pokémon appears near them.

We’ll be leveraging a couple services to help us do so without any infrastructure.

Namely, the tools we’ll be using are the following:

Without further ado, here’s the agenda:

  1. Define Pokémon Go Clone schema.
  2. Set up an Apple Push Notification Service (APNS) account.
  3. Enable the iOS Push Notification Integration via Scaphold.
  4. Set up an Amazon Web Services (AWS) account for Lambda.
  5. Deploy a simple Lambda function that sends iOS push notifications.
  6. Enable a Custom Integration via Scaphold.
  7. Go catch ‘em all!

Let’s get started!

Build our GraphQL API

To get started, we are going to need a datastore. Scaphold.io makes it easy to build and deploy production backend services with GraphQL. The Scaphold platform will both host our data as well as provide integrations so that we can easily tie in other services to supercharge our app.

After creating your app on Scaphold, we’ll use the Schema Designer to define our data types.

Let’s define two types: User and Pokemon. We want to be able to create a user who plays the game, and users can catch nearby Pokemon.

Here’s the cool part: Once a new Pokemon appears, nearby trainers will be notified that a new Pokemon appeared via an iOS push notification.

With these scenarios in place, we can define a GraphQL schema like so.

User (Pokémon Trainer) Type
Pokémon Type

After you define the shape of your data in the Schema Designer, Scaphold automatically creates a GraphQL API backed by a distributed data store that you can begin working with. It also offers integrations that enhance your API by pulling in the functionality of popular third party services. For example you can quickly add Stripe payments, social auth with AuthO, and push notifications with iOS Push.

The next step in building our Pokémon Go Clone is to add Apple’s Push Notification Service. To do this we are going to create a webhook that posts to an AWS lambda function that will query our GraphQL API and send push notifications to Pokémon trainers near where the new Pokemon appeared.

Apple Push Notification Service (APNS)

To prepare ourselves for writing the AWS Lambda function, we’ll need to configure APNS keys to be able to send iOS push notifications to trainers’ client devices. There are two keys that you’ll need to verify your push notifications: a .p12 key and a corresponding .pem key. If you don’t have these keys already, or are unfamiliar with how to obtain them for your Apple Developer account, you can start by checking out this guide here. Following these steps, you’ll initially create an app on your Apple Developer account and generate an SSL certificate (.cer file). Exporting this certificate, you’ll obtain an exchange certificate (.p12 file) and set your passphrase. Save these for use later.

But we’re still missing one file: the .pem key. This will be generated by combining both the .cer file and the .p12 file while executing this command:

$ openssl x509 -in scaphold-pokemon-go-clone.cer -inform DER -outform PEM -out scaphold-pokemon-go-clone.pem

You should now have three items saved for use in the next step:

  • .p12 key
  • .pem key
  • passphrase (for use with the .p12 key)

Scaphold iOS Push Integration

With APNS set up, we’ll need to make our GraphQL server aware of our APNS configuration. To do so, we’ll use Scaphold’s iOS push notifications integration to extends your GraphQL API with queries and mutations that hook into the Apple Push Notification Service.

From the Integrations dashboard, add the iOS Push Integration and input the keys and passphrase from the prior step.

Upon successful creation of your iOS Push Integration, you’ll now have access to several more queries that will help you manage your iOS device tokens and channel subscriptions.

  • Device Tokens: retrieve, create, or delete
  • Channels: subscribe and unsubscribe

In addition, you’ll also have the ability to send push notifications with these GraphQL mutations to specific users or channels.

Let’s play around with this newly set up integration by creating and associating a device token with a new user that’s logged in. We’ll create a new user first:

// Mutationmutation CreateUser($data: _CreateUserInput!) {
createUser(input: $data) {
token // Set Authorization header for future requests
changedUser {
id
username
}
}
}
// Variables{
"data": {
"username": "you@scaphold.io",
"password": "xxxxxxxx"
}
}

With the returned ID of the new user, your Location Services settings should update the user with a location.

// Mutationmutation updateUserQuery($data: _UpdateUserInput!){
updateUser(input: $data){
changedUser {
id
username
location {
lon
lat
}
}
}
}
// Variables{
"data": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // User ID
"location": {
"lat": 47.619098,
"lon": -122.321925
}
}
}

Finally, we’ll create a token that’s associated with the current user so that we can send a push notification to this trainer in the next few steps.

// Mutationmutation CreateDeviceToken($data: _CreateDeviceTokenInput!){
createDeviceToken(input: $data){
id
userId
token
}
}
// Variables{
"data": {
"token": "xxxx-xxxx-xxxx-xxxx-xxxx", // retrieved from APNS
"deviceType": "iOS"
}
}

AWS Identity & Access Management (IAM)

Now’s the time to create our webhook function using AWS. If you don’t have one already, you’ll need to set up an Amazon Web Services account here. Once you’ve logged in, in order to create a Lambda function, we’ll need to have a basic AWS role set up with permissions. Navigate to the Identity & Access Management (IAM) page.

In the column on the left, click on Roles to create a new IAM role.

Step 1: Create name for the new IAM role as lambda_basic_execution.

Step 2: Select role type as AWS Lambda.

Step 2: Choose AWS Lambda role type

Step 3: Attach AdministratorAccess as the policy.

Step 4: Save the Role ARN for use later and complete creation of the new role.

AWS Lambda

Now that you have an IAM role set up, you can now deploy live AWS Lambda functions. Here’s how:

Go ahead and download Scaphold’s AWS Lambda boilerplate code for this tutorial:

$ git clone https://github.com/scaphold-io/aws-lambda-pokemon-go-tutorial.git

Let’s walk through the code a little bit…

// AWS Lambda handler function that calls main()exports.handler = function(event, context, callback) {
console.log("New Pokemon appeared: " + JSON.stringify(event));
main(event).then(done => {
context.succeed(done);
}).catch(err => {
console.error("Error", err);
callback(err, 'Oh no! Process completed with errors!');
});
}

The main() function is a private function that contains the custom code that serves two purposes:

function main(pokemon) {    /* 1. Retrieve nearest trainer to the new Pokemon */    const body = {
query: `query Viewer ($data: _GeoLocationInput!) {
viewer {
getNearestUsersByLocation (location: $data) {
dist
node {
id
username
location {
lon
lat
}
}
}
}
}`,
variables: {
"data": {
"lat": pokemon.location.lat,
"lon": pokemon.location.lon
}
}
};
return post(body).then(res => { /* 2. Send notification to the nearest user. */ let nearestUser;
if (res.data.viewer.getNearestUsersByLocation.length) {
nearestUser = res.data.viewer.getNearestUsersByLocation[0].node;
} else {
return resolve("Looks like there's no one around.");
}
const sendPushNotificationBody = {
query: `
mutation sendPushNotificationToUserQuery($data: _SendPushNotificationToUserInput!){
sendPushNotificationToUser(input: $data){
id
channel {
id
createdAt
modifiedAt
}
badge
alertBody
alertActionLocKey
createdAt
clientMutationId
}
}
`,
variables: {
"data": {
"userId": nearestUser.id,
"badge": 1,
"alertBody": "A new Pokemon appeared near you! It's a Level " + pokemon.level + " " + pokemon.name + " with " + pokemon.health + " health.",
"alertActionLocKey": "Catch it!",
"payload": pokemon.location
}
}
}
return post(sendPushNotificationBody);
}

Along with this lightweight Lambda function, we’ve also included a few utilities to package and deploy your function to Lambda. Here are the steps:

$ npm run build
$ ./ZipFunction.sh scaphold-pokemon-go-ios-push

Set your unique AWS access keys as environment variables for the AWS SDK to work. Obtain keys here. Then, update the deployFunction.js file to reflect your IAM role ARN that you created earlier.

// deployFunction.jsconst FUNCTION_ROLE = "arn:aws:iam::xxxxxxxxxxxx:role/lambda_basic_execution"; // Change this. Use the IAM role ARN that you configured.

Now, you can go ahead an deploy your Lambda function.

$ node deployFunction.js scaphold-pokemon-go-ios-push

After deploying the Lambda function, you’ll need to configure how you want the function to be invoked. The simplest way to do that is to set up a trigger from AWS API Gateway.

You can learn how to set up security measures via API Gateway so that the endpoint is authenticated, but for simplicity, we’ll be keeping this Open.

The resulting API Gateway endpoint should look like this:

Pat yourself on the back! You’ve successfully created your first AWS Lambda function that retrieves nearby users based on location of a new Pokémon that appeared, and sends push notifications all through GraphQL!

Scaphold Custom Integration

Switching gears back to Scaphold, we’ll need to create a Custom Integration to allow Scaphold to know how and when to invoke the AWS Lambda function that you just deployed.

Use the AWS API Gateway endpoint that you created and saved from earlier to configure the Custom Integration. Essentially, this will fire a POST request to this endpoint every time a new Pokemon is created (i.e. newly-appeared). We’ll be sending the new Pokemon’s stats and its location to your AWS Lambda function, upon which the function will get the nearest trainer and send this information to your Scaphold iOS Push Integration for display on that particular trainer’s device.

Now, we can test this out by sending a GraphQL request to create a new Pokemon.

// Mutationmutation createPokemonQuery($data: _CreatePokemonInput!){
createPokemon(input: $data){
changedPokemon {
id
name
level
type
description
health
trainer {
id
username
}
}
}
}
// Variables{
"data": {
"name": "Pikachu",
"level": 5,
"type": "Lightning",
"description": "Ash Ketchum's favorite Pokemon",
"health": "100",
"location": {
"lat": 48.1,
"lon": -121.2
}
}
}

You can send this request via GraphiQL on Scaphold and if the device token was correct, check that trainer’s device to see that they received an iOS push notification. You can also verify against AWS CloudWatch for logs on your Lambda function to see that the data passed through and sent correctly.

Congratulations! You’ve just completed implementing a complex workflow in a few quick steps. AWS Lambda provides a natural extension to Scaphold’s GraphQL services. It’s never been easier to work with AWS Lambda functions and GraphQL. Keep posted to learn about the many ways Scaphold can help you build better backends that work with your existing workflows.

Now go catch ’em all!

Brought to you by Scaphold.io

--

--

Vince Ning
Scaphold

The service behind Scaphold’s GraphQL-as-a-service