Next.js(App Router)でHonoを使ってREST APIを実装する

Next.jsをベースにして、Honoを使ったAPIの作成方法を試してみました。ファイルベースのルーティングに不便さを感じたため、ルーティング部分だけHonoを使用する方法を試しました。Honoで作成したAPIはNext.jsのミドルウェア上で動かします。APIパスの定義は、Next.js側で処理するパスも含める必要があります。また、Next.jsのミドルウェアでパスを書き換えることもできます。参考にしたものはこちらです:https://nextjs.org/docs/app/building-your-application/routing/middleware、https://github.com/honojs/examples/blob/main/nextjs-stack/pages/api/%5B…route%5D.ts

広告ここから
広告ここまで

目次

    ちょっとしたデモアプリなどを作りたいときに、Hono / expressライクなメソッド・パス定義の方が都合がよかったので、Next.jsをベースとしつつ、APIだけHonoを使うパターンを試してみました。

    背景

    簡単なデモアプリやベンダーがリリースした新機能のテストアプリを作る際、「どのようなパス・メソッドを追加する必要があるか」を探りながら実装を進めることがあります。そしてこの模索を行う開発をするケースでは、Next.js App Routerのような「ファイルベースのルーティング」に不便さを感じることがありました。

    ファイルベースのルーティング自体は、どのファイルにどんな機能が入っているかなどが分かりやすくなるのでありがたい機能なのですが、勉強目的などの「どんなAPIを追加するかわからない状態」では、都度都度ファイルを作ることになるためすこし手間になることもあります。

    ということで、今回はルーティング部分だけHonoを使う方法を試しました。

    Next.jsのミドルウェアでHonoを動かす

    結論から書くと、Honoで作ったAPIはNext.jsのミドルウェア上で動かします。

    まずはHonoの書き方でAPIを用意しましょう。

    import { NextRequest } from "next/server";
    import { Hono } from "hono";
    const honoApp = new Hono()
    
    honoApp.get('/', c => {
        return c.text('hello')
    })
    honoApp.get('/test', async c => {
        return c.json({
            message: true
        })
    })

    作成したHonoのアプリは、./middleware.ts または /src/middleware.tsでNext.jsアプリに組み込みます。リクエスト内容に応じて呼び出すかどうかを判定することもできますが、今回はconfigmatcherを使って、特定のパスでのみこのミドルウェアを動かすようにしました。

    export async function middleware(req: NextRequest) {
        const honoResponse = await honoApp.fetch(req)
        return honoResponse
    }
    
    export const config = {
        matcher: ['/api', '/api/:path*'],
    }

    この構成にする場合、HonoのAPIパス定義は「Next.js側で処理するパス」も含める必要があります。例えば/apiから始まるリクエストをHonoに中継する場合、Hono側の実装でも、/apiから始まるパスを指定する必要があります。

    
    import { NextRequest } from "next/server";
    import { Hono } from "hono";
    const honoApp = new Hono()
    
    honoApp.get('/api', c => {
        return c.text('hello')
    })
    honoApp.get('/api/test', async c => {
        return c.json({
            message: true
        })
    })
    
    
    export async function middleware(req: NextRequest) {
        const honoResponse = await honoApp.fetch(req)
        return honoResponse
    }
    
    export const config = {
        matcher: ['/api', '/api/:path*'],
    }

    Next.jsのパスを無視したパス定義をできるようにする

    Honoにルーティングされてきた部分のパス定義だけを行いたい場合、Next.jsのミドルウェアでパスを書き換えることもできます。

    
    import { NextRequest } from "next/server";
    import { Hono } from "hono";
    const honoApp = new Hono()
    
    honoApp.get('/', c => {
        return c.text('hello')
    })
    honoApp.get('/test', async c => {
        return c.json({
            message: true
        })
    })
    
    
    export async function middleware(req: NextRequest) {
        const newUrl = req.url.endsWith('/api') ? req.url.replace(/api/, '') : req.url.replace(/api\//, '')
        const newRequest = new NextRequest(newUrl, req) 
        const honoResponse = await honoApp.fetch(newRequest)
        return honoResponse
    }
    
    export const config = {
        matcher: ['/api', '/api/:path*'],
    }

    どちらにするかは、書きやすさやパス定義の読みやすさなどで検討するのが良いかなと思います。

    参考にしたもの

    • https://nextjs.org/docs/app/building-your-application/routing/middleware
    • https://github.com/honojs/examples/blob/main/nextjs-stack/pages/api/%5B…route%5D.ts
    広告ここから
    広告ここまで
    Home
    Search
    Bookmark