Next.js + typescriptでサーバーサイドのバックグラウンド処理(メインループ)の実装
最近ラズパイをいじっていて、Web画面をNext.jsで実装しつつ、センサーやモーターを制御するサーバーのバックグラウンド処理を実装したくなりました。案外まとまった情報が無かったので載せてみます。(ラズパイには触れません)
server.ts と周辺の実装もtypescriptで行いつつ、ちゃんとホットリロードするようにします。
サーバーサイドのメインループを実装
Next.jsのカスタムサーバー機能を使えば、Webサービス立ち上げ後にメインループを実装します。必要な機能をインストールします。
npm i -S express
npm i -D @types/express ts-node nodemon
ts-node は typescript を自前でビルドするのに必要で、nodemonは実行中にファイル更新がかかった場合リスタートする機能を持っています。
まとまってるts-node-devと言う便利そうなものがあるようですがNext.jsと相性が悪いので今回は不採用。
server/index.ts を作成して以下の様に記述します。
import express, { Request, Response } from "express";
import next from "next";
import { Main } from "./main";
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const port = process.env.PORT || 3000;
async function main() {
try {
await app.prepare();
const server = express();
server.all("*", (req: Request, res: Response) => {
return handle(req, res);
});
server.listen(port, (err?: any) => {
if (err) throw err;
console.log(`> Ready on localhost:${port} - env ${process.env.NODE_ENV}`);
});
} catch (e) {
console.error(e);
process.exit(1);
}
const main = new Main();
const sleep = (ms:number) => new Promise((resolve) => setTimeout(resolve, ms));
// メインループ
while(true){
main.Frame();
await sleep(0);
}
}
main();
公式のカスタムサーバーと基本は同じですが、typescript対応と、末尾にメインループが入ってます。
メインループ本体は server/main.ts に以下の様に記入します。
export class Main{
constructor(){
// 初期化など
}
public Frame() {
// ロジックの実装
}
}
Next.jsはカスタムサーバーのtypescriptファイルをビルドしてくれないので、その部分のビルドは自分で組み込むために tsconfig.server.json ファイルを作成して以下の様に記述。
{
"extends": "./tsconfig.json", // tsconfig.jsonの設定を継承する
"compilerOptions": {
"module": "commonjs", // Next.jsとExpressの両方を連携させる
"outDir": "dist", // ビルドファイルの出力先
"noEmit": false // Next.jsはBebelを使用してTypeScriptをコンパイルするので、TSコンパイラはjsを出力しない
},
"include": ["server/**/*.ts"] // serverフォルダ以下をコンパイルさせる
}
実行中のファイル更新監視をするために nodemon.json を作成して以下の様に記述。
{
"watch": ["server"],
"exec": "ts-node --project tsconfig.server.json server/index.ts",
"ext": "js ts"
}
package.json の scripts を以下のように変更
"dev": "nodemon",
"build:next": "next build",
"build:server": "tsc --project tsconfig.server.json",
"start": "NODE_ENV=production node dist/index.js",
これで main.ts の Frame 内にconsole.logなど書いて実行すれば無限に出力されるはずです。