"Middleware" for serverless functions

Dearest Community

I have an increasing amount of serverless functions, most of them requiring auth. I found myself repeating the following code to check if the user is authenticated in each of the functions:

 const { user } = context.clientContext;

  if (!user) {
    return {
      statusCode: 401,
      body: JSON.stringify({ ok: false, msg: "Unauthorized" }),
    };
  }

as you can see, I’m just returning a 401 error with some json if there is no user in the clientContext - no rocket science going on here.

I was wondering and searching for a “nice-ish” way to call this piece of code like a middleware, but all my search results pointed me in the direction of using express with serverless…which, to my understanding, is not really the point of serverless “functions” right!?!

Anyway - I stumbled across the MDN documentation on Proxy (handler.apply() - JavaScript | MDN) which led me to come up with something like this:

const decline_orders = async (event, context) => {
   return {
     statusCode: 200,
     body: JSON.stringify({
       ok: true,
       data: "bonjour",
    }),
   };
  }

  const handler = {
    apply: (target, thisArg, argumentsList) => {
        const { user } = argumentsList[1].clientContext
        if (!user) {
            return {
                statusCode: 401,
                body: JSON.stringify({ ok: false, msg: "Unauthorized" }),
            };
        }

        return target(argumentsList[0], argumentsList[1])
     }
  }

  const useAuth = new Proxy(decline_orders, handler)
  exports.handler = (event, context) => useAuth(event, context)

Now, the two questions I have are, 1) am I going to be disowned by the JS/Netlify community for attempting such a thing and 2) is there a simpler way (apart from calling a function inside every serverless-function) to achieve this nice feeling of middleware encapsulation.

Any help, advice or feedback is greatly appreciated

Cheers, Ben

UPDATE

Here’s an usage example (/.netlify/functions/middleware/auth.js):

const withAuth = func => (a, b) => {
const handler = {
       apply: (target, thisArg, args) => {
          const { user } = args[1].clientContext
          if (!user) {
             return {
            statusCode: 401,
            body: JSON.stringify({ ok: false, msg: "Unauthorized" }),
        };
    }

    return target(args[0], args[1])
   }
}

  const proxy = new Proxy(func, handler)

  return proxy.apply(this, [a, b])
}

module.exports = withAuth;

and can be used like this (/.netlify/functions/foo.js):

const withAuth = require("./middleware/auth")

const decline_orders = async (event, context) => {
    return {
        statusCode: 200,
        body: JSON.stringify({
          ok: true,
          data: "bonjour",
        }),
      };
}

exports.handler = (event, context) => withAuth(decline_orders)(event, context);
  1. We will love you always :kissing_heart:

  2. Your middleware pattern is correct while using Netlify functions. For more information on serverless function auth strategies check out GitHub - DavidWells/serverless-auth-strategies: How to handle authentication with serverless functions

There is a package call middy GitHub - middyjs/middy: 🛵 The stylish Node.js middleware engine for AWS Lambda 🛵 that works as a middleware layer for functions. An example of a middy as auth middleware can be seen here: https://github.com/netlify-labs/movie-app/blob/master/functions/middleware/auth.js

2 Likes

I know this is an old thread, but I thought I’d chime in with a solution that I use in 2025 (some things have changed since 2020). This is written in Typescript and uses the Functions 2.0 structure without an external dependency. It also attempts to avoid the “pyramid of doom” with nested callbacks.

In a server/utils.ts file, I have a requestPipeline method:

type V2Function = import(‘@netlify/serverless-functions-api/dist/api’).V2Function[‘default’]

type V2Middleware = (
  next: V2Function
) => V2Function

/** 
 * Provide an array of handlers, the last one gives the final response, unless
 * the middleware catches an error and responds with that.
 */
export const requestPipeline = (handlers: […V2Middleware, V2Function]) =>
  (handlers.slice(0, -1) as V2Middleware).reduceRight(
    (next, mw) => mw(next),
    handlers.at(-1) as V2Function
  )

Then to use it in a Netlify Function 2.0, it becomes straightforward:

// Example: .netlify/functions/protected
const isPostReq: V2Middleware = next => async (req, context) => {
  if (req.method === 'POST') {
    return next(req, context)
  }
  return new Response('only POST requests supported', { status: 404 })
}

const userCheck: V2Middleware = next => async (req, context) => {
  const user = req.headers.get('auth-user-email')
  if (user && allowedUsers.includes(user)) {
    return next(req, context)
  }
  return new Response('userCheck failed' , { status: 404 })
}

const handler: V2Function = async (req, context) => {
  // Everything Checks OK
  // Do your main stuff here!
  const data = await getSomeData();
  return new Resonse(data, { status: 200 })
}

// Specify the pipeline. if any of the checks before `handler`
// fail, that handler will never run.
export default requestPipeline([
  isPostReq, // Middleware
  userCheck, // Middleware
  handler    // Final Response
]);

This can also be solved using Edge Functions: Edge Functions overview | Netlify Docs.

1 Like