Page updated Jan 17, 2024

Connect to data from server-side runtimes

This guide walks through how you can connect to the Amplify GraphQL API from any server-side runtimes. For Next.js applications, Amplify provides first-class support for App Router (React Server Components, Route Handlers, and Server Actions) and Pages Router (Components, API Routes), and Middleware. Review Connect to GraphQL API from AWS Lambda if need to call the Amplify GraphQL API from a Node.js AWS Lambda function.

Connect to GraphQL API from a Next.js server runtime

Before you begin, you will need:

Step 1 - Choose the correct GraphQL API client for Next.js server runtimes

Amplify offers two specialized GraphQL API clients for Next.js server runtimes (from @aws-amplify/adapter-nextjs/api) that you should use depending whether you retrieve the user tokens using cookies or NextRequest and NextResponse:

  • generateServerClientUsingCookies() 🍪 generates an API client with the Next.js' cookies function from next/headers. Each API request dynamically refetches the cookies at runtime.
  • generateServerClientUsingReqRes() 🌐 generates an API client requiring NextRequest and NextResponse to provided to an runWithAmplifyServerContext function to prevent token contamination.

Choose the correct GraphQL API client based on your Next.js Router (App or Pages) and then their use case:

Use caseRequired GraphQL client
React Server ComponentgenerateServerClientUsingCookies() 🍪
Server ActionsgenerateServerClientUsingCookies() 🍪
Route HandlergenerateServerClientUsingCookies() 🍪
MiddlewaregenerateServerClientUsingReqRes() 🌐

Pages Router

Use caseRequired GraphQL client
Server-side component codegenerateServerClientUsingReqRes() 🌐
API RoutegenerateServerClientUsingReqRes() 🌐
MiddlewaregenerateServerClientUsingReqRes() 🌐

Step 2 - Generate the GraphQL API client for Next.js server runtimes

To generate a GraphQL API client for the Next.js server runtime using cookies, you need to provide both your Amplify configuration and the cookies function from Next.js.

1import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
2import amplifyConfig from '@/amplifyconfiguration.json';
3import { cookies } from 'next/headers';
4
5export const cookieBasedClient = generateServerClientUsingCookies({
6 config: amplifyConfig,
7 cookies
8});

We recommend to generate the server API client in a utility file. Then, import the generated client in your Next.js React Server Components, Server Actions, or Route Handlers.

To generate a GraphQL API client for the Next.js server runtime using NextRequest and NextResponse, you need to only provide your Amplify configuration. When making the individual API requests, you'll need to pass it into an runWithAmplifyServerContext function to pass in the cookies from request and response variables.

1import { createServerRunner } from '@aws-amplify/adapter-nextjs';
2import { generateServerClientUsingReqRes } from '@aws-amplify/adapter-nextjs/api';
3import amplifyConfig from '@/amplifyconfiguration.json';
4
5export const { runWithAmplifyServerContext } = createServerRunner({
6 config: amplifyConfig
7});
8
9export const reqResBasedClient = generateServerClientUsingReqRes({
10 config: amplifyConfig
11});

We recommend to generate the server API client in a utility file. Then, import the generated client in your Next.js Middleware, component's server runtime code, and API Routes.

Step 3 - Call GraphQL API using generated server API clients

With the generated server API clients, you can make any GraphQL query or mutation request. Subscriptions are not available within server runtimes.

Import the cookie-based GraphQL API's server client in your Next.js React Server Component code and make your GraphQL.

1import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
2import amplifyConfig from '@/amplifyconfiguration.json'
3import { cookies } from 'next/headers'
4import { listTodos } from '@/graphql/queries'
5
6export const cookieBasedClient = generateServerClientUsingCookies({
7 config: amplifyConfig,
8 cookies
9})
10
11async fetchTodos() {
12 const request = await cookieBasedClient.graphql({
13 query: listTodos
14 })
15
16 return request.data.listTodos.items
17}

Import the NextRequest/NextResponse-based GraphQL API's server client in your Next.js server runtime code and make your GraphQL within the runWithAmplifyServerContext function. Review Server-side Rendering to learn more about creating an Amplify server context.

For example, in a Next.js Pages Router API route, use the req and res parameters from the handler function with runWithAmplifyServerContext:

1import type { NextApiRequest, NextApiResponse } from 'next'
2import type { Todo } from '@/API'
3import { runWithAmplifyServerContext, reqResBasedClient } from '@/utils/amplifyServerUtils'
4import { listTodos } from '@/graphql/queries'
5
6type ResponseData = {
7 todos: Todo[]
8}
9
10export default async function handler(
11 req: NextApiRequest,
12 res: NextApiResponse<ResponseData>
13) {
14 const todos = await runWithAmplifyServerContext({
15 nextServerContext: { req, res}
16 operation: async (contextSpec) => {
17 const request = await reqResBasedClient.graphql(contextSpec, {
18 query: listTodos
19 })
20
21 return request.data.listTodos.items
22 }
23 })
24 res.status(200).json({ todos })
25}

Connect to GraphQL API from AWS Lambda

You can call an AppSync GraphQL API from a Node.js app or a Lambda function. Take a basic Todo app as an example:

1type Todo @model @auth(rules: [{ allow: public }]) {
2 name: String
3 description: String
4}

This API will have operations available for Query, Mutation, and Subscription. Let's take a look at how to perform both a query as well as a mutation from a Lambda function using Node.js.

Utilizing Lambda function template (IAM authorization)

First, create a Lambda function with amplify add function and choose the AppSync - GraphQL API request (with IAM) to get started. Be sure to grant access to your GraphQL API when prompted by the CLI to grant access to other resources in the project. Alternatively, you can create the function from scratch.

1amplify add function
2? Select which capability you want to add: Lambda function (serverless function)
3? Provide an AWS Lambda function name: myfunction
4? Choose the runtime that you want to use: NodeJS
5? Choose the function template that you want to use: AppSync - GraphQL API request (with IAM)
6
7Available advanced settings:
8- Resource access permissions
9- Scheduled recurring invocation
10- Lambda layers configuration
11- Environment variables configuration
12- Secret values configuration
13
14? Do you want to configure advanced settings? Yes
15? Do you want to access other resources in this project from your Lambda function? Yes
16? Select the categories you want this function to have access to. api
17? Select the operations you want to permit on <YOUR_API_NAME> Query, Mutation, Subscription
18
19You can access the following resource attributes as environment variables from your Lambda function
20 API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT
21 API_<YOUR_API_NAME>_GRAPHQLAPIIDOUTPUT
22 API_<YOUR_API_NAME>_GRAPHQLAPIKEYOUTPUT
23 ENV
24 REGION

The function can only be added when the GraphQL API with IAM authorization exists.

Create from scratch

1amplify add function
2? Select which capability you want to add: Lambda function (serverless function)
3? Provide an AWS Lambda function name: myfunction
4? Choose the runtime that you want to use: NodeJS
5? Choose the function template that you want to use: Hello World
6
7Available advanced settings:
8- Resource access permissions
9- Scheduled recurring invocation
10- Lambda layers configuration
11- Environment variables configuration
12- Secret values configuration
13
14? Do you want to configure advanced settings? Yes
15? Do you want to access other resources in this project from your Lambda function? Yes
16? Select the categories you want this function to have access to. api
17? Select the operations you want to permit on <YOUR_API_NAME> Query, Mutation, Subscription
18
19You can access the following resource attributes as environment variables from your Lambda function
20 API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT
21 API_<YOUR_API_NAME>_GRAPHQLAPIIDOUTPUT
22 API_<YOUR_API_NAME>_GRAPHQLAPIKEYOUTPUT
23 ENV
24 REGION

The examples on this page use node-fetch to make a HTTP request to your GraphQL API. When the Node.js v18 runtime is released for Lambda this dependency can be removed in favor of native fetch To get started, add the node-fetch module as a dependency:

CommonJS:

For functions written using CommonJS, you will need to install version 2 of node-fetch

1{
2 "name": "myfunction",
3 "version": "2.0.0",
4 "description": "Lambda function generated by Amplify",
5 "main": "index.js",
6 "license": "Apache-2.0",
7+ "dependencies": {
8+ "node-fetch": "2"
9+ },
10 "devDependencies": {
11 "@types/aws-lambda": "^8.10.92"
12 }
13}

ESM:

1{
2 "name": "myfunction",
3+ "type": "module",
4 "version": "2.0.0",
5 "description": "Lambda function generated by Amplify",
6 "main": "index.js",
7 "license": "Apache-2.0",
8+ "dependencies": {
9+ "node-fetch": "^3.2.3"
10+ },
11 "devDependencies": {
12 "@types/aws-lambda": "^8.10.92"
13 }
14}

Query

Using an API Key for authenticating your requests, you can query the GraphQL API to get a list of all Todos. To paginate over the list queries, you need to pass in a limit and nextToken on the listTodos query. See more at GraphQL pagination .

1import { default as fetch, Request } from 'node-fetch';
2
3const GRAPHQL_ENDPOINT = process.env.API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT;
4const GRAPHQL_API_KEY = process.env.API_<YOUR_API_NAME>_GRAPHQLAPIKEYOUTPUT;
5
6const query = /* GraphQL */ `
7 query LIST_TODOS {
8 listTodos {
9 items {
10 id
11 name
12 description
13 }
14 }
15 }
16`;
17
18/**
19 * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
20 */
21export const handler = async (event) => {
22 console.log(`EVENT: ${JSON.stringify(event)}`);
23
24 /** @type {import('node-fetch').RequestInit} */
25 const options = {
26 method: 'POST',
27 headers: {
28 'x-api-key': GRAPHQL_API_KEY,
29 'Content-Type': 'application/json'
30 },
31 body: JSON.stringify({ query })
32 };
33
34 const request = new Request(GRAPHQL_ENDPOINT, options);
35
36 let statusCode = 200;
37 let body;
38 let response;
39
40 try {
41 response = await fetch(request);
42 body = await response.json();
43 if (body.errors) statusCode = 400;
44 } catch (error) {
45 statusCode = 400;
46 body = {
47 errors: [
48 {
49 status: response.status,
50 message: error.message,
51 stack: error.stack
52 }
53 ]
54 };
55 }
56
57 return {
58 statusCode,
59 body: JSON.stringify(body)
60 };
61};

Mutation

In this example you will create a mutation showing how to pass in variables as arguments to create a Todo record.

1import { default as fetch, Request } from 'node-fetch';
2
3const GRAPHQL_ENDPOINT = process.env.API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT;
4const GRAPHQL_API_KEY = process.env.API_<YOUR_API_NAME>_GRAPHQLAPIKEYOUTPUT;
5
6const query = /* GraphQL */ `
7 mutation CREATE_TODO($input: CreateTodoInput!) {
8 createTodo(input: $input) {
9 id
10 name
11 createdAt
12 }
13 }
14`;
15
16/**
17 * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
18 */
19export const handler = async (event) => {
20 console.log(`EVENT: ${JSON.stringify(event)}`);
21
22 const variables = {
23 input: {
24 name: 'Hello, Todo!'
25 }
26 };
27
28 /** @type {import('node-fetch').RequestInit} */
29 const options = {
30 method: 'POST',
31 headers: {
32 'x-api-key': GRAPHQL_API_KEY,
33 'Content-Type': 'application/json'
34 },
35 body: JSON.stringify({ query, variables })
36 };
37
38 const request = new Request(GRAPHQL_ENDPOINT, options);
39
40 let statusCode = 200;
41 let body;
42 let response;
43
44 try {
45 response = await fetch(request);
46 body = await response.json();
47 if (body.errors) statusCode = 400;
48 } catch (error) {
49 statusCode = 400;
50 body = {
51 errors: [
52 {
53 status: response.status,
54 message: error.message,
55 stack: error.stack
56 }
57 ]
58 };
59 }
60
61 return {
62 statusCode,
63 body: JSON.stringify(body)
64 };
65};

IAM Authorization

(TK authorization rules from Lambda)

Let's take a look at another example schema that uses iam authorization.

1type Todo @model @auth(rules: [{ allow: private, provider: iam }]) {
2 name: String
3 description: String
4}

The CLI will automatically configure the Lambda execution IAM role to call the GraphQL API. Before writing your Lambda function you will first need to install the appropriate AWS SDK v3 dependencies:

1{
2 "name": "myfunction",
3+ "type": "module",
4 "version": "2.0.0",
5 "description": "Lambda function generated by Amplify",
6 "main": "index.js",
7 "license": "Apache-2.0",
8+ "dependencies": {
9+ "@aws-crypto/sha256-js": "^2.0.1",
10+ "@aws-sdk/credential-provider-node": "^3.76.0",
11+ "@aws-sdk/protocol-http": "^3.58.0",
12+ "@aws-sdk/signature-v4": "^3.58.0",
13+ "node-fetch": "^3.2.3"
14+ },
15 "devDependencies": {
16 "@types/aws-lambda": "^8.10.92"
17 }
18}

Then, the following example will sign the request to call the GraphQL API using IAM authorization.

1import crypto from '@aws-crypto/sha256-js';
2import { defaultProvider } from '@aws-sdk/credential-provider-node';
3import { SignatureV4 } from '@aws-sdk/signature-v4';
4import { HttpRequest } from '@aws-sdk/protocol-http';
5import { default as fetch, Request } from 'node-fetch';
6
7const { Sha256 } = crypto;
8const GRAPHQL_ENDPOINT = process.env.API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT;
9const AWS_REGION = process.env.AWS_REGION || 'us-east-1';
10
11const query = /* GraphQL */ `
12 query LIST_TODOS {
13 listTodos {
14 items {
15 id
16 name
17 description
18 }
19 }
20 }
21`;
22
23/**
24 * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
25 */
26export const handler = async (event) => {
27 console.log(`EVENT: ${JSON.stringify(event)}`);
28
29 const endpoint = new URL(GRAPHQL_ENDPOINT);
30
31 const signer = new SignatureV4({
32 credentials: defaultProvider(),
33 region: AWS_REGION,
34 service: 'appsync',
35 sha256: Sha256
36 });
37
38 const requestToBeSigned = new HttpRequest({
39 method: 'POST',
40 headers: {
41 'Content-Type': 'application/json',
42 host: endpoint.host
43 },
44 hostname: endpoint.host,
45 body: JSON.stringify({ query }),
46 path: endpoint.pathname
47 });
48
49 const signed = await signer.sign(requestToBeSigned);
50 const request = new Request(GRAPHQL_ENDPOINT, signed);
51
52 let statusCode = 200;
53 let body;
54 let response;
55
56 try {
57 response = await fetch(request);
58 body = await response.json();
59 if (body.errors) statusCode = 400;
60 } catch (error) {
61 statusCode = 500;
62 body = {
63 errors: [
64 {
65 message: error.message
66 }
67 ]
68 };
69 }
70
71 return {
72 statusCode,
73 body: JSON.stringify(body)
74 };
75};

CommonJS

When writing functions with CommonJS, you will need to install version 2 of node-fetch:

1{
2 "name": "myfunction",
3 "version": "2.0.0",
4 "description": "Lambda function generated by Amplify",
5 "main": "index.js",
6 "license": "Apache-2.0",
7+ "dependencies": {
8+ "@aws-crypto/sha256-js": "^2.0.1",
9+ "@aws-sdk/credential-provider-node": "^3.76.0",
10+ "@aws-sdk/protocol-http": "^3.58.0",
11+ "@aws-sdk/signature-v4": "^3.58.0",
12+ "node-fetch": "2"
13+ },
14 "devDependencies": {
15 "@types/aws-lambda": "^8.10.92"
16 }
17}

Similar to the example above you can now write your handler. The difference here is the use of require() rather than import ... from

1const { Sha256 } = require('@aws-crypto/sha256-js');
2const { defaultProvider } = require('@aws-sdk/credential-provider-node');
3const { SignatureV4 } = require('@aws-sdk/signature-v4');
4const { HttpRequest } = require('@aws-sdk/protocol-http');
5const { default: fetch, Request } = require('node-fetch');
6
7const GRAPHQL_ENDPOINT =
8 process.env.API_ < YOUR_API_NAME > _GRAPHQLAPIENDPOINTOUTPUT;
9const AWS_REGION = process.env.AWS_REGION || 'us-east-1';
10
11const query = /* GraphQL */ `
12 query LIST_TODOS {
13 listTodos {
14 items {
15 id
16 name
17 description
18 }
19 }
20 }
21`;
22
23/**
24 * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
25 */
26exports.handler = async (event) => {
27 console.log(`EVENT: ${JSON.stringify(event)}`);
28
29 const endpoint = new URL(GRAPHQL_ENDPOINT);
30
31 const signer = new SignatureV4({
32 credentials: defaultProvider(),
33 region: AWS_REGION,
34 service: 'appsync',
35 sha256: Sha256
36 });
37
38 const requestToBeSigned = new HttpRequest({
39 method: 'POST',
40 headers: {
41 'Content-Type': 'application/json',
42 host: endpoint.host
43 },
44 hostname: endpoint.host,
45 body: JSON.stringify({ query }),
46 path: endpoint.pathname
47 });
48
49 const signed = await signer.sign(requestToBeSigned);
50 const request = new Request(GRAPHQL_ENDPOINT, signed);
51
52 let statusCode = 200;
53 let body;
54 let response;
55
56 try {
57 response = await fetch(request);
58 body = await response.json();
59 if (body.errors) statusCode = 400;
60 } catch (error) {
61 statusCode = 500;
62 body = {
63 errors: [
64 {
65 message: error.message
66 }
67 ]
68 };
69 }
70
71 return {
72 statusCode,
73 body: JSON.stringify(body)
74 };
75};