0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

API Gateway(REST) + Apollo Serverを構築する際の注意点

Last updated at Posted at 2025-09-25

はじめに

元々はAPI Gatewayのv2(HTTP)とLambda(Apollo Server)を使用していましたが、API Gatewayのタイムアウト時間を延長するため、HTTP(v2) → REST(v1)に変更することになりました。

しかし、API GatewayをHTTP(v2)からREST(v1)に変更しただけだと、下記のエラーが発生します。

{"statusCode":400,"body":"Cannot read properties of undefined (reading 'http')"}

そもそもなぜエラーが発生するのか、どうすればREST(v1)を使用してApllo Serverを構築できるのかを解説していきます。

そもそもなぜエラーが発生するのか?

API GatewayからLambdaに送られる際のデータの形式を確認したところ、下記のようなデータが送られていました。

API Gateway v2(HTTP)のAPI GatewayからLambdaへのデータ例

http.json
{
    "version": "2.0",
    "routeKey": "POST /",
    "rawPath": "/",
    "rawQueryString": "",
    "headers": {
        "accept": "*/*",
        "accept-encoding": "gzip, deflate, br",
        "content-length": "162",
        "content-type": "application/json",
        "host": "host.com",
        "user-agent": "PostmanRuntime/7.43.0",
        "x-amzn-trace-id": "Root=1-67998c52-7abe39f40f8d84a393ce94e4",
        "x-forwarded-for": "192.168.1.1",
        "x-forwarded-port": "443",
        "x-forwarded-proto": "https"
    },
    "requestContext": {
        "accountId": "0000000000000",
        "apiId": "gir39gt5m8",
        "domainName": "host.com",
        "domainPrefix": "host",
        "http": {
            "method": "POST",
            "path": "/",
            "protocol": "HTTP/1.1",
            "sourceIp": "192.168.1.1",
            "userAgent": "PostmanRuntime/7.43.0"
        },
        "requestId": "FISa-dktjVIROEI=",
        "routeKey": "POST /",
        "stage": "$default",
        "time": "29/Jan/2025:02:02:58 +0000",
        "timeEpoch": 1738116178654
    },
    "body": "{\"query\":\"query {\\n    login(email: \\\"test@test.com\\\", password: \\\"password\\\") {\\n        common {\\n    statusCode\\n            message\\n        }\\n    }\\n}\"}",
    "isBase64Encoded": false
}

API Gateway v1(REST)のAPI GatewayからLambdaへのデータ例

rest.json
{
    "body": {
        "query": "query {\n    login(email: \"test@test.com\", password: \"password\") {\n        common {\n        statusCode\n            message\n        }\n    }\n}"
    },
    "method": "POST",
    "principalId": "",
    "stage": "dev",
    "cognitoPoolClaims": {
        "sub": ""
    },
    "enhancedAuthContext": {},
    "headers": {
        "accept": "*/*",
        "accept-encoding": "gzip, deflate, br",
        "content-length": "162",
        "content-type": "application/json",
        "host": "host.com",
        "user-agent": "PostmanRuntime/7.43.0",
        "x-amzn-trace-id": "Root=1-67998c52-7abe39f40f8d84a393ce94e4",
        "x-forwarded-for": "192.168.1.1",
        "x-forwarded-port": "443",
        "x-forwarded-proto": "https"
    },
    "query": {},
    "path": {},
    "identity": {
        "cognitoIdentityPoolId": "",
        "accountId": "",
        "cognitoIdentityId": "",
        "caller": "",
        "sourceIp": "192.168.1.1",
        "principalOrgId": "",
        "accessKey": "",
        "cognitoAuthenticationType": "",
        "cognitoAuthenticationProvider": "",
        "userArn": "",
        "userAgent": "PostmanRuntime/7.43.0",
        "user": ""
    },
    "stageVariables": {},
    "requestPath": "/"
}

上記の内容を見ると、v2(HTTP)の方はhttpというオブジェクトデータが存在しますが、v1(REST)の方にはそのオブジェクトが存在しないため、最初のエラーが出力されていました。

対応方法

では、どうすればv1(REST)とApollo Serverをつなげるか?ですが、下記の対応が必要になります。

  • ApolloServerのheaderの関数を変更
    • headerの設定をcreateAPIGatewayProxyEventRequestHandler()に変更
  • API Gatewayのリクエストにマッピングテンプレートを作成する
  • API Gatewayのレスポンスにマッピングテンプレートを作成する

ApolloServerのheaderの関数を変更

v2(HTTP)の場合、index.jsは下記のようにcreateAPIGatewayProxyEventV2RequestHandler()を使用して構築を行います。

createAPIGatewayProxyEventV2RequestHandler()

v1(REST)の場合
それに対し、v1(REST)の場合はcreateAPIGatewayProxyEventRequestHandler()を使用します。

createAPIGatewayProxyEventRequestHandler()

API Gatewayのリクエストにマッピングテンプレートを作成する

API Gatewayの画面で対象のリソースを選択し、「統合リクエスト」→「編集」→「マッピングテンプレート」から下記のマッピングテンプレートを追加します

application/json

コンテンツタイプ:application/json
テンプレート本文

#define( $loop )
{
 #foreach($key in $map.keySet())
   #set( $k = $util.escapeJavaScript($key) )
   #set( $v = $util.escapeJavaScript($map.get($key)).replaceAll("\\'", "'") )
   "$k":
     "$v"
     #if( $foreach.hasNext ) , #end
 #end
}
#end

{
 "body": "$util.escapeJavaScript($input.json('$'))",
  "httpMethod": "$context.httpMethod",
  "method": "$context.httpMethod",
 "principalId": "$context.authorizer.principalId",
 "stage": "$context.stage",

  "cognitoPoolClaims" : {

    "sub": "$context.authorizer.claims.sub"
  },

 #set( $map = $context.authorizer )
  "enhancedAuthContext": $loop,

  #set( $map = $input.params().header )
  "headers": $loop,

 #set( $map = $input.params().querystring )
  "query": $loop,

  #set( $map = $input.params().path )
  "path": $loop,

  #set( $map = $context.identity )
  "identity": $loop,

  #set( $map = $stageVariables )
  "stageVariables": $loop,

  "requestPath": "$context.resourcePath"
}

application/x-www-form-urlencoded

コンテンツタイプ:application/x-www-form-urlencoded
テンプレート本文

#define( $body )
  {
    #foreach( $token in $input.path('$').split('&') )
     #set( $keyVal = $token.split('=') )
     #set( $keyValSize = $keyVal.size() )
     #if( $keyValSize >= 1 )
      #set( $key = $util.escapeJavaScript($util.urlDecode($keyVal[0])) )
      #if( $keyValSize >= 2 )
       #set($val = $util.escapeJavaScript($util.urlDecode($keyVal[1])).replaceAll("\\'","'"))
      #else
       #set( $val = '' )
      #end
      "$key": "$val"#if($foreach.hasNext),#end
     #end
#end
}
#end

#define( $loop )
{
  #foreach($key in $map.keySet())
    #set( $k = $util.escapeJavaScript($key) )
    #set( $v = $util.escapeJavaScript($map.get($key)).replaceAll("\\'", "'") )
    "$k":
      "$v"
      #if( $foreach.hasNext ) , #end
  #end
}
#end

{
"body": $body,
"method": "$context.httpMethod",
"principalId": "$context.authorizer.principalId",
"stage": "$context.stage",

"cognitoPoolClaims" : {
  "sub": "$context.authorizer.claims.sub"
},

#set( $map = $context.authorizer )
"enhancedAuthContext": $loop,

#set( $map = $input.params().header )
"headers": $loop,

#set( $map = $input.params().querystring )
"query": $loop,

#set( $map = $input.params().path )
"path": $loop,

#set( $map = $context.identity )
"identity": $loop,

#set( $map = $stageVariables )
"stageVariables": $loop,

"requestPath": "$context.resourcePath"
}

API Gatewayのレスポンスにマッピングテンプレートを作成する

API Gatewayの画面で対象のリソースを選択し、「統合レスポンス」の全てのレスポンスに下記の設定を追加します。

「編集」→「マッピングテンプレート」を選択し、下記の内容を設定する

コンテンツタイプ:application/json
テンプレート本文

$input.path('$.body')

上記の対応後、API Gatewayのデプロイを行い、GraphQLへのリクエストを確認してみてください
問題なければ、GraphQLのリクエストが可能になっています。

参考にした資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?