Introduction
At Logto, we offer a Next.js SDK named @logto/next, accompanied by a sample project. This project effectively demonstrates how to integrate Logto with Next.js using the traditional "pages" folder, offering examples of API routes, getServerSideProps
, Client-side Fetching, and even edge runtime.
Next.js 13 brought us the groundbreaking App Router (previously called the app directory), introducing new features and conventions that quickly became popular in the developer community. Given that Logto’s Next.js SDK fully supports these new features, we were inspired to build a new sample project utilizing this App Router.
In this post, we'll walk you through the details of migrating our old sample project to the new one using the App Router. The project was built upon the old sample project and followed the official migration guide. The process involved several steps: creating pages and layouts, migrating API routes, and utilizing server and client components.
The Migration Process
Page Layout: A New Structure
In our original setup, we utilized an _app.tsx
file for setting up the SWR fetcher and an index.tsx
file served as the home page.
With the App Router, these become layout.tsx
and page.tsx
. The layout.tsx
file contains the core information, while page.tsx
mirrors the functionality of the old index.tsx
file.
Client component
One hiccup arose during the first step: setting an "onClick" handler for the button was unsuccessful, yielding an error message stating, "Event handlers cannot be passed to Client Component props. If you need interactivity, consider converting part of this to a Client Component."
To remedy this issue, we extracted the problematic section into a separate component and prefixed the file with use client
:
'use client';
type Props = {
isAuthenticated: boolean;
};
const Nav = ({ isAuthenticated }: Props) => {
return (
<nav>
{isAuthenticated ? (
<button
onClick={() => {
window.location.assign('/api/logto/sign-out');
}}
>
Sign Out
</button>
) : (
<button
onClick={() => {
window.location.assign('/api/logto/sign-in');
}}
>
Sign In
</button>
)}
</nav>
);
};
export default Nav;
API routes
Transitioning the API routes was as simple as transferring the previous files from /pages/api
to /app/api
with a few tweaks:
-
index.ts
was renamed toroute.ts
. - The exported function was renamed to
GET
or another relevant method name.
Server component
Within the api
folder, we have the capability to add server-only
functions, which allow React Server Components to fetch data.
In the /app/api/logto/user
directory, there's a get-user.tsx
file:
import { type LogtoContext } from '@logto/next';
import { cookies } from 'next/headers';
// `server-only` guarantees any modules that import code in file
// will never run on the client. Even though this particular api
// doesn't currently use sensitive environment variables, it's
// good practise to add `server-only` preemptively.
// eslint-disable-next-line import/no-unassigned-import
import 'server-only';
import { config } from '../../../../libraries/config';
export async function getUser() {
const response = await fetch(`${config.baseUrl}/api/logto/user`, {
cache: 'no-store',
headers: {
cookie: cookies().toString(),
},
});
if (!response.ok) {
throw new Error('Something went wrong!');
}
// eslint-disable-next-line no-restricted-syntax
const user = (await response.json()) as LogtoContext;
return user;
}
This function can then be invoked in page.tsx
:
import { getUser } from './api/logto/user/get-user';
import Nav from './nav';
const Page = async () => {
const user = await getUser();
return (
<div>
<header>
<h1>Hello Logto.</h1>
</header>
<Nav isAuthenticated={user.isAuthenticated} />
{user.isAuthenticated && user.claims && (
<div>
...
</div>
)}
</div>
);
}
export default Page.
Conclusion
Upon completing the migration, we found our code and structure significantly more streamlined and intuitive. Although it seemed challenging at the outset, the process was far from daunting. We hope our experience can serve as a valuable guide, helping you confidently migrate your projects to the App Router.
For comparison, here are the links to our projects, both before and after the migration:
Before: https://github.com/logto-io/js/tree/master/packages/next-sample
After: https://github.com/logto-io/js/tree/master/packages/next-app-dir-sample
By examining these projects, you can gain a clearer understanding of the changes and benefits introduced by this migration.
Top comments (0)