Next.js (with TypeScript)のサーバーをKoaで起動する

基本的にKoaやExpressなどを使う必要はないのですが、Shopify Appなどでお世話になりそうなので勉強がてら車輪の再開発してました。

進め方

Next.jsのKoa Exampleを目指して、自力で実装します。
なのでこのExampleをcloneする方が手取り早いです。
https://github.com/vercel/next.js/tree/canary/examples/custom-server-koa

セットアップ

TypeScript化はcreate-next-appでできるのでサッと立ち上げてしまいます。

$ yarn create next-app --typescript

ライブラリインストール

Koaライブラリと型定義、そしてサーバーを動かすためにts-nodeをインストールします。

$ yarn add koa koa-router ts-node
$ yarn add -D @types/koa @types/koa-router

サーバー向けtsconfigの作成

Next.jsでTypeScriptを使う場合、isolatedModulestrueにする必要があります。しかしこれを有効にしていると、サーバー側のJSをts-nodeで動かせないので、別途サーバー用のtsconfigを作成します。

tsconfig.server.json

{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "module": "commonjs",
        "isolatedModules": false
    },
    "include": ["server/**/*.ts"]
}

簡単なKoaサーバーのコードを実装

兎にも角にもまずはKoaでサーバーを動かしてみないと始まりません。Next.jsのコードと分けるために、server/index.tsとしてファイルを作成します。

server/index.ts

import Koa from 'koa'
import Router from 'koa-router'

const server = new Koa()
const router = new Router()

router.get('/', (ctx) => {
    ctx.body = 'hello'
})

server.use(router.routes())

server.listen(3100, () => {
    console.log('> Ready on http://localhost:3100')
})

Package.jsonにサーバー起動コマンドを設定

あとはts-nodeでサーバーを動かすだけです。オプションが長いので、npm-scriptとして定義してしまいましょう。

  "scripts": {
    "start:server": "yarn ts-node --project tsconfig.server.json server/index.ts",

これでyarn start:serverを実行するとサーバーが起動します。

% yarn start:server
 yarn run v1.22.10
 $ yarn ts-node --project tsconfig.server.json server/index.ts
 $ /Users/development/koa-typescript/node_modules/.bin/ts-node --project tsconfig.server.json server/index.ts
   Ready on http://localhost:3100 

cURLでhelloが帰ってくることを確認します。

$ curl http://localhost:3100/
 hello

Next.jsを組み込む

あとはNext.jsをKoaに載せるだけです。Exampleの実装をそのままserver/index.tsに載せましょう。

import Koa from 'koa'
import Router from 'koa-router'
import next from 'next'

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {

    const server = new Koa()
    const router = new Router()
    router.all('(.*)', async (ctx) => {
        await handle(ctx.req, ctx.res)
        ctx.respond = false
    })

    server.use(async (ctx, next) => {
        ctx.res.statusCode = 200
        await next()
    })

    server.use(router.routes())
    server.listen(3100, () => {
        console.log('> Ready on http://localhost:3100')
    })
})

これでブラウザからアクセスすると、Next.jsのスターター画面が表示されます。

終わりに

これだけだと正直やるメリットはあまりないです。ただ、ShopifyでNext.jsを使う場合にお世話になるので、内容理解の第一歩としてはちょうどよかったかなと思います。

参考

Comment