Open17

Amplify - Lambda Functionで外部RestAPIを叩いてGraphQLで返してみる

minamikminamik

外部のRestAPIをLambda経由で加工してAppSyncのGraphQLで取得する試み。
Amplifyの初期設定は終わっているものとする。

minamikminamik

とりあえずGraphQL APIを追加する。
名前はtestApi。

% amplify add api
 ? Select from one of the below mentioned services: GraphQL
 ? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue
 ? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description) 
...
 GraphQL schema compiled successfully.
...
 ✅ Successfully added resource viteproject locally

Lambda Functionを追加する。
名前はgetExternalApi。

% amplify add function
? Select which capability you want to add: Lambda function (serverless function)
? Provide an AWS Lambda function name: getExternalApi
? 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? No
? Do you want to edit the local lambda function now? No
Successfully added resource testFunc locally.
minamikminamik

/amplify/backend/api/testApi/schema.graphql に以下を追加

// /amplify/backend/api/testApi/schema.graphql
type Query {
  externalApi: AWSJSON @function(name: "getExternalApi-${env}")
}
minamikminamik

/amplify/backend/function/getExternalApi/src 以下にtsconfig.jsonを追加

{
  "compilerOptions": {
    /* Language and Environment */
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    /* Modules */
    "module": "commonjs",                                /* Specify what module code is generated. */
    /* Interop Constraints */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    /* Completeness */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  "include": [
    "index.ts",
    "src/**/*.ts"
  ],
  "exclude": []
}
minamikminamik

以下をインストール

% cd amplify/backend/function/getExternalApi/src
% yarn add axios @types/aws-lambda typescript
minamikminamik

テンプレートのjsをtsに。

mv index.js index.ts

index.ts の書き換え

import axios from 'axios'

export const handler = async (event: any) => {
    console.log(`EVENT: ${JSON.stringify(event)}`)
    const response = await axios({
        method:'GET',
        url:'https://api.example.com/xxx', // 外部APIのエンドポイント
        headers:{
            'Content-Type': 'application/json',
            'X-API-KEY': 'XXXXX' // テストだから直指定。実際は必ず環境変数に入れるようにすること。
        }
    })

    return {
        statusCode: response.data.statusCode,
        body: response.data,
    }
}
minamikminamik

/package.json に以下を追記

  "scripts": {
    "lambdaUpdate": "cd amplify/backend/function/getExternalApi/src && npx tsc && cd -"
  },

tsコンパイル

% yarn lambdaUpdate
yarn run v1.22.18
$ cd amplify/backend/function/getExternalApi/src && npx tsc && cd -
...
✨  Done in 0.73s.
minamikminamik

フロントエンド(Vue3&Vuetify)から叩いてみる。

<script setup lang="ts">
import { externalApi } from '../graphql/queries'

const myJson = ref({})

const getExternalApi = async () => {
  const response = await API.graphql({
      query: externalApi
    })
  const result = JSON.parse(response.data.externalApi)
  console.log(result)
  myJson.value = result
}
</scirpt>
<template>
  <v-container>
    <h2 >External API Test</h2>
    <v-btn block color="secondary" v-on:click="getExternalApi">Get</v-btn>
    <p>{{ myJson }}</p>
  </v-container>
</template>

minamikminamik

GraphQLから引数を受けられるようにする

minamikminamik

schemaの更新

// /amplify/backend/api/testApi/schema.graphql
type Query {
  externalApi(myId: String): AWSJSON @function(name: "getExternalApi-${env}")
}

functionの更新

// /amplify/backend/test-project/functions/getExternalApi/src/index.ts
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from "axios"

 export const handler = async (event: any) => {
    console.log(`EVENT: ${JSON.stringify(event)}`)

    const id = event.arguments.myId
    const url = process.env.API_URL + "/" + id

    const requestParam: AxiosRequestConfig = {
        method: "GET",
        url: url || "",
        headers: {
            'Content-Type': 'application/json',
            'x-bintu-apikey': process.env.API_KEY || "",    
        },
      }
    const response = await axios(requestParam)

    return {
        statusCode: response.data.statusCode,
        body: response.data,
    }
}

minamikminamik

frontendの更新

<script setup lang="ts">
import { externalApi } from '../graphql/queries'

const myJson = ref({})
const id = ref('xxx-xxx-xxx')

const getExternalApi = async () => {
  const response = await API.graphql({
      query: externalApi,
      variables: {
        myId: id.value
      }
    })
  const result = JSON.parse(response.data.externalApi)
  console.log(result)
  myJson.value = result
}
</scirpt>
<template>
  <v-container>
    <h2 >External API Test</h2>
    <v-text-field v-model="id" label="id"></v-text-field>
    <v-btn block color="secondary" v-on:click="getExternalApi">Get</v-btn>
    <p>{{ myJson }}</p>
  </v-container>
</template>

minamikminamik

外部APIのAPIキーを環境変数に保持するようにする

minamikminamik

functionの設定変更で、環境変数を入れる

% amplify update function
? Select the Lambda function you want to update getExternalApi
General information
- Name: getExternalApi
- Runtime: nodejs

Resource access permission
- test-project (Query)

Scheduled recurring invocation
- Not configured

Lambda layers
- Not configured

Environment variables:
- Not configured

Secrets configuration
- Not configured

? Which setting do you want to update? Environment variables configuration
? Enter the environment variable name: APIKEY
? Enter the environment variable value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
? Select what you want to do with environment variables: I'm done
? Do you want to edit the local lambda function now? No

Lambda内でprocess.env使おうとしたら、なんか型エラーで怒られたので、色々直していく

// /amplify/backend/test-project/functions/getExternalApi/src/index.ts
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from "axios"

 export const handler = async (event: any) => {
    console.log(`EVENT: ${JSON.stringify(event)}`)

    const requestParam: AxiosRequestConfig = {
        method: "GET",
        url: process.env.API_URL || "",
        headers: {
            'Content-Type': 'application/json',
            'x-bintu-apikey': process.env.API_KEY || "",    
        },
      }
    const response = await axios(requestParam)

    return {
        statusCode: response.data.statusCode,
        body: response.data,
    }
}