Search code examples
next.jsmiddlewareblitz.js

Writing Blitz js middleware to return plain text for a url endpoint


I have a Next.js app I am trying to migrate to Blitz.js.

It's a programming related site and some of the site's routes are intended to let the user run a script with something like cURL or wget piped through their shell interpreter.

These routes need to meet these two criteria:

  1. Return the response as text/plain.
  2. Read query params and adjust the data based on those params.

In Next.js I wrote middleware in pages/_middleware.js that looks like the following (and it worked). I don't understand how to port this to Blitz.js. I read the middleware page, but I don't understand where to put it in the application.

Next.js implementation

// _middleware.js
import { NextResponse, NextRequest } from 'next/server'

/**
 * This simple example just reroutes requests to /script
 * to a no-op script.
 */
export async function middleware(req, ev) {
  const { pathname } = req.nextUrl
  if (pathname == '/script') {
    let version = parseInt(req.nextUrl.searchParams.get('v'));

    if (isNaN(version)) {
      version = 'latest';
    }   
    
    const params = !version ? '' : `?version=${version}`;
    return NextResponse.rewrite(`/api/basic-script${params}`);
  }
  return NextResponse.next();
}
// pages/api/basic-script.js
export default function handler (req, res) {
  let {version} = req.query;
  version = parseInt(version);

  if (isNaN(version)) {
    version = latest;
  }

  res.status(200).send(
    'echo "This script doesn't do anything yet. (version: ${version})"
  );
}

Blitz.js implementation

Adding the API endpoint works as expected, but I don't know where to put the middleware (or how to write it in Blitz).

It looks to me like adding a middleware property to the config in blitz.config.js isn't the right solution to this, since that would just redirect me to a new page.

I tried adding the same _middleware.js file in Blitz's app/pages/, but it didn't seem to be loading.


Solution

  • I had the same issue and was able to resolve with the following:

    // blitz.config.ts
    import { BlitzConfig, connectMiddleware } from "blitz"
    import middlewareHandler from "app/pages/middleware" // your middleware file
    
    const config: BlitzConfig = {
      middleware: [
        connectMiddleware(middlewareHandler),
      ],
    }
    
    module.exports = config
    
    // app/pages/middleware.ts
    import { BlitzApiRequest, BlitzApiResponse } from "blitz"
    import Cors from "cors"
    
    // Initializing the cors middleware
    const cors = Cors({
      methods: ["GET", "POST", "HEAD", "OPTIONS"],
    })
    
    // Helper method to wait for a middleware to execute before continuing
    // And to throw an error when an error happens in a middleware
    function runMiddleware(req, res, fn) {
      return new Promise((resolve, reject) => {
        fn(req, res, (result) => {
          if (result instanceof Error) {
            return reject(result)
          }
    
          return resolve(result)
        })
      })
    }
    
    async function handler(req: BlitzApiRequest, res: BlitzApiResponse) {
      // Run the middleware
      return await runMiddleware(req, res, cors)
    }
    
    export default handler