In your Mobilehub ‘hub’ you’ll see a list of projects. When you scroll down you can add many AWS services which corresponds with the npm package aws-amplify.

DynamoDB: NoSQL DB

Cognito: User sign-in

Lambda: Cloud functions

S3: Static file storage

CloudFront: Website hosting

User Sign-in is enough for our example. Click it and add only the setting Username: Username. All these settings can be changes later.

Create user pool, go back to your mobilehub project and hit Integrate on the web project.

We’re told to enter our client in the terminal, where we earlier initiated awsmobile. Run awsmobile pull and your local application should be up to date with your AWS Mobilehub project. The whole AWS process adds some folders to your client project.

Now that we created the Cognito User Pool, you can go back to AWS AppSync and connect it in the Projects settings:

Go back to AppSync and add your newly created User Pool. This will now be the new Auth method to your GraphQL Server

We’re also going to modify the AppSync GraphQL Server Resolvers. This is to make sure that every Cognito user that logs in only reads their own authored Recipes. Go to AppSync > Schema and browse the list on the right for ListRecipes. Click on it’s resolver.

In there, every time a user calls ListRecipes, we want them only to browse for their own authored Recipe. In the configure template object, add “expression”, “expressionValues” and “index” so that the object looks like this:

{

"version": "2017-02-28",

"operation": "Query",

"query": {

"expression": "author = :author",

"expressionValues": {

":author": {"S": "${ctx.identity.username}" }

}

},

"index": "author-index",

"limit": $util.defaultIfNull($ctx.args.first, 20),

"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.after, null)),

}

Now our application will look for the correct author in the Resource (DynamoDB). But how is this the Author field added in there? We’ll have to do the same for CreateRecipe’s resolver. Go back to Schema and find it:

In this resolver, we’re going to add the username as an Author (see the second #set line). Modify the code with the highlighted text:

#set($attribs = $util.dynamodb.toMapValues($ctx.args.input))

#set($attribs.author = $util.dynamodb.toDynamoDB($ctx.identity.username)) {

"version": "2017-02-28",

"operation": "PutItem",

"key": {

"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id),

},

"attributeValues": $util.toJson($attribs),

"condition": {

"expression": "attribute_not_exists(#id)",

"expressionNames": {

"#id": "id",

},

},

}

Great! Now our users will only read from their own Created Recipes:

Users are required to log in to the client and will only see their Recipes

Now it’s time to get our hands dirty in code. Go to your project index.js file and follow the setup I used here. Make sure your config files are correct, relative to your index.js file.

This code does a whole lot of things.

ApolloProvider sets you up to use Apollo-Client further down in the code. We pass down a client as props which is setup with AppSync specific data and the Amplify Cognito authentication method. The Rehydrated container allows AppSync to work with your code further down. AppWithAuth is the App with the aws-amplify-react HOC withAuthenticator() which gives you a premade GUI for the login screen. Amplify.configure(aws-exports.js) connects your client with AWS mobilehub. Further down we can use all the mobilehub services with ease! Here’s an example from the Amplify docs:

AWS GUI login screen for web, requires 2 lines of code

Further config with Amplify from their docs

The reason we set this all up in the index.js file is that they’re mostly HOC’s, containers and configs. They are to be setup once. For the last part of our project, we’ll create the Client and connect it with Apollo-Client.

4. Creating the Client

React, GraphQL and Apollo Client

It’s all coming down to this. It’s time for our client code. Go to your project in the terminal and create two folders, components and graphql. Then inside graphql, create 4 folders: mutations, queries, subscriptions and resolvers:

> cd src && mkdir components graphql > cd graphql && mkdir mutations queries subscriptions resolvers

Apollo + GraphQL

In the mutations, queries and subscriptions folders we keep all our GraphQL API functions, just as we created in AppSync Queries tab. In resolvers we currently have a index.js which holds all our Apollo-Client resolvers. It imports all our GraphQL. Resolvers are functions that we import to our App.js and compose them, like this:

import React, { Component } from 'react';

import { compose } from 'react-apollo';

import * as resolver from './graphql/resolvers/index'; class App extends Component { ... } export default compose(resolver.listRecipeGQLAction, resolver.createRecipeGQLAction, resolver.deleteRecipeAction)(App);

The following resolver contains all the Apollo Client logic for our app. It’s a heavy file with a lot of things going on. The Apollo team has stated that this isn’t a very friendly developer experience and will be simplified in the future. To get an explanation of the implementation, check out the video I’ve linked in the beginning by Nader Dabit. I’ve done some modifications to his code by adding DeleteRecipe and DeleteRecipeSubscription.

We see in our resolvers that they return props in every function. Because we compose them in our App.js, these props are automatically mapped to our App.js and can be passed down. The content of our Recipe application App.js looks like this:

Our State and onChange, addDirection and addIngredient functions manages the Recipe currently being added.

In componentDidMount() we set up the Subscription streams, which is a real-time connection with our database! This makes sure our client is updated at all times.

The addRecipe function calls our props.onAdd() which is created in the Apollo Resolver CreateRecipeGQLAction.

Our rendered components are treated as any normal React components with props passed down. See these github URL’s here as they’re mostly standard React Code:

The only exception here is really in Header.js where I created a logout function with aws-amplify:

Added benefits

Apollo-Client offers awesome caching and offline support. With this app, I can turn off internet, add a Recipe, refresh to error message, go to the toilet, return to turn back internet and refresh and the Recipe will have refreshed in my Database.

As stated, I were able to do all steps here, the use create-react-native-app and copy the GraphQL folder, setup some RN components and get a list of my recipes, updated in real-time if I delete in either DynamoDB directly, or the web client.

The end result

Screenshot of React Native app running alongside Webapp, updating real-time onAdd!

Conclusion

What is really interesting about this project is that I followed Nader’s guide with no experience of GraphQL except but a few meetups, and only some experience in AWS. After doing so I were able to completely ramp up my skills with some youtube and studying. 2 weeks later I were presenting this at a meetup for ~50 frontend developers, which is pretty decent.

I’m already sold on AWS a while ago. Now I’m absolutely loving GraphQL with Apollo and I get all the hype.

If you’re still here, thank you so much for reading!! This is my first technical post after spending years on Medium, learning a lot from the community. If you have any questions or there’s anything missing — comment or contact me asap and we’ll chat!

Cheers, jorgenlybeck