0% found this document useful (0 votes)
29 views14 pages

GraphQL _ How To Build GraphQL Resolvers for AWS Data Sources _ Amazon Web Services (AWS)

Uploaded by

Anu
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
0% found this document useful (0 votes)
29 views14 pages

GraphQL _ How To Build GraphQL Resolvers for AWS Data Sources _ Amazon Web Services (AWS)

Uploaded by

Anu
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 14

Front-End Web & Mobile / AWS AppSync / GraphQL  

How to Build GraphQL Resolvers for


Get Started for Free Contact Us

AWS Data Sources


Build GraphQL APIs with AWS AppSync

What is a GraphQL resolver


A GraphQL resolver is a function or method that resolves a value for a type or field within a schema. A
resolver is the key architectural component that connects GraphQL fields, graph edges, queries, mutations,
and subscriptions to their respective data sources and micro-services.

What AWS data sources can GraphQL APIs connect to


GraphQL APIs can connect to any AWS data source using resolvers. This article shows GraphQL resolver
examples for Amazon DynamoDB, Amazon Aurora, Amazon Relational Database Service (Amazon RDS),
and AWS Lambda. Custom resolvers extend the default GraphQL options that many services, such
as Apollo Server, Express GraphQL, GraphQL Java, Hot Chocolate .NET, and AWS AppSync, a fully
managed GraphQL service, provide.

How to build GraphQL resolvers with self-managed GraphQL


servers
If opting for a self-hosted, open source GraphQL server, there are many options available. This article uses
Apollo Server as an example for building resolvers with JavaScript (or TypeScript) to connect a self-
managed, open source GraphQL server to various AWS data sources.

This is an example of the code for a query resolver:

JavaScript

1 const resolvers = {
2 Query: {
3 hello: () => { return 'Hello world!'},
4 },};  

Get Started for Free Contact Us Copy

This is an example of the code for a GraphQL mutation resolver:

JavaScript

1 Mutation: {
2 login: async (_, { email }, { dataSources }) => {
3 const user = await dataSources.userAPI.findOrCreateUser({ em
4
5 if (user) {
6 user.token = Buffer.from(email).toString('base64');
7 return user;
8 }
9 },},

Copy

The login scenario above shows how GraphQL resolvers can become more complex. Moreover, it
highlights how it might be necessary to reach out to other systems, call other functions, and perform other
calls to build the return object for the query or mutation.

Connecting to Amazon DynamoDB with Apollo Server


For building connections to AWS data sources, it is recommended to use AWS SDKs. This is especially
important for DynamoDB, as the SDK provides many features for interacting with DynamoDB tables and
data. See this article on DynamoDB data modeling with GraphQL.

In the following snippet, Apollo Library and SDK are used to perform several key steps: configuration for
the connection, instantiation of the DocumentClient, and then executing these for a put against the
database.

JavaScript

1 var AWS = require("aws-sdk");


2
3 AWS.config.update({
4 region: "us-west-2",
5 endpoint: "http://wherever.your.location/is"
6 // Security keys, tokens, etc, will need setup and stored securely
7 })
8
9 var docClient = new AWS.DynamoDB.DocumentClient()  
10
11 var Get
params = for
Started { Free Contact Us
12 TableName: "MoviesTable",
13 Item:{
14 "year": 2025,
15 "title": "The Big New Movie"
16 }
17 }
18
19 Mutation: {
20 createMovie: async (_, { email }, { dataSources }) => {
21 docClient.put(params, function(err, data) {
22 if (err) {
23 // Item failed, handle error and respond with pertim
24 HANDLE.error("Unable to add item. Error JSON:", JSON
25 return err
26 } else {
27 // Item added here.
28 HANDLE.log("Added item:", JSON.stringify(data, null,
29 return data
30 }
31 })
32 },
33 }

Copy

For additional information on building out access and usage plans, see the DynamoDB Documentation
Developer Guide and the Apollo Documentation.

Connecting to Amazon RDS and Aurora with Apollo Server


Amazon RDS and Aurora are two AWS relational databases. They provide many options with support for
Postgres, MySQL, SQL Server, and others as the SQL engines.

This is an example of an Apollo GraphQL resolver with a PostgreSQL database using the pg client for
Node.js and the SDK.

JavaScript

1 const { Pool } = require('pg')


2 const { RDS } = require('aws-sdk')
3
4 const signerOptions = {
5 credentials: {  
6 accessKeyId: process.env.youraccesskey,
7 secretAccessKey:
Get Started for Free process.env.yoursecretaccesskey,
Contact Us
8 },
9 region: 'us-east-2',
10 hostname: 'example.hostname.us-east-2.rds.amazonaws.com',
11 port: 5432,
12 username: 'postgres-api-account',
13 }
14
15 const signer = new RDS.Signer()
16 const getPassword = () => signer.getAuthToken(signerOptions)
17
18 const pool = new Pool({
19 host: signerOptions.hostname,
20 port: signerOptions.port,
21 user: signerOptions.username,
22 database: 'my-db',
23 password: getPassword,
24 })
25
26 var insertQuery = 'INSERT INTO MoviesTable(title, year) VALUES($1, $
27 var queryValues = ['The Big New Movie', '2025']
28
29 Mutation: {
30 createMovie: async (_, { email }, { dataSources }) => {
31 res = await pool.query(insertQuery, queryValues)
32 // Handle response errors, data, and other processing here.
33 if (err) {
34 // Item failed, handle error and respond with pertiment
35 HANDLE.error("Unable to add item. Error JSON:", JSON.str
36 return err
37 } else {
38 // Item added here.
39 HANDLE.log("Added item:", JSON.stringify(data, null, 2))
40 return data
41 }
42 },
43 }
44
45 pool.query(insertQuery, queryValues)

Copy
This example demonstrates Amazon RDS and PostgreSQL specificities. The connection pooling that a

relational database does – whether PostgreSQL, MySQL, SQL Server, or another – is an important part of 
the connection that must be managed.
Get Started for Free Contact Us
For more information on writing queries against Amazon RDS and Aurora, see documentation on
Amazon RDS Proxy; for more on PostgreSQL pooling for pg, see Pooling Documentation.

Connecting to AWS Lambda with Apollo Server


Lambda is not a data source specifically, but it could provide a bridge to almost any data source.
Connecting and invoking a Lambda function follows several steps. The following example demonstrates
the key parts of making a Lambda call from a resolver using the SDK.

Parameters are built up and passed to the Lambda function and the call within the GraphQL resolver.

JavaScript

1 var AWS = require("aws-sdk");


2
3 AWS.config.update({
4 region: "us-west-2",
5 endpoint: "http://wherever.your.location/is"
6 // Security keys, tokens, etc, will need setup and stored securely
7 });
8
9 const params = {
10 FunctionName: 'my-lambda-function',
11 Payload: queryValues,
12 };
13
14 Mutation: {
15 createMovie: async (_, { email }, { dataSources }) => {
16 const result = await (new AWS.Lambda().invoke(params).promis
17
18 if (err) {
19 // Act on the err here to pass the err.stack back as an
20 // GraphQL error.
21 return err
22 } else {
23 const res = await pool.query(insertQuery, queryValues)
24 // Act on the response here.
25 return data
26 }
27 })
28 },
29 }
Copy  

Get Started for Free Contact Us


Best practices for building resolvers with self-managed
GraphQL servers
1. SDK

Use AWS SDKs for all calls to AWS resources. This will ensure a consistent, reliable, and maintained
access method for GraphQL APIs and their respective data sources.

The SDK provides a more consistent way to setup configuration; pass secrets for connections; handle
errors, retries, and exponential back off; and other important aspects across the code base. Using the SDK
also makes it easier to set up a particular access style, such as async await, promises, or callbacks. There
are many additional reasons to use the SDK beyond these immediate advantages.

JavaScript

1 var AWS = require("aws-sdk");


2
3 AWS.config.update({
4 region: "us-west-2",
5 endpoint: "http://wherever.your.location/is"
6 // Security keys, tokens, etc, will need setup and stored securely
7 });

Copy

2. Naming, parameterization, and clear code

Name all the operations, parameters, and other passed code mapped with their respective database
queries across their layers of concern.

For example, if the GraphQL query is to get movies, and it looks like this example, ensure that the
respective query on the database side matches the names or naming. The table in the database should be
named movie or movies, depending on the convention. Moreover, this all leads to a better understanding
and readability when determining which GraphQL query belongs to which database query, database table,
and so on. This prevents the multiple layers of GraphQL query or mutation and entity with fields from
getting confused or out of synchronization with the database. Furthermore, when options present
themselves, this provides an easier path to automation if code generation or other technologies are used
to create resolvers.

JavaScript
1 query GetMovies{
2 movies {
 
3 title
4 Get Started
year for Free Contact Us
5 }
6 }

Copy

3. Request and response connections, pooling, and fire and forget

Determine a strategy to maintain a clear tactical understanding of what behavior to expect from the API.

For example, a good practice to follow is writing code against a determined (based on desired outcomes)
connection method. If a database is setup and the intent is to use a service such as Amazon RDS Proxy to
implement connection pooling, ensure that driver code is written for the GraphQL resolvers to match that
architectural decision. If a client connection must fire and forget, this needs to be taken into account.
Otherwise, if a client must set up a connection and interact with multiple queries per resolver, that must
be part of the design patterns used. This prevents resolvers from being written that deviate from expected
usage, which would result in few to no errors or warnings and an almost impossible situation to debug.

4. Authorization

Determine what authorization strategy will be used upfront. Define expected results and behavior, and
tooling requirements before starting to design a GraphQL API.

JavaScript

1 async theUser(_, args, context) {


2 if (!context.isAuthenticated) {
3 // Handle not authenticated.
4 }
5 if (!context.isInRole(role)) {
6 // Handle not being in role.
7 }

Copy

For example, if row or field level authorization of data is required in the API, the decision must be known to
make the correct decisions about tooling. For some scenarios, using Amazon Cognito might be perfect,
for others, perhaps just a simple authentication mechanism, and for others still, something completely
different might be needed. Deciding on this up front ensures that the right tooling choices are made and
that project restarts are minimized.
5. Queries  
Ensure that only the fields being requested are being queried for, and that they only return what is asked
for. Get Started for Free Contact Us

For example, if the query reads like the following, then it is only asking for field1 and field2 of theEntity to
be returned.

JavaScript

1 query anExtremelyMinimalQuery {
2 theEntity {
3 field1
4 field2
5 }
6 }

Copy

If the database must pull data from a singular table, then the SQL query would look something like the
following:

JavaScript

1 SELECT field1, field2 FROM theEntityTable

Copy

However, if a join must occur, then it might look like this:

JavaScript

1 SELECT field1.firstEntity, field2.secondEntity


2 FROM firstEntity
3 INNER JOIN secondEntity ON firstEntity.ID = secondEntity.ID

Copy

In these cases, these queries must be generated or written to only include the needed elements for the
GraphQL query. Furthermore, they are written or generated in a way that would be performant. Not doing
so can lead to performance issues.
How to build GraphQL resolvers with managed GraphQL  
servers
Get Started for Free Contact Us
Unlike self-hosted open source GraphQL options such as Apollo Server, managed GraphQL solutions
provide a way to shift many operational concerns, and reorient efforts toward organizational use cases.
With a managed GraphQL API service like AWS AppSync, the infrastructure, patching, scaling, availability,
scalability, and other operational activities are managed by AWS.

AWS AppSync provides a serverless GraphQL API service with optimized resolvers to connect to AWS data
sources, compute, and others.

Connecting to Amazon DynamoDB with AWS AppSync


A schema can be used to auto-generate GraphQL resolvers, which in AWS AppSync are called Unit
Resolvers. These provide a direct resolver connection to DynamoDB. The resolvers execute actions
against the database, thereby providing a singular response and request mapping template using Apache
Velocity Template Language (VTL). This takes the request as inputs, and then outputs a JSON document
with instructions for the GraphQL resolver. To learn more about Unit Resolvers, work through the How to
Configure Resolvers Developer Guide.

For more information on VTL use in AWS AppSync, see the Resolver Mapping Template Programming
Guide.

If multiple operations need to be executed against one of multiple data sources in a single client request,
the Unit Resolver has to change to a Pipeline Resolver. These Pipeline Resolver operations are executed in
order against selected data source(s). Furthermore, functions can be created to execute during these
operations. This opens up a wide range of sequential operational options during the execution of
esolvers. For further information, see the Pipeline Resolvers Tutorial.

Connecting to Amazon RDS and Aurora with AWS AppSync


AWS AppSync also provides resolver options using Apache VTL (Velocity Template Language) to connect
to Aurora Serverless. For example, an insert of data, with the respective return data for the GraphQL
response, would look like the following. This code would be added to the request mapping template.

JavaScript

1 #set($id=$utils.autoId())
2 {
3 "version": "2018-05-29",
4 "statements": [
5 "insert into Pets VALUES ('$id', '$ctx.args.input.type', $ct
6 "select * from Pets WHERE id = '$id'"
7 ]
8 }  

Get Started for Free Contact Us Copy

Then, for the response mapping template, the following code would complete the resolver:

JavaScript

1 $utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])

Copy

For an example of connecting resolvers to Aurora Serverless options, see this Aurora Serverless Tutorial.

Connecting to AWS Lambda with AWS AppSync


The Lambda resolvers can fulfill nearly any remaining need for your GraphQL API. These resolvers provide
a direct way for the AWS AppSync service to call Lambda functions. This can be done by connecting to
stand alone RDS Database Servers, Amazon Neptune,
Amazon Kinesis, or any number of other sources of processing or data storage.

This Lambda Resolvers Tutorial demonstrates the power of building GraphQL resolvers for AWS AppSync
with Lambda functions. The switch is used to handle one of several types of actions in the Lambda for
each of the AWS AppSync resolvers.

JavaScript

1 exports.handler = (event, context, callback) => {


2 // Other code to handle invoke and getting/retrieving/adding pos
3 switch(event.field) {
4 case "getPost":
5 var id = event.arguments.id;
6 callback(null, posts[id]);
7 break;
8 case "allPosts":
9 var values = [];
10 for(var d in posts){
11 values.push(posts[d]);
12 }
13 callback(null, values);
14 break;
15 case "addPost":
16 callback(null, event.arguments);
17 break;
18 case "addPostErrorWithData":  
19 var id = event.arguments.id;
20 Get Started var result = posts[id];
for Free Contact Us
21 result.errorMessage = 'Error with the mutation, data has
22 result.errorType = 'MUTATION_ERROR';
23 callback(null, result);
24 break;
25 case "relatedPosts":
26 var id = event.source.id;
27 callback(null, relatedPosts[id]);
28 break;
29 default:
30 callback("Unknown field, unable to resolve" + event.fiel
31 break;
32 }
33 // Other code to handle getting/retrieving/adding posts, errors,
34 };

Copy

Once that Lambda is available, then the resolver VTL would make calls that are minimal to pass in the
event, context, and related parameters. The request mapping template would look like the following:

JavaScript

1 {
2 "version": "2017-02-28",
3 "operation": "Invoke",
4 "payload": {
5 "field": "getPost",
6 "arguments": $utils.toJson($context.arguments)
7 }
8 }

Copy

And the response mapping template would look like this:

JavaScript

1 $utils.toJson($context.result)

Copy
Best practices for building resolvers with managed GraphQL AppSync
 

1. ResolverGet Started
level for Free
caching Contact Us

Turning on per-resolver caching provides resolver specific settings for arguments, source, and identity
maps on which to base the cache hits. This provides another level of caching beyond full request caching
that lets each resolver cache based on its specific data requests to
servers.

2. HTTP resolvers as HTTP actions

A popular capability to pair with GraphQL APIs is the ability to issue an HTTP action as a request, and then
build the response based on that action call. A HTTP Resolver can be used to accomplish this task based
on API needs. This can provide a means to connect to other APIs via whichever mechanism, such as
GraphQL, REST, or other options.

3. Avoiding VTL with Lambda resolvers

If a particular language or functionality is needed for an API, using Lambda resolvers introduces the option
for different support languages to connect with any type of data source. These include Amazon RDS and
Postgres or Aurora, and it will work with those results as needed. For more information, see Introducing
Direct Lambda Resolvers: AWS AppSync GraphQL APIs without VTL.

4. Resolver auto-generation with Amplify

To try AWS AppSync with little coding, AWS Amplify Studio or AWS Amplify CLI tooling are good options
are they auto-generate GraphQL APIs. This includes all VTL resolver code based on a schema. Additional
tooling with Amplify Studio provides a way to build out a schema graphically. Furthermore, it can draw
relationships with immediate schema and the ability to deploy the API with fully functional resolvers built.

5. Custom authentication with Lambda resolvers

There are many authentication options with AWS AppSync, including Amazon Cognito, OIDC, IAM, and API
Keys. Moreover, if a custom authentication option is needed, using Lambda Resolvers provides this option
to connect and authenticate, or authorize data consumers against the API.

Which GraphQL resolver development option to choose


Both self-hosted and managed GraphQL options provide extensive tooling to build out APIs. The choice
should be guided by desired outcome, efficiency, and long-time impact.

Category Self-hosted, open source GraphQL AWS AppSync managed GraphQL service
servers
 
Configuration Many of the secrets for connection, Secrets are managed across
Get Started for Free Contact Us
configuration of databases, and other environments and require minimal
criteria must be managed. This interaction from the developer. These
requires a secrets vault or other data sources in an AWS AppSync API are
system of keeping secrets, and all seamlessly integrated and the
configurations managed across developer can focus more on the model
environments. and organizational use case.

Data Full control over the exact response Control of the request and response
and request of data inbound and cycles with various tooling, and
outbound from the database. immediacy of generated query calls into
However, each function can become a the database. This provides a boost
costly development process. toward focusing on organizational use
cases.

Developer Can provide the most extensive Provides a faster on-ramp for the
flexibility around implementing code, deployment of GraphQL APIs, and
database, and models for GraphQL streamlines process. However, it will be
APIs. limited if more elaborate and complex
coding is needed.

Logging Must determine exactly what is AWS AppSync uses CloudWatch to


needed, then build out the solution, provide an easy way to turn on logging.
connecting it to the chosen GraphQL Tracing can also be turned on with AWS
server. X-Ray to bolster insight and information
in the system.

Cost Introduces a range of additional costs, Provides a singular line item based on
including servers, functions, individual usage only.
resources, possible additional staff,
and others.
Looking for a fully managed GraphQL service?  

Get Started for Free Contact Us

Explore AWS AppSync


AWS AppSync is an enterprise level, fully managed serverless GraphQL service with real-time data
synchronization and offline programming features. AppSync makes it easy to build data driven mobile and
web applications by securely handling all the application data management tasks such as real-time and
offline data access, data synchronization, and data manipulation across multiple data sources.

Read Quick Start documentation

Learn more about AWS AppSync

Resources for AWS Developers on AWS


Sign In to the Console
Getting Started Developer Center
Training and Certification SDKs & Tools
Learn About AWS
AWS Solutions Library .NET on AWS
What Is AWS?
Architecture Center Python on AWS
What Is Cloud Computing?
Product and Technical FAQs Java on AWS
AWS Inclusion, Diversity & Equity
Analyst Reports PHP on AWS
What Is DevOps?
AWS Partners JavaScript on AWS
What Is a Container?
What Is a Data Lake?
AWS Cloud Security

You might also like