🐥

Serverless FrameworkでmonorepoのtRPCをデプロイする

2023/02/07に公開

前提

Yarn Workspacesを使ってMonorepo環境を構築し、Sub PackageのapiをServerless Frameworkを使ってAWS Lambdaにデプロイします。

.
├── apps
│   ├── api
│   │    ├── index.ts
│   │    └── serverless.yml
│   └── web
└── package.json

Monorepo環境でServerless Frameworkをデプロイする場合、一部のパッケージはルートディレクトリのnode_modulesにホイストされ、Serverless Frameworkの最終的な.zipファイルに含まれないという問題があります。

Serverless Plugin Monorepoを使うことで、Yarn Workspacesからホイスト機能を無効にせずにこの問題を解決することができます。

Serverless Frameworkのpluginsは上から順番にロードされるため、Serverless Plugin Monorepoはpluginsのできるだけ上の方に定義する方が良いでしょう。依存関係によりますが、ローカルのnode_modulesには存在するが成果物の.zipファイルには含まれず、Cannot find module '***'というエラーがAWS Lambdaで発生する場合があります。

Serverless Framework Load Order

Serverless FrameworkでtRPCの設定

エントリーポイントのindex.tsは以下のように記述しています。AWS LambdaでExpressのようなhttpをルーティングするためにはserverless-httpが必要です。

import express from 'express';
import * as trpcExpress from '@trpc/server/adapters/express';
import cors from 'cors';
import dotenv from 'dotenv';
import { createContext } from './trpc';
import serverless from 'serverless-http';

import { appRouter, AppRouter } from './router/_app';

const app = express();
app.use(cors());
dotenv.config();

const port = process.env.PORT || 8080;

app.use(
  '/trpc',
  trpcExpress.createExpressMiddleware({
    router: appRouter,
    createContext,
  })
);

app.get('/', (req, res) => {
  res.send('Hello World!!');
});

app.listen(port, () => {
  console.log(`server listening at http://localhost:${port}`);
});

export const handler = serverless(app);
export { AppRouter };

最終的なserverless.ymlは以下のようになります。
自分の環境ではnode_mosulesの.prismaがルートディレクトリに含まれてしまったため、Error: Cannot find module '.prisma/client/index'というエラーが出ました。そのためpatternsでルートディレクトリの.prismaフォルダを含めるようにしています。

service: trpc-lambda

plugins:
  - serverless-plugin-monorepo
  - serverless-dotenv-plugin
  - serverless-plugin-typescript
  - serverless-plugin-common-excludes
  - serverless-plugin-include-dependencies

provider:
  name: aws
  region: ap-northeast-1
  runtime: nodejs16.x

functions:
  trpc:
    handler: index.handler
    events:
      - http:
          method: ANY
          path: /{proxy+}
package:
  excludeDevDependencies: false
  patterns:
    - '!node_modules/prisma/libquery_engine-*'
    - 'node_modules/@prisma/client/**/*'
    - '!node_modules/@prisma/engines/**'
    - '../../node_modules/.prisma/**/*'
    - '!../../node_modules/.prisma/client/libquery_engine-*'
    - '../../node_modules/.prisma/client/libquery_engine-rhel-*'

参考

Serverless Framework - Plugins

Setup Monorepo via Yarn workspace for Serverless Framework and Expo with Typescript

Electron-Prisma Error: can not find module '.prisma/client'

Deployed appsync code can't find node_module even if present in the .serverless package

Discussion