You can call an AppSync GraphQL API from a Node.js app or a Lambda function. Take a basic `Todo` app as an example: ```graphql type Todo @model @auth(rules: [{ allow: public }]) { name: String description: String } ``` 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](#create-from-scratch). ```console amplify add function ? Select which capability you want to add: Lambda function (serverless function) ? Provide an AWS Lambda function name: myfunction ? Choose the runtime that you want to use: NodeJS ? Choose the function template that you want to use: AppSync - GraphQL API request (with IAM) Available advanced settings: - Resource access permissions - Scheduled recurring invocation - Lambda layers configuration - Environment variables configuration - Secret values configuration ? Do you want to configure advanced settings? Yes ? Do you want to access other resources in this project from your Lambda function? Yes ? Select the categories you want this function to have access to. api ? Select the operations you want to permit on Query, Mutation, Subscription You can access the following resource attributes as environment variables from your Lambda function API__GRAPHQLAPIENDPOINTOUTPUT API__GRAPHQLAPIIDOUTPUT API__GRAPHQLAPIKEYOUTPUT ENV REGION ``` The function can only be added when the GraphQL API with IAM authorization exists. ### Create from scratch ```console amplify add function ? Select which capability you want to add: Lambda function (serverless function) ? Provide an AWS Lambda function name: myfunction ? Choose the runtime that you want to use: NodeJS ? Choose the function template that you want to use: Hello World Available advanced settings: - Resource access permissions - Scheduled recurring invocation - Lambda layers configuration - Environment variables configuration - Secret values configuration ? Do you want to configure advanced settings? Yes ? Do you want to access other resources in this project from your Lambda function? Yes ? Select the categories you want this function to have access to. api ? Select the operations you want to permit on Query, Mutation, Subscription You can access the following resource attributes as environment variables from your Lambda function API__GRAPHQLAPIENDPOINTOUTPUT API__GRAPHQLAPIIDOUTPUT API__GRAPHQLAPIKEYOUTPUT ENV REGION ``` The examples on this page use [`node-fetch`](https://www.npmjs.com/package/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`](https://nodejs.org/dist/latest-v18.x/docs/api/globals.html#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` ```diff { "name": "myfunction", "version": "2.0.0", "description": "Lambda function generated by Amplify", "main": "index.js", "license": "Apache-2.0", + "dependencies": { + "node-fetch": "2" + }, "devDependencies": { "@types/aws-lambda": "^8.10.92" } } ``` **ESM**: ```diff { "name": "myfunction", + "type": "module", "version": "2.0.0", "description": "Lambda function generated by Amplify", "main": "index.js", "license": "Apache-2.0", + "dependencies": { + "node-fetch": "^3.2.3" + }, "devDependencies": { "@types/aws-lambda": "^8.10.92" } } ``` ## Query Using an API Key for authenticating your requests, you can query the GraphQL API to get a list of all `Todo`s. To paginate over the list queries, you need to pass in a `limit` and `nextToken` on the `listTodos` query. See more at [GraphQL pagination ](/guides/api-graphql/graphql-pagination). ```js import { default as fetch, Request } from 'node-fetch'; const GRAPHQL_ENDPOINT = process.env.API__GRAPHQLAPIENDPOINTOUTPUT; const GRAPHQL_API_KEY = process.env.API__GRAPHQLAPIKEYOUTPUT; const query = /* GraphQL */ ` query LIST_TODOS { listTodos { items { id name description } } } `; /** * @type {import('@types/aws-lambda').APIGatewayProxyHandler} */ export const handler = async (event) => { console.log(`EVENT: ${JSON.stringify(event)}`); /** @type {import('node-fetch').RequestInit} */ const options = { method: 'POST', headers: { 'x-api-key': GRAPHQL_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ query }) }; const request = new Request(GRAPHQL_ENDPOINT, options); let statusCode = 200; let body; let response; try { response = await fetch(request); body = await response.json(); if (body.errors) statusCode = 400; } catch (error) { statusCode = 400; body = { errors: [ { status: response.status, message: error.message, stack: error.stack } ] }; } return { statusCode, body: JSON.stringify(body) }; }; ``` ## Mutation In this example you will create a mutation showing how to pass in variables as arguments to create a `Todo` record. ```js import { default as fetch, Request } from 'node-fetch'; const GRAPHQL_ENDPOINT = process.env.API__GRAPHQLAPIENDPOINTOUTPUT; const GRAPHQL_API_KEY = process.env.API__GRAPHQLAPIKEYOUTPUT; const query = /* GraphQL */ ` mutation CREATE_TODO($input: CreateTodoInput!) { createTodo(input: $input) { id name createdAt } } `; /** * @type {import('@types/aws-lambda').APIGatewayProxyHandler} */ export const handler = async (event) => { console.log(`EVENT: ${JSON.stringify(event)}`); const variables = { input: { name: 'Hello, Todo!' } }; /** @type {import('node-fetch').RequestInit} */ const options = { method: 'POST', headers: { 'x-api-key': GRAPHQL_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }) }; const request = new Request(GRAPHQL_ENDPOINT, options); let statusCode = 200; let body; let response; try { response = await fetch(request); body = await response.json(); if (body.errors) statusCode = 400; } catch (error) { statusCode = 400; body = { errors: [ { status: response.status, message: error.message, stack: error.stack } ] }; } return { statusCode, body: JSON.stringify(body) }; }; ``` ## IAM Authorization Let's take a look at another example schema that uses `iam` authorization. ```graphql type Todo @model @auth(rules: [{ allow: private, provider: iam }]) { name: String description: String } ``` 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: ```diff { "name": "myfunction", + "type": "module", "version": "2.0.0", "description": "Lambda function generated by Amplify", "main": "index.js", "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^2.0.1", + "@aws-sdk/credential-provider-node": "^3.76.0", + "@aws-sdk/protocol-http": "^3.58.0", + "@aws-sdk/signature-v4": "^3.58.0", + "node-fetch": "^3.2.3" + }, "devDependencies": { "@types/aws-lambda": "^8.10.92" } } ``` Then, the following example will sign the request to call the GraphQL API using IAM authorization. ```js import crypto from '@aws-crypto/sha256-js'; import { defaultProvider } from '@aws-sdk/credential-provider-node'; import { SignatureV4 } from '@aws-sdk/signature-v4'; import { HttpRequest } from '@aws-sdk/protocol-http'; import { default as fetch, Request } from 'node-fetch'; const { Sha256 } = crypto; const GRAPHQL_ENDPOINT = process.env.API__GRAPHQLAPIENDPOINTOUTPUT; const AWS_REGION = process.env.AWS_REGION || 'us-east-1'; const query = /* GraphQL */ ` query LIST_TODOS { listTodos { items { id name description } } } `; /** * @type {import('@types/aws-lambda').APIGatewayProxyHandler} */ export const handler = async (event) => { console.log(`EVENT: ${JSON.stringify(event)}`); const endpoint = new URL(GRAPHQL_ENDPOINT); const signer = new SignatureV4({ credentials: defaultProvider(), region: AWS_REGION, service: 'appsync', sha256: Sha256 }); const requestToBeSigned = new HttpRequest({ method: 'POST', headers: { 'Content-Type': 'application/json', host: endpoint.host }, hostname: endpoint.host, body: JSON.stringify({ query }), path: endpoint.pathname }); const signed = await signer.sign(requestToBeSigned); const request = new Request(GRAPHQL_ENDPOINT, signed); let statusCode = 200; let body; let response; try { response = await fetch(request); body = await response.json(); if (body.errors) statusCode = 400; } catch (error) { statusCode = 500; body = { errors: [ { message: error.message } ] }; } return { statusCode, body: JSON.stringify(body) }; }; ``` ### CommonJS When writing functions with CommonJS, you will need to install version 2 of `node-fetch`: ```diff { "name": "myfunction", "version": "2.0.0", "description": "Lambda function generated by Amplify", "main": "index.js", "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^2.0.1", + "@aws-sdk/credential-provider-node": "^3.76.0", + "@aws-sdk/protocol-http": "^3.58.0", + "@aws-sdk/signature-v4": "^3.58.0", + "node-fetch": "2" + }, "devDependencies": { "@types/aws-lambda": "^8.10.92" } } ``` Similar to the example above you can now write your handler. The difference here is the use of `require()` rather than `import ... from` ```js const { Sha256 } = require("@aws-crypto/sha256-js"); const { defaultProvider } = require("@aws-sdk/credential-provider-node"); const { SignatureV4 } = require("@aws-sdk/signature-v4"); const { HttpRequest } = require("@aws-sdk/protocol-http"); const { default: fetch, Request } = require("node-fetch"); const GRAPHQL_ENDPOINT = process.env.API__GRAPHQLAPIENDPOINTOUTPUT; const AWS_REGION = process.env.AWS_REGION || "us-east-1"; const query = /* GraphQL */ ` query LIST_TODOS { listTodos { items { id name description } } } `; /** * @type {import('@types/aws-lambda').APIGatewayProxyHandler} */ exports.handler = async (event) => { console.log(`EVENT: ${JSON.stringify(event)}`); const endpoint = new URL(GRAPHQL_ENDPOINT); const signer = new SignatureV4({ credentials: defaultProvider(), region: AWS_REGION, service: "appsync", sha256: Sha256, }); const requestToBeSigned = new HttpRequest({ method: "POST", headers: { "Content-Type": "application/json", host: endpoint.host, }, hostname: endpoint.host, body: JSON.stringify({ query }), path: endpoint.pathname, }); const signed = await signer.sign(requestToBeSigned); const request = new Request(GRAPHQL_ENDPOINT, signed); let statusCode = 200; let body; let response; try { response = await fetch(request); body = await response.json(); if (body.errors) statusCode = 400; } catch (error) { statusCode = 500; body = { errors: [ { message: error.message, }, ], }; } return { statusCode, body: JSON.stringify(body), }; }; ```