ITの隊長のブログ

ITの隊長のブログです。Rubyを使って仕事しています。最近も色々やっているお(^ω^ = ^ω^)

Serverless Frameworkのaws-nodejs-typescriptでCORSが設定されたAPIを用意する

ドキュメント読んだら色々わかるんですが、 aws-nodejs-typescript のテンプレートでどうやって設定すればよいのかわからなかったのでメモ。

APIのfunctions設定で、CORSの設定をonにしたい関数だけ下記設定をします。

aws-nodejs-typescript のテンプレートでは、 src/functions/ 以下にAPIごとにディレクトリが作成できるので、 src/functions/hello/index.ts で関数の設定が編集できます。

import schema from './schema';
import { handlerPath } from '@libs/handler-resolver';

export default {
  handler: `${handlerPath(__dirname)}/handler.main`,
  events: [
    {
      http: {
        method: 'post',
        path: 'hello',
        request: {
          schemas: {
            'application/json': schema,
          },
        cors: true
        },
      },
    },
  ],
};

cors: true にしたらおk

次は、関数自体をいじります。

import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway';
import { formatJSONResponse } from '@libs/api-gateway';
import { middyfy } from '@libs/lambda';

import schema from './schema';

const hello: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event) => {
  return formatJSONResponse({
    message: `Hello ${event.body.name}, welcome to the exciting Serverless world!`,
    event,
  });
};

export const main = middyfy(hello);

返却するレスポンスを作ってくれる formatJSONResponse をいじります。こいつの実装に飛んでみると

import type { APIGatewayProxyEvent, APIGatewayProxyResult, Handler } from "aws-lambda"
import type { FromSchema } from "json-schema-to-ts";

type ValidatedAPIGatewayProxyEvent<S> = Omit<APIGatewayProxyEvent, 'body'> & { body: FromSchema<S> }
export type ValidatedEventAPIGatewayProxyEvent<S> = Handler<ValidatedAPIGatewayProxyEvent<S>, APIGatewayProxyResult>

export const formatJSONResponse = (response: Record<string, unknown>) => {
  return {
    statusCode: 200,
    body: JSON.stringify(response)
  }
}

こうなっておりました。Lambda統合プロキシが設定されているので、Lambda側で返してあげるように修正します。

import type { APIGatewayProxyEvent, APIGatewayProxyResult, Handler } from "aws-lambda"
import type { FromSchema } from "json-schema-to-ts";

type ValidatedAPIGatewayProxyEvent<S> = Omit<APIGatewayProxyEvent, 'body'> & { body: FromSchema<S> }
export type ValidatedEventAPIGatewayProxyEvent<S> = Handler<ValidatedAPIGatewayProxyEvent<S>, APIGatewayProxyResult>

export const formatJSONResponse = (response: Record<string, unknown>) => {
  return {
    statusCode: 200,
    body: JSON.stringify(response),
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Credentials': true,
    },
  }
}

こんな感じ(引数でheadersを渡せるようにしても良いと思う)

これで設定はおkです。実際運用する場合は、 Access-Control-Allow-Origin をちゃんと設定するとかしないと行けないと思います。

Serverless Frameworkのaws-nodejs-typescriptでHTTP GET methodを用意する

GitHub検索したらあるんだけど、Googleではあんまりヒットしないなぜ?

TypeScript力が低すぎて、いまいち何が書いてあるのかわからないのですが、とりあえずできたのでメモ。

github.com

import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway';
import { formatJSONResponse } from '@libs/api-gateway';
import { middyfy } from '@libs/lambda';

const hello: ValidatedEventAPIGatewayProxyEvent<void> = async () => {
  return formatJSONResponse({
    message: `Hello. welcome to the porn Serverless world!`
  });
};

export const main = middyfy(hello);

テンプレ通りだと、schemaが必要なんだけど、 ValidatedEventAPIGatewayProxyEvent の型?をvoidにすることでschemaを削除することができました。これだけ。

ServerlessFrameworkでHello world.

すべてはここにおいてきた。

github.com

で、へろーわーるどしようと叩いてみたけどエラーで失敗するなぜ???

root@00231e897e28:/app/backend-prj# sls invoke local -f hello
Running "serverless" from node_modules
{
    "errorMessage": "Cannot read properties of undefined (reading 'name')",
    "errorType": "TypeError",
    "stackTrace": [
        "TypeError: Cannot read properties of undefined (reading 'name')",
        "    at hello (/app/backend-prj/src/functions/hello/handler.ts:9:34)",
        "    at runRequest (/app/backend-prj/node_modules/@middy/core/index.js:86:32)",
        "    at processTicksAndRejections (node:internal/process/task_queues:96:5)"
    ]
}
root@00231e897e28:/app/backend-prj# sls invoke local -f hello --data '{"name":"aipa"}'
Running "serverless" from node_modules
{
    "errorMessage": "Cannot read properties of undefined (reading 'name')",
    "errorType": "TypeError",
    "stackTrace": [
        "TypeError: Cannot read properties of undefined (reading 'name')",
        "    at hello (/app/backend-prj/src/functions/hello/handler.ts:9:34)",
        "    at runRequest (/app/backend-prj/node_modules/@middy/core/index.js:86:32)",
        "    at processTicksAndRejections (node:internal/process/task_queues:96:5)"
    ]
}
root@00231e897e28:/app/backend-prj# sls invoke local -f hello --path src/functions/hello/mock.json
Running "serverless" from node_modules
{
    "statusCode": 200,
    "body": "{\"message\":\"Hello Frederic, welcome to the exciting Serverless world!\",\"event\":{\"headers\":{\"Content-Type\":\"application/json\"},\"body\":{\"name\":\"Frederic\"},\"rawBody\":\"{\\\"name\\\": \\\"Frederic\\\"}\"}}"
}

色々叩いたけど、requestの構造が間違ってて、mock.json使ったら行けましたとさ(便利)

Nuxtでの実装について

フロントエンドの実装がなんもわからん状態になっていました

ぼくはReactを触ったことがないので、あくまでVueしか語れないですが(正直Vueもそんな語れる気がしない)、もともとjQueryでSPA開発をしたことがある僕が、Component志向なフレームワークを触ってから便利ー!!感動!!!的なことを思ったのですが、ここ最近はComponentむずい〜ってなっておりました。

主に理由は下記

  • ビジネスロジックどこに書いていいのかわからん問題 → Plugin???
  • Storeに絡む処理は全部Vuexに!!→Vuexが太る
  • 用意したPluginをComponentから呼ぼう!あ、あのUIも必要だ。このUIにはAという処理が必要だから〜
    • Aという処理はBというComponentからも呼びたい。UIと密結合なこいつをどうやって分離すれば・・・?
  • Component内でやること大杉内。fat Componentだ!!どうすれば。。。
    • APIを叩くVuex Actionを叩く。mountedでレンダリングが完了後にJavaScript APIを呼んでほげほげ。ComponentのUIと密結合な処理をmethodsやcomputedに追加していく、、、etc

大体個人的に思っていることなので、まとまってもなく且つ本当に問題なのかがわからないですが、僕はこのあたりが難しいなーと感じているところでした。

これらを解消するために練習したリポジトリが下記です。

github.com

やったこと

この3つだけでもだいぶスッキリかけるようになりました。

あとこのリポジトリが勉強になる。

github.com

また、スッキリ書くための学びだったけど、ここから

あたりを勉強したいと思っている。

Nuxt2でViteを使う

ビルドが速いらしいので使ってみた

vite.nuxtjs.org

この記事参考にした(インストールはこれで大丈夫)

zenn.dev

ローカルにいれてれば特に問題ないと思うが、ぼくはDockerを使ってたので謎の挙動が発生した(Nuxtアプリをブラウザで開くとhot reloadの無限ループ)

今回バージョンは違うけどこの記事が参考になりました。

qiita.com

開発側だけの問題っぽい。ちなみにポートを変更したい場合は、 nuxt.config.js に下記設定を追記したらよい

  vite: {
    server: {
      hmr: {
        protocol: 'ws',
        port: 81,
      },
    },
  },

あとはDockerでportを紐付けるだけ。一旦これでおk