Enhancing traditional functions with Javascript annotations or decorators

Currently, I am in the process of developing a Next.js application, where I have defined an API as shown below:

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'GET') {
    fn1Get(req, res);
  } else if (req.method === 'POST') {
    fn1Post(req, res);
  } else {
    res.status(501).json({ operation: `${req.method}: not implemented` });
  }
}
async function fn1Get(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> {
  const authenticated = await checkAuth(req, res);
  if (authenticated) {
      // Fetch Data
      res.status(200).json({status: 'all good!'});
  }
}
async function fn1Post(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> {
  const authenticated = await checkAuth(req, res);
  if (authenticated) {
      // Submit Data
      res.status(201).json({status: 'all good!'});
  }
}
const checkAuth = async (req: NextApiRequest, res: NextApiResponse) => {
  const tokenValid = await extnernalApiCall(getToken(req));
  if (!tokenValid) {
    res.status(403).json({ error: 'Authentication Failed' });
  }
  return tokenValid
};

I am looking for a more efficient method to define authenticated functions, rather than including the line

const authenticated = await checkAuth(req, res);
within each function

In languages like Java or Python, decorators/annotations/AOP can be used for this purpose. Is there a similar approach in JavaScript? Perhaps by using function wrapping and/or bind/call/apply??

Conceptual example:

const checkAuth = async (fn) => {
  const req = arguments[1];
  const res = arguments[2];
  const tokenValid = await extnernalApiCall(getToken(req));
  if (!tokenValid) {
    res.status(403).json({ error: 'Authentication Failed' });
  }
  return fn(arguments);
}
async function fn1Get = checkAuth(_fn1Get(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> {
  const authenticated = await checkAuth(req, res);
  if (authenticated) {
      // Fetch Data
      res.status(200).json({status: 'all good!'});
  }
})

All the functions that require authentication will have the same parameters req and res (request and response). The authentication function also needs these parameters to extract the token from req and send a 403 error to res if not authenticated

The technologies being used include Next.js with React 17, TypeScript, and ECMAScript 6

Answer №1

Achieving this functionality can be done using a wrapper function, which is essentially what decorators are in essence. The wrapper function needs to return another function. An example of how this can be implemented is shown below (types may need to be adjusted accordingly):

const checkAuth = (fn) => {
  return async (req: NextApiRequest,res: NextApiResponse): Promise<void> => {
    const tokenValid = await extnernalApiCall(getToken(req));
    if (!tokenValid) {
      res.status(403).json({ error: 'Authentication Failed' });
    } else {
      fn(req, res);
    }
  }
}

const fn1Get = checkAuth((
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> => {
  // Get Stuff
  res.status(200).json({status: 'all right!'});
})

It is worth noting that if you are using next.js, there could be a mechanism in place to register middleware handlers that would automatically run on every request without the need to manually wrap each handler.

Answer №2

For authentication in my Next.js project, I utilize Next-auth along with a custom function to handle user sessions. If a user's session is not found, they are redirected to the sign-in page. If a session exists, the props are passed to the getServerSideProps function.

import { getSession } from "next-auth/react";

/* gssp =  */
export const requireAuth = (gssp) => {
  return async (ctx) => {
      const { req } = ctx;
      const session = await getSession({ req })
      if (!session) {
          return {
              redirect: { permanent: false, destination: '/api/auth/signin' }
          };
      };
      const ctxWithSession = { ...ctx, session };
      return await gssp(ctxWithSession);
  };
};

To implement this, I call the custom authentication function as an enhancement of the getServerSideProps function in my Next.js page:

export const getServerSideProps = requireAuth(async _ctx => {
    const { session } = _ctx;
    return {
        props: {
          session: session,  
        },
    };
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Transform an array containing objects into a single object

Currently exploring the Mapael Jquery plugin, which requires an object to draw map elements. In my PHP code, I am returning a JSON-encoded array of objects: [ { "Aveiro": { "latitude": 40.6443, "longitude": -8.6455, ...

Problematic Situation Arising from JavaScript AJAX and NVD3.JS Unresolved Object Error

I am currently in the process of developing a script that will allow me to retrieve data for my chart from an external PHP file. Initially, I attempted manually inputting the data into the chart and it worked perfectly fine. However, when I tried using A ...

Fixed position of Material UI tooltip popper when the word length increases

https://i.sstatic.net/mXcqy.png I am striving to replicate the image above by customizing the MUI tooltip properties, but instead, I am only able to achieve this result: https://i.sstatic.net/Rr79x.png By applying a margin to the popper element, I was a ...

Consecutive POST requests in Angular 4

Can you assist me with making sequential (synchronous) http POST calls that wait for the response from each call? generateDoc(project, Item, language, isDOCXFormat) : Observable<any> { return this.http.post(this.sessionStorageService.retriev ...

Avoiding the creation of a history entry while switching languages on a Next.js website

I'm currently developing a Next.js project that includes a language dropdown feature for users to choose their preferred language. In the existing setup, we are utilizing the router.push method from next/router to update the language selection and red ...

What is the best way to conceal the legend in a chart.js component within a React application?

I've been struggling to hide the legend on my Chart.js chart. Per the official documentation (https://www.chartjs.org/docs/latest/configuration/legend.html), hiding the legend requires setting the display property of the options.legend object to fals ...

Dealing with JWT management in the absence of Cookies

After doing some research on JSON Web Token (which is a new concept to me), I have learned about its secure mechanism for transmitting information between parties without the need for server Sessions. Currently, I am in the process of building a web app f ...

What could be the reason for the incorrect parsing of this JSON data?

Issue arising due to unparseable JSON in JavaScript and jQuery API. Here is the code snippet: JSON parsing issue in JavaScript: // Code sample: alert(data); // output is an object alert(data.horas[0].hora; // returns undefined The JSON structure: {"hor ...

Experimenting with a Jest test on an express middleware

I'm currently faced with a challenge in testing my controller (express middleware) using Jest. To better illustrate the issue, I will share the code snippet below: import request from 'utils/request'; import logger from 'config/logger& ...

Reactivity in Vue not responding when listening from a different object

Can someone help me understand why my Vue object is not reactive to changes in another object? See the code snippet below. exampleObject = { exampleProperty: null } exampleObject.update = function () { this.exampleProperty = 'updated data&apo ...

Caution: Material-UI in conjunction with Next.js and express is generating a warning message regarding the mismatch of the `className` prop between the server and client

Whenever I directly reload the /login and /account pages, issues arise. These two pages contain Material-UI components. https://i.sstatic.net/h7qww.png Additionally, here is a glimpse of my express server: Server.js ...

Preserving scroll position during page navigation in Next.js

Currently, I am working on a website using the Next.js boilerplate. Here is the routing code that I am using: import Link from 'next/link' <Link href={{ pathname: '/some-url', query: { param: something } }}> <div> ...

How to stop Backbone.js from changing the URL hash when navigating back and forth

I have been working on developing a simple Single Page Application (SPA) using Backbone.js. In my application, I am facing challenges with two specific routes: the index route ("/#index") and the menu route ("/#mainmenu"). The general flow of my app is as ...

Utilizing npm scripts to compress all HTML files within a directory, including its subdirectories

I am looking for a way to compress all the files with a .html extension in a particular folder, including any subfolders, using a script called npm run. Ideally, I would like the original .html files to be replaced with the minified versions, but creating ...

XML and numerous, distinct DIV elements

Is it possible to display content from an XML file in a DIV upon clicking, without using IDs and involving multiple DIVs? For instance, when clicking on the first link in the following code snippet, the content from an XML file should only appear in the r ...

What is the best way to send multiple variables to a url using jQuery ajax?

I am having trouble passing multiple values in the method below. Can someone help me with the correct syntax? I have attempted data: ('keyword='+$(this).val(),'id='10), as well as data: {'keyword='+$(this).val(),'id=&a ...

Exploring the usage of multidimensional arrays in React components

How can I extract data such as teamId from the "teams" array using datas.map() method in a multidimensional array? Any tips or help would be appreciated. Is there another way to map out this data? { "gameId": 3226262256, "platformId": "NA1", " ...

Tips for altering the background of a video on Vonage

Hello everyone, Currently, I am incorporating the Vonage API for video calling into my project. I would like to tweak the video background - does anyone have any ideas on how to accomplish this? I eagerly await your responses! Thank you in advance! ...

The MUI classes in my React project differ between the local and live versions, creating inconsistency in styling

I've encountered a discrepancy in the Mui classes used in my React project between the local and live versions. For instance: Localhost MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmal ...

Using function arguments as private variables in JavaScript allows for better encapsulation

Have you ever wondered if function arguments automatically become private variables within the function? I came across this interesting concept and decided to experiment: var node = function(nParent,nName,nContent){ this.init = function(){ ale ...