📣

Remixが流行ってそうなので、AWS Amplify Hostingにデプロイしてみる

2024/03/28に公開

はじめに

こんにちは!
犬専用の音楽アプリ オトとりっぷでエンジニアしています、足立です!

https://www.oto-trip.com/

この記事では、Remix を AWS Amplify Hosting するための方法を解説しています。
「とりあえず結果だけくれ!」という方は、こちらにレポジトリを公開しておりますので、よろしければご覧ください。

https://github.com/ototrip-lab/remix-amplify-hosting

目次

  • 背景
    • Remix が流行ってる?
    • Amplify Hosting で SSR
  • 手順
    • Remix Init
    • Amplify Setting
    • Deploy
    • 追加修正

背景

Remix が流行ってる?

どうやら、Remix が流行る兆しがあるみたいです!
状況については、こちらの記事が非常にわかりやすいです。

https://zenn.dev/typebase_dev/articles/why-choose-remix

私は主に Next.js を利用しているので、Remix は初めてです。
せっかくなので流れに乗って Remix を調査してみよう、というのがこの記事の主旨です。

Amplify Hosting で SSR

Amplify Hosting で SSR 可能なフレームワークは、以前は Next.js のみ提供でしたが、昨年の 11 月に他のフレームワークでもようやく利用可能になりました。
Astro や Hono などのフレームワークについて、先人達が貴重な情報を残してくださっています。

https://www.m3tech.blog/entry/amplify-astro-ssr

https://gensobunya-tech.hatenablog.com/entry/2023/11/21/221416

そんな中、「よーし!Remix が流行ってそうなので、Amplify にもデプロイしてみるか!」と思ったら Remix に関する情報がどこにも出てなかったのでまとめてみました。
Remix に慣れていないので、間違いなどありましたらご指摘いただけると幸いです。

手順

Remix Init

まずは Remix をQuick Start (5m)で Init していきます。
Github 連携のために、すべての工程の前に空のレポジトリを作成しておいてください。

$ cd <root>
$ npm init -y
$ npm i @remix-run/node @remix-run/react @remix-run/serve isbot@4 react react-dom
$ npm i -D @remix-run/dev vite

次に vite の設定ファイルを作成します。

$ touch vite.config.js
vite.config.js
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [remix()],
});

次にエントリーファイルを作成します。

$ mkdir app
$ touch app/root.jsx
app/root.jsx
import { Links, Meta, Outlet, Scripts } from '@remix-run/react';

export default function App() {
  return (
    <html>
      <head>
        <link rel='icon' href='data:image/x-icon;base64,AA' />
        <Meta />
        <Links />
      </head>
      <body>
        <h1>Hello world!</h1>
        <Outlet />
        <Scripts />
      </body>
    </html>
  );
}

それではローカルでビルドしてスタートさせてみます。
remix-serve を動かすためにはpackage.jsonの一部書き換えが必要です。

package.json
{
  "type": "module"
  // ...
}
$ npx remix vite:build
$ npx remix-serve build/server/index.js

http://localhost:3000 にアクセスして Hello world! と表示されていれば成功です。

次にサーバ上で動かすための設定を追加していきます。
Quick Start では Express を使った例が出ているので、それに従います。

$ npm i express @remix-run/express cross-env
$ touch server.js
server.js
import { createRequestHandler } from '@remix-run/express';
import express from 'express';

// notice that the result of `remix vite:build` is "just a module"
import * as build from './build/server/index.js';

const app = express();
app.use(express.static('build/client'));

// and your app is "just a request handler"
app.all('*', createRequestHandler({ build }));

app.listen(3000, () => {
  console.log('App listening on http://localhost:3000');
});

では、サーバにアクセスしてみます。

$ node server.js

こちらでもデカデカとHello world! と表示されていれば成功です。

Amplify Setting

次に Amplify Hosting 用のセッティングを追加していきます。
Express サーバを Amplify にデプロイするための公式ドキュメントはこちらです。

https://docs.aws.amazon.com/amplify/latest/userguide/deploy-express-server.html

これに従って、各種設定を追加していきます。

まず、「Amplify deployment manifest」なるものを作成します。

$ touch deploy-manifest.json
deploy-manifest.json
{
  "version": 1,
  "framework": { "name": "express", "version": "4.18.2" },
  "imageSettings": {
    "sizes": [
      100,
      200,
      1920
    ],
    "domains": [],
    "remotePatterns": [],
    "formats": [],
    "minimumCacheTTL": 60,
    "dangerouslyAllowSVG": false
  },
  "routes": [
    {
      "path": "/_amplify/image",
      "target": {
        "kind": "ImageOptimization",
        "cacheControl": "public, max-age=3600, immutable"
      }
    },
    {
      "path": "/*.*",
      "target": {
        "kind": "Static",
        "cacheControl": "public, max-age=2"
      },
      "fallback": {
        "kind": "Compute",
        "src": "default"
      }
    },
    {
      "path": "/*",
      "target": {
        "kind": "Compute",
        "src": "default"
      }
    }
  ],
  "computeResources": [
    {
      "name": "default",
      "runtime": "nodejs18.x",
-      "entrypoint": "index.js"
+      "entrypoint": "server.js"
    }
  ]
}

公式ドキュメントからのコピペですが、一点だけエントリーファイルを変更しています。

次に「post-build script」なるものを追加します。
こちらはビルド後に必要なファイルを必要な場所に配置するためのスクリプトっぽいです。

$ mkdir bin
$ touch bin/postbuild.sh
bin/postbuild.sh
#!/bin/bash

rm -rf ./.amplify-hosting

mkdir -p ./.amplify-hosting/compute/default

cp -r ./build ./.amplify-hosting/compute/default/build
cp -r server.js ./.amplify-hosting/compute/default
cp -r ./node_modules ./.amplify-hosting/compute/default/node_modules

cp -r public ./.amplify-hosting/static

cp deploy-manifest.json ./.amplify-hosting/deploy-manifest.json

こちらも、ほとんど公式からのコピペですが、ビルド済みファイルの命名を変更し、server.js をコピーに含めるように修正しました。
このままでは public が存在しないと怒られるので、適当に Icon を追加しておきます。

$ mkdir public

最後にpackage.jsonを修正し、ビルド後に postbuild.sh が適応されるように修正します。

{
  ...
  "scripts": {
    "build": "remix vite:build",
    "dev": "remix vite:dev",
    "start": "remix-serve ./build/server/index.js",
    "postbuild": "chmod +x bin/postbuild.sh && ./bin/postbuild.sh"
  },
  ...
}

ビルドして、.amplify-hosting以下に必要なファイルが移動されていれば成功です。

$ npm run build

Deploy

デプロイしていきましょう。
詳細は、先達とほとんど同じなので、割愛します。
気を付けるポイントとしては、amplify.ymlを以下の通りに設定してください。

amplify.yml
version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm install
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: .amplify-hosting
    files:
      - '**/*'

デプロイ完了したら、https://main.<ID>.amplifyapp.com にアクセスしてみましょう!
きっと、このページは動作していませんとエラーページが返ってきます。あれ?

追加修正

原因を探っていきます。
AWS Amplify Hosting のマネージメントコンソール画面の左側サイドバーからモニタリングをクリックし、メイン画面の上部タブの中からホスティングしているコンピューティングログをクリックし、該当ブランチのCloudWatch ログストリームをクリックします。

そうすると、CloudWatch でサーバサイドのログが見れるので、何のエラーが発生してるのか確認します。

INIT_REPORT Init Duration: 192.25 ms Phase: init Status: error Error Type: Runtime.ExitError
LOGS Name: bootstrap State: Already subscribed Types: [Platform, Function]
(node:18) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/var/task/server.js:1
import { createRequestHandler } from '@remix-run/express';
^^^^^^
SyntaxError: Cannot use import statement outside a module

...

どうやら、@remix-run/expressが読み込めずエラーで落ちているみたいです。
こちらの指示通り、package.json に"type": "module"を追加します。

$ touch bin/package.json
bin/package.json
{
  "type": "module"
}
bin/postbuild.sh
#!/bin/bash

rm -rf ./.amplify-hosting

mkdir -p ./.amplify-hosting/compute/default

cp -r ./build ./.amplify-hosting/compute/default/build
cp -r server.js ./.amplify-hosting/compute/default
+ cp -r bin/package.json ./.amplify-hosting/compute/default
cp -r ./node_modules ./.amplify-hosting/compute/default/node_modules

cp -r public ./.amplify-hosting/static

cp deploy-manifest.json ./.amplify-hosting/deploy-manifest.json

追加の修正を Github に Push して、再度デプロイしてみます。
次はデカデカとHello world!が無事に表示されましたでしょうか?

最後に

いかがでしたでしょうか。
これはあくまで SSR できたよっていうことなので、サーバサイドでデータフェッチをするなど、もう少し深い利用シーンのやり方についても引き続き調査してみたいと思います。
また、Remix の方もの字ぐらいしか味わってないので、もっと触りたいですね。

もし犬専用の音楽アプリに興味を持っていただけたら、ぜひダウンロードしてみてください!

https://www.oto-trip.com/

Discussion