"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 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/apply) 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 https://github.com/DavidWells/serverless-auth-strategies

There is a package call middy https://github.com/middyjs/middy 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