Use AWS GraphQL for React Native Message App
Recently I came up with a react native app which needs to integrate AWS GraphQL. Its actually provided with AWS App Sync and a very convenient way if you want a quick pub-sub. The problem was I had to do mutations as a background asynchronous process and wanted to update the app state from that. When I went through the documents I found the AWSAppSyncClient which is an Apollo Client. Although it was promising the react native examples were more focusing towards react or react-native Components which was really making a mess.
- The Views will have the logic of mutations and even if we move the code it wont be compact and easy to read.
- No way to call mutations asynchronously and since the state is maintained with redux, using component state for data is not good.
So I went through all the docs and used the Apollo Client documentation to come up with a plan like following
Configure AWSAppSyncClient to access AWS GraphQL URL
import AWSAppSyncClient from "aws-appsync"; import { Rehydrated } from 'aws-appsync-react'; import { AUTH_TYPE } from "aws-appsync/lib/link/auth-link"; import { graphql, ApolloProvider, compose } from 'react-apollo'; const client = new AWSAppSyncClient({ url: amplifyConfig.API.graphqlEndpoint, // https://##################.appsync-api.##-####-#.amazonaws.com/graphql region: amplifyConfig.API.region, // us-east-# auth: { type: AUTH_TYPE.API_KEY, apiKey: amplifyConfig.API.apiKey, //da2-############## }, disableOffline: true, });
Here I have used the API_KEY authentication since its easy for the first time configuration. Note that this auth key is expired after 7 days. Check the source code for the AWSAppSyncClient from the source https://github.com/awslabs/aws-mobile-appsync-sdk-js
The AWSAppSyncClient is a wrapper around the Apollo Client which is widely used as a Graphql client. The source for it can be found here https://github.com/awslabs/aws-mobile-appsync-sdk-js/blob/master/packages/aws-appsync/src/client.ts in case you need some understanding.
And the api reference regarding the Apollo Client can be found here https://www.apollographql.com/docs/react/api/apollo-client.html
Create a schema on AWS GraphQL, AWS App Sync
Login to the AppSync of AWS and create the schema with type message. (Note that you can change the data types as you want. I just wanted to use String here)
type Message { id: String! message: String! createdTime: String! createdUser: String! }
Then click on the Create Resource
After that enter the details as in the image and create the data resource. It will create the DynamoDB table and resolvers to get the data. That way it’s a one-click creation. But you will often need to change the fields and queries to get the data that you require.
After creating the resource you will see the following schema.
input CreateMessageInput { id: String! message: String! createdTime: Int! createdUser: String! } input CreatePositionInput { userId: String! placeId: String! updatedTime: String! } input DeleteMessageInput { id: String! createdUser: String! } input DeletePositionInput { userId: String! } type Message { id: String! message: String! createdTime: Int! createdUser: String! } type MessageConnection { items: [Message] nextToken: String } type Mutation { createMessage(input: CreateMessageInput!): Message updateMessage(input: UpdateMessageInput!): Message deleteMessage(input: DeleteMessageInput!): Message } type Query { getMessage(id: String!, createdUser: String!): Message listMessages(first: Int, after: String): MessageConnection } type Subscription { onCreateMessage( id: String, message: String, createdTime: Int, createdUser: String ): Message @aws_subscribe(mutations: ["createMessage"]) onUpdateMessage( id: String, message: String, createdTime: Int, createdUser: String ): Message @aws_subscribe(mutations: ["updateMessage"]) onDeleteMessage( id: String, message: String, createdTime: Int, createdUser: String ): Message @aws_subscribe(mutations: ["deleteMessage"]) } schema { query: Query mutation: Mutation subscription: Subscription }
Then save the schema and let’s code schema for the client to send message data
Query for schema from Client
Now we have to create the client schema. For that first what we need is a create mutation. And then subscribe to new messages created. The createMessage mutation schema looks like follows. And I have used this as Message.schema.js in next Message service
export const CreatePosition = gql`mutation CreateMessage($id: String!, $message: String!, $createdTime: String!, $createdUser: String!) { createMessage(input: {id: $id, message: $message, createdTime: $createdTime, createdUser: $createdUser }) { id message createdTime createdUser } }`; export const SubscribeToMessage = gql`subscription OnUpdateMessage($createdUser: String!) { onUpdateMessage(createdUser: $createdUser) { id message createdTime createdUser } }`;
Create an object that could execute this query using apollo client as follows. I named it as Message.subscription.js
import gql from 'graphql-tag'; import { CreateMessage, SubscribeToMessage } from './schema/Message.schema'; export class MessageSubscription { constructor() { this.initialized = false; } init({ store, client }) { this.store = store; this.client = client; this.initialized = true; } checkInitialized() { if (!this.initialized) { throw "Subscription Not initialized"; } } createMessage(message) { const result = client.mutate({ mutation: CreateMessage, variables: message, }).then(result => { store.dispatch(Actions.messageCreated(result)) }).catch(e => { // check and retry for attempts or send an error message }); } subscribeToMessages(userId, force = false) { if (userId === this.userId && !force) { return; } const { dispatch } = this.store; client.subscribe({ query: SubscribeToMessage, variables: { createdUser: userId, }}).subscribe({ next: (data) => { dispatch(Actions.messageRecieved(data)); }, //complete: subscribe again or notify //error: console.log, }); } } export default new MessageSubscription();
Now your client service is ready. To integrate it with the app, initialize the service when creating the store
MessageSubscription.init({ store, client });
Now you can dispatch any action and invoke the create Message and subscription as you want. Whether you want to access it through react components or redux actions with redux state.
The important thing to remember is that you need to consider the primary key and Validation errors which could occur with DynamoDB.
Feel free to add any comment if you want code or any other clarfications.