This will be a multipart series where I show you:
Without further ado, let's get started!
What is BFF?
Backend for Frontend(BFF) is the single general frontline API layer (Fascade) which our clients would be communicating to instead of making multiple API calls to fragmented microservices. It can be thought of as Alexa. Using VI (Voice Interface), you ask Alexa to send an email, turn on light, play TV etc. Here Alexa is the BFF, and email service, TV service, Lighting service can be considered as microservices.
Realworld use case?
This use case is about an IoT based vehicle monitoring app. IoT sensor sends data to IoT Core, and trigger a lambda, with the received payload. The lambda invokes Appsync mutate endpoint and updates the sensor data. This data is saved into the data source (say DynamoDB). The data can be then subscribed by Mobile Apps authenticated via Cognito. This data can also be locally persisted (offline sync) for offline access.
Appsync AWS Service
It defines the data attributes and functions available to the client.
1.a GraphQL Operations:
- Query (To read data from Database)
- Mutate (To update an entry)
- Subscribe (To subscribe to the changes introduced by mutating)
1.b Example Vehicle Schema
Vehicle Entity will also require Autoriziaton annotations. Else the below error is observed.
For Additional reference:
So you might be wondering how function declaration using type alone works in Appsync?
Function declarations have their implementations as well. On the right of Schema, we can see, the function declaration and we need to attach resolvers to the Query and Mutate functions.
For subscription, “@aws_subscribe” annotation alone does the job.
2.a Normal Resolver
- A sample resolver implementation for CreateVehicle is shown below
- Data Resolver: Here we are DynamoDB data resolver.
- Request Mapping Template: Maps Data Request to DynamoDB PutItem.
- Response Mapping Template: It converts the response to JSON.
Note: The language used is Apache Velocity Template (AVT).
- Similarly a sample resolver of Query:
2.b Pipeline Resolver
You can also setup Pipeline Resolver. It is kind of a chain of actions that you can perform on a request, from multiple data-sources supported by Appsync. You can click on Convert to pipeline resolver link and it automatically sets up the chain of responsibility.
NOTE: You can also perform transformation alone using NONE Datasource functions.
2.c Update Resolver for Single Argument (Used in subscription)
Normally, when you set up an AWS example, it provides UpdateVehicle endpoint which accepts the Vehicle Entity to be updated. In that case, it would be difficult to set up a subscription, based on a single argument (Say Id). So we need to set up a custom update function which accepts Id, latitude, longitude. So below is the data-source resolver for the same.
If you created/updated the schema manually, you can click on Create Resources button to create the corresponding DynamoDB tables etc.
These can be used individually in schema functions or in conjunction with functions of pipeline resolver.
Below are the available Appsync data sources:
- AMAZON_DYNAMODB: The data source is an Amazon DynamoDB table
- AMAZON_ELASTICSEARCH: The data source is an Amazon Elasticsearch Service domain.
- AWS_LAMBDA: The data source is an AWS Lambda function.
- NONE: There is no data source. This type is used when you wish to invoke a GraphQL operation without connecting to a data source, such as performing data transformation with resolvers or triggering a subscription to be invoked from a mutation.
- HTTP: The data source is an HTTP endpoint.
- RELATIONAL_DATABASE: The data source is a relational database.
DynamoDB gets automatically created for the schema uploaded.
For testing, I have created an SNS Lambda resolver, which just sends out a notification, when the endpoint is invoked.
3.a SNS Lambda
Below is the Node-based lambda, which sends SNS notification. This SNS Topic is having an email subscription.
3.a.1 NodeJS Serverless Code
3.a.2 IAM Role
Functions are single operations against a data source that can be used to implement redundant logic across multiple pipeline resolvers.
So, in the pipeline resolver, I can call a particular function and reduce redundancy. It is not applicable for regular data-source resolvers since they are having single function implementation.
Here you can test your Appsync functions (Query, Mutation, Subscription).
If you have set authentication to Cognito, you will find a Login button on the top. Click login and provide the Cognito App client id, username and password.
Here you can see that function is placed in the first half, and argument values are placed in the second half.
You can also execute these GraphQL Queries from POSTMAN (Except for subscription).
NOTE: I couldn’t get the Cognito Flow working in POSTMAN. But adding API-Key worked for me.
Execution Subscription request in POSTMAN will result in the below. The response is Amplify/Appsync specific, and postman won’t be able to process it. Subscribe works in AWS Amplify Queries, because of the embedded appsync SDK in there Query Page.
You can enable caching on the responses from data-source.
You can find configurations like API URL, APP ID etc.
You can also set a primary authorization mode.
7.a Default authorization mode
You can configure Cognito Authentication. You can also provide App Client Id regex to provide access to only specific client ids in the User pool.
7.b Additional Authorization Provider
You can set up additional authorization providers like API key (That can be used in POSTMAN) etc.
Enable logging to see logs in cloudwatch
7.d IAM Role Permission
7.e IAM Trust relationship
You can see cloudwatch metrics and logs if you have enabled Cloudwatch in the settings.
When you click on view Cloudwatch Logs, you will see the log messages. You can also use cloudwatch insight to make better use of logs.
Pros & Cons
- Offline Database for mobile/web client
- Realtime Update
- Fargate based (no worries about scaling)
- Internal throttling to protect against abusive traffic.
- Amplify support for Android, iOS, ReactNative (Flutter not officially supported)
- Authentications: IAM, Cognito, OpenID, API Key
- GraphQL benefits (Query what you need)
- Not that flexible for complex scenarios ( GraphQL replaces your entire BFF)
- The learning curve for Apache Velocity Template
- Exposed using Cloudfront (Hence no direct solution for monetisation, supported by API-Gateway or Apigee)
- No circuit breakers can be introduced.
- No developer portal available (as in API Gateway)
- Supported resolvers don’t include gRPC etc.
I have covered general details about Appsync. Next session would be about some insights on Appsync Subscription.
Found it Interesting?
Please show your support by 👏.