TypeScriptで書いたLambdaをAPI GatewayとInvokeどちらからでも使えるようにする

「なんのこっちゃら」という方や「えぇ・・・」って思われる方もいるかも知れませんが、今の自分には必要だったのです。 やりたいこと 1つのLambdaをAPI Gateway経由と直接Invokeどちらからも動せる実装。 問 […]

広告ここから
広告ここまで

目次

    「なんのこっちゃら」という方や「えぇ・・・」って思われる方もいるかも知れませんが、今の自分には必要だったのです。

    やりたいこと

    1つのLambdaをAPI Gateway経由と直接Invokeどちらからも動せる実装。

    問題

    TypeScriptでeventの型をどうするか。

    対応

    型のあるAPI Gateway Proxy EventにInvoke側を寄せていきます。

    コード

    まずInvokeしてもAPI Gatewayからコールしても同じ型のイベントオブジェクトを手に入れられるようにします。

    import { APIGatewayProxyEvent } from 'aws-lambda';
    
    export interface PayloadType {
      method: 'GET' | 'OPTION' | 'POST' | 'DELETE' | 'PUT',
      path: string,
      body?: any,
      pathParameters?: {
        [name: string]: string
      },
      queryStringParameters?: {
        [name: string]: string
      },
      resource: string
    }
    
    export const generateEvent = (payload: PayloadType): APIGatewayProxyEvent => {
      const {
        method,
        body,
        path,
        pathParameters,
        queryStringParameters,
        resource
      } = payload
      const event: APIGatewayProxyEvent = {
        body: body ? JSON.stringify(body) : null,
        headers: {},
        multiValueHeaders: {},
        httpMethod: method,
        isBase64Encoded: false,
        path,
        pathParameters: pathParameters || null,
        queryStringParameters: queryStringParameters || null,
        multiValueQueryStringParameters: {},
        stageVariables: {},
        requestContext: {
          accountId: 'accountId',
          apiId: 'apiId',
          connectedAt: 9999,
          httpMethod: method,
          identity: {
            accessKey: null,
            accountId: null,
            apiKey: null,
            apiKeyId: null,
            caller: null,
            cognitoAuthenticationProvider: null,
            cognitoAuthenticationType: null,
            cognitoIdentityId: null,
            cognitoIdentityPoolId: null,
            sourceIp: 'sourcecIp',
            user: null,
            userAgent: null,
            userArn: null
          },
          path,
          stage: 'stage',
          requestId: 'requestId',
          requestTimeEpoch: 9999,
          resourceId: 'resourceId',
          resourcePath: resource
        },
        resource
      }
      return event
    }
    
    export const isDirectInvoke = <T extends PayloadType>(event: any): event is T => {
      return event.httpMethod === undefined
    }
    
    export const getAPIProxyEvent = <T extends PayloadType>(event: APIGatewayProxyEvent | T): APIGatewayProxyEvent => {
      if (isDirectInvoke<T>(event)) return generateEvent(event)
      return event
    }

    これをハンドラー側でよびましょう。

    import { APIGatewayProxyHandler, APIGatewayProxyEvent } from 'aws-lambda';
    import { generateEvent, PayloadType, getAPIProxyEvent } from './apiProxyHelpers'
    
    interface NewPayload extends PayloadType {
      body: {}
    }
    
    export const handler:APIGatewayProxyHandler = async (proxyEvent: APIGatewayProxyEvent | NewPayload) => {
      const event = getAPIProxyEvent<CFPayload>(proxyEvent)
      return {
        statusCode: 200,
        body: JSON.stringify({event})
      }
    }

    これでhandler内ではeventはAPIGatewayProxyEventとして取り扱えますし、直接InvokeしてもAPI Gateway側を想定した実装のまま使えます。

    実際のところ

    asとかanyとか使っちゃえばいい気はします。好みとどれくらい頑張ろうかというモチベーションに相談してください。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark