Lambda 関数から GraphQL API を呼び出す
Node.js アプリまたは Lambda 関数から AppSync GraphQL API を呼び出すことができます。基本的な Todo アプリを例として考えます:
type Todo @model @auth(rules: [{ allow: public }]) { name: String description: String}この API には Query、Mutation、Subscription の操作が利用可能です。Node.js を使用して Lambda 関数からクエリとミューテーションの両方を実行する方法を見てみましょう。
Lambda 関数テンプレートの利用 (IAM 認可)
まず、amplify add function で Lambda 関数を作成し、AppSync - GraphQL API request (with IAM) を選択して開始します。CLI がプロジェクト内の他のリソースへのアクセスを許可するよう促した場合は、GraphQL API へのアクセスを許可してください。または、ゼロから作成することもできます。
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 <YOUR_API_NAME> Query, Mutation, Subscription
You can access the following resource attributes as environment variables from your Lambda function API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT API_<YOUR_API_NAME>_GRAPHQLAPIIDOUTPUT API_<YOUR_API_NAME>_GRAPHQLAPIKEYOUTPUT ENV REGIONゼロから作成
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 <YOUR_API_NAME> Query, Mutation, Subscription
You can access the following resource attributes as environment variables from your Lambda function API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT API_<YOUR_API_NAME>_GRAPHQLAPIIDOUTPUT API_<YOUR_API_NAME>_GRAPHQLAPIKEYOUTPUT ENV REGIONこのページの例では、node-fetch を使用して GraphQL API への HTTP リクエストを作成します。Node.js v18 ランタイムが Lambda 用にリリースされると、この依存関係は ネイティブ fetch に置き換えることができます。開始するには、node-fetch モジュールを依存関係として追加します:
CommonJS:
CommonJS を使用して記述された関数の場合、node-fetch のバージョン 2 をインストールする必要があります
{ "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:
{ "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" }}クエリ
API キーを使用してリクエストを認証すると、GraphQL API をクエリして、すべての Todo のリストを取得できます。リスト クエリにページネーションを行うには、listTodos クエリに limit と nextToken を渡す必要があります。詳細は GraphQL ページネーション を参照してください。
import { default as fetch, Request } from 'node-fetch';
const GRAPHQL_ENDPOINT = process.env.API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT;const GRAPHQL_API_KEY = process.env.API_<YOUR_API_NAME>_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) };};ミューテーション
この例では、変数を引数として渡して Todo レコードを作成する方法を示すミューテーションを作成します。
import { default as fetch, Request } from 'node-fetch';
const GRAPHQL_ENDPOINT = process.env.API_<YOUR_API_NAME>_GRAPHQLAPIENDPOINTOUTPUT;const GRAPHQL_API_KEY = process.env.API_<YOUR_API_NAME>_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 認可
iam 認可を使用する別のスキーマ例を見てみましょう。
type Todo @model @auth(rules: [{ allow: private, provider: iam }]) { name: String description: String}CLI は Lambda 実行 IAM ロールを自動的に構成して GraphQL API を呼び出します。Lambda 関数を記述する前に、適切な AWS SDK v3 依存関係をインストールする必要があります:
{ "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" }}その後、以下の例は IAM 認可を使用して GraphQL API を呼び出すリクエストに署名します。
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_<YOUR_API_NAME>_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
CommonJS で関数を記述する場合、node-fetch のバージョン 2 をインストールする必要があります:
{ "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" }}上記の例と同様に、ハンドラーを記述できます。ここでの違いは import ... from ではなく require() を使用することです。
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_ < YOUR_API_NAME > _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) };};