Next.js migration guide v6 to v7

Overview

In this migration guide, we will go over the process of migrating from @frontegg/[email protected] to @frontegg/[email protected]. The new version introduces some breaking changes, please make sure to follow the below instructions carefully.

This guide is intended for applications that use SSR and withFronteggApp. If you used FronteggProviderNoSSR in your project, there is no need to update your project.


⚡ Before you start: ⚡


🚧

Next.js

@frontegg/nextjs supports up to Next.js version 13.2.4

📘

Prerequisites:

Node >= 16
Next >= 12
Typescript >= 3.9.7

The files in this guide are in typescript - if you are working in js, simply change the file types and remove the typing.

STEP 1: Frontegg API middleware migration

  • Rename imports to @frontegg/nextjs/middleware.
  • Export Next.js config to mark as externalResolver and disable the response limit.
import { FronteggApiMiddleware } from '@frontegg/nextjs/middleware';

export default FronteggApiMiddleware;
export const config = {
  api: {
    externalResolver: true,
    // https://nextjs.org/docs/messages/api-routes-response-size-limit
    responseLimit: false,
  },
};
export { fronteggMiddleware as default } from '@frontegg/nextjs';

STEP 2: Edge middleware migration

If you are using Next.js edge middleware, (Ex: middleware.ts):

  • Rename the imports to @frontegg/nextjs/edge.
  • Rename getSession to getSessionOnEdge.
  • Use the redirectToLogin method instead of building the login URL.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { getSessionOnEdge, shouldByPassMiddleware, redirectToLogin } from '@frontegg/nextjs/edge';

export const middleware = async (request: NextRequest) => {
  const pathname = request.nextUrl.pathname;

  if (shouldByPassMiddleware(pathname)) {
    return NextResponse.next();
  }

  const session = await getSessionOnEdge(request);
  if (!session) {
    return redirectToLogin(pathname);
  }
  return NextResponse.next();
};

export const config = {
  matcher: '/(.*)',
};
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { getSession, shouldByPassMiddleware } from '@frontegg/nextjs/edge';

export const middleware = async (request: NextRequest) => {
  const pathname = request.nextUrl.pathname;

  if (shouldByPassMiddleware(pathname /*, options: optional bypass configuration */)) {
    return NextResponse.next();
  }

  const session = await getSession(request);
  if (!session) {
    //  redirect unauthenticated user to /account/login page
    const loginUrl = `/account/login?redirectUrl=${encodeURIComponent(pathname)}`;
    return NextResponse.redirect(new URL(loginUrl, process.env['FRONTEGG_APP_URL']));
  }
return NextResponse.next();
};

export const config = {
  matcher: '/(.*)',
};

Pages Architecture Migration

import { withFronteggApp } from '@frontegg/nextjs';

function CustomApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

export default withFronteggApp(CustomApp, 
  {
    hostedLoginBox: true, 
    authOptions: {
    // keepSessionAlive: true // Uncomment this in order to maintain the session alive
    }
});
import { withFronteggApp } from '@frontegg/nextjs/pages';

function CustomApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

export default withFronteggApp(CustomApp, 
  {
    hostedLoginBox: true, 
    authOptions: {
    // keepSessionAlive: true // Uncomment this in order to maintain the session alive
    }
});

import { FronteggRouter, FronteggRouterProps } from '@frontegg/nextjs/pages';

export const getServerSideProps = FronteggRouterProps;
export default FronteggRouter;
export {
  FronteggRouter as default,
  FronteggRouterProps as getServerSideProps,
} from '@frontegg/nextjs';

Example page

import { useCallback } from "react";
import { GetServerSideProps } from "next";
import { getSession } from "@frontegg/nextjs/pages";
import { useAuth } from "@frontegg/nextjs";
import { useRouter } from "next/router"

export default function MyPage({ products }) {
  const { user } = useAuth();

  const router = useRouter();

  const logout = useCallback(() => {
    router.replace('/account/logout');
  }, [router]);
 
  return (
    <div>
      <h1>My Page</h1>
      {products}
      <div>
        <img src={user?.profilePictureUrl} alt={user?.name} />
      </div>
      <div>
        <span>Logged in as: {user?.name}</span>
      </div>
      <div>
        <button onClick={logout}>Log out</button>
      </div>
    </div>
  );
}

// In the `getServerSideProps` method you can get data from an external service to pull relevant data for a logged in user.
// we used the prop `products`. See the commented code for an example.

export const getServerSideProps: GetServerSideProps = withSSRSession(
  async (context, session) => {
    //     const { data } = await fetch('{external}/product', {
    //      headers: {
    //        Authorization: 'bearer ' + session.accessToken,
    //      },
    //    });
    return { props: {} };
  }
);
import { GetServerSideProps } from 'next';
import { withSSRSession, useAuth } from '@frontegg/nextjs';

export default function MyPage({ products }) {
  const {user} = useAuth();
  
  //baseUrl should be your FRONTEGG_APP_URL from .env.local
  const baseUrl =  'FRONTEGG_APP_URL'
  
  const logout = () => {
    window.location.href = `${baseUrl}/account/logout`;
  };

  return (
    <div>
      <h1>My Page</h1>
       {products}
      <div>
        <img src={user?.profilePictureUrl} alt={user?.name}/>
      </div>
      <div>
        <span>Logged in as: {user?.name}</span>
      </div>
            <div>
        <button onClick={logout}>Log out</button>
      </div>
    </div>
  );
}


// In the `getServerSideProps` method you can get data from an external service to pull relevant data for a logged in user.
// we used the prop `products`. See the commented code for an example.

export const getServerSideProps: GetServerSideProps = withSSRSession(
  async (context, session) => {
//     const { data } = await fetch('{external}/product', {
//      headers: {
//        Authorization: 'bearer ' + session.accessToken,
//      },
//    });
    return { props: { } };
  }
);

App Directory Architecture Aigration

  • Rename imports from @frontegg/nextjs/server to @frontegg/nextjs/app.
  • Move FronteggAppProvider to inside RootLayout :

import { FronteggAppProvider } from '@frontegg/nextjs/app';

export default function RootLayout({ children }: { children: React.ReactNode }) {
    const authOptions = {
            // keepSessionAlive: true // Uncomment this in order to maintain the session alive
    }
    return (
      <html>
        <head></head>
        <body>
            {/* @ts-expect-error Server Component for more details visit: https://github.com/vercel/next.js/issues/42292 */}
            <FronteggAppProvider authOptions={authOptions} hostedLoginBox={false}>
                {children}
            </FronteggAppProvider>
        </body>
      </html>
    );
}
import { FronteggAppProvider } from "@frontegg/nextjs/server";

export default function RootLayout({children}: {children: React.ReactNode;}) {

  const authOptions = {
    // keepSessionAlive: true // Uncomment this in order to maintain the session alive
}
  
  return (
    <FronteggAppProvider authOptions={authOptions} hostedLoginBox={true}>
      <html>
        <head></head>
        <body>{children}</body>
      </html>
    </FronteggAppProvider>
  );
}

import { FronteggAppRouter } from '@frontegg/nextjs/app';

export default FronteggAppRouter;
export { FronteggAppRouter as default } from '@frontegg/nextjs/client';

Rename getSession and getUserTokens to getAppUserSession and getAppUserToken

import { getAppUserSession, getAppUserTokens } from '@frontegg/nextjs/app';

export const ServerSession = async () => {
  
  const userSession = await getAppUserSession();
  const tokens = await getAppUserTokens();
  
  return (
    <div>
      <div>user session server side: {JSON.stringify(userSession)}</div>;
      <div>user tokens server side: {JSON.stringify(tokens)}</div>
    </div>
  );
};

You're all set!