Next.js 13 introduced a new routing system called the App Router, which provides a more flexible and intuitive way to define routes and handle navigation in your application.
What is the App Router ?
The App Router is built on top of React Server Components and introduces new concepts and conventions for defining routes and handling navigation.
With the App Router, you define your routes using a directory structure inside the app
directory. Each directory represents a route segment, and the files inside those directories define the UI and behavior for that route.
Here's an example of a basic App Router structure:
app/
dashboard/
layout.js
page.js
settings/
layout.js
page.js
layout.js
page.js
In this example, we have two routes: /dashboard
and /settings
. Each route has its own layout.js
and page.js
files. The layout.js
file defines the common UI layout for that route, while the page.js
file defines the specific UI for that route.
Migrating from Pages Router to App Router
If you have an existing Next.js application using the Pages Router, you can migrate to the App Router incrementally. The App Router is available in Next.js 13 and later versions and can coexist with the Pages Router.
To start migrating, you can create an app
directory at the root of your project and gradually move your routes and components into it. You can have a mix of Pages Router and App Router in your application during the migration process.
Here's an example of a migrated route:
Before :
pages/
dashboard.js
After :
app/
dashboard/
page.js
In this example, the /dashboard
route is migrated to the App Router by creating a dashboard
directory inside the app
directory and moving the route's logic into the page.js
file.
File-based Routing
With the App Router, routes are defined using a file-based convention. Each file inside the app
directory represents a route segment, and the directory structure determines theroute hierarchy.
Here's an example of a more complex App Router structure:
app/
blog/
[slug]/
page.js
page.js
dashboard/
settings/
page.js
page.js
layout.js
page.js
In this example, we have the following routes:
-
/
: Defined byapp/page.js
-
/blog
: Defined byapp/blog/page.js
-
/blog/[slug]
: Defined byapp/blog/[slug]/page.js
, where[slug]
is a dynamic segment -
/dashboard
: Defined byapp/dashboard/page.js
-
/dashboard/settings
: Defined byapp/dashboard/settings/page.js
The layout.js
file at the root level defines the common UI layout for all routes.
Layouts and Nesting
The App Router introduces the concept of layouts, which allow you to define shared UI across multiple routes. Layouts are defined using the layout.js
file in each route directory.
Here's an example of a layout file:
// app/dashboard/layout.js
import Sidebar from './Sidebar';
export default function DashboardLayout({ children }) {
return (
<div>
<Sidebar />
<main>{children}</main>
</div>
);
}
In this example, the DashboardLayout
component defines the common UI structure for all routes within the /dashboard
route. The children
prop represents the content of the nested routes.
Layouts can be nested, allowing you to create a hierarchy of shared UI components. For example:
app/
dashboard/
settings/
page.js
layout.js
page.js
layout.js
In this case, the app/dashboard/layout.js
file defines the layout for the /dashboard
route, and the app/layout.js
file defines the root layout for all routes.
Data Fetching with the App Router
The App Router provides a new data fetching approach using React Server Components. You can fetch data directly in your components using the fetch
function or other data fetching libraries.
Here's an example of data fetching in a page component:
// app/blog/[slug]/page.js
async function getPost(slug) {
const res = await fetch(`https://api.example.com/posts/${slug}`);
return res.json();
}
export default async function PostPage({ params }) {
const post = await getPost(params.slug);
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
In this example, the getPost
function fetches a blog post from an API based on the slug
parameter. The PostPage
component is an async function that awaits the data fetching and renders the post.
The App Router automatically handles data fetching on the server and sends the pre-rendered HTML to the client, providing a fast initial load experience.
6. API Routes
Next.js allows you to create API routes, which are server-side endpoints that can handle HTTP requests and perform server-side logic.
Creating API Routes
To create an API route, you need to create a file inside the pages/api
directory. The file name becomes the route path, and the default export is a request handler function.
Here's an example of a simple API route:
// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}
In this example, the /api/hello
route responds with a JSON object containing a "Hello, World!" message.
Handling HTTP Methods
API routes can handle different HTTP methods like GET, POST, PUT, DELETE, etc. You can use conditional statements based on the req.method
to handle specific methods.
Here's an example:
// pages/api/users.js
export default function handler(req, res) {
if (req.method === 'GET') {
// Handle GET request
res.status(200).json({ users: [] });
} else if (req.method === 'POST') {
// Handle POST request
const newUser = req.body;
// Process the new user data
res.status(201).json({ message: 'User created' });
} else {
res.status(405).json({ message: 'Method not allowed' });
}
}
In this example, the /api/users
route handles GET and POST requests differently. It returns a list of users for GET requests and creates a new user for POST requests.
Connecting to Databases
API routes can connect to databases or external services to perform data operations. You can use libraries like prisma
, mongoose
, or any other database client to interact with your database.
Here's an example using Prisma:
// pages/api/posts.js
import prisma from '../../lib/prisma';
export default async function handler(req, res) {
if (req.method === 'GET') {
const posts = await prisma.post.findMany();
res.status(200).json({ posts });
} else if (req.method === 'POST') {
const { title, content } = req.body;
const newPost = await prisma.post.create({
data: {
title,
content,
},
});
res.status(201).json({ post: newPost });
} else {
res.status(405).json({ message: 'Method not allowed' });
}
}
In this example, the /api/posts
route uses Prisma to fetch posts from a database for GET requests and create a new post for POST requests.
Authentication and Authorization
API routes can handle authentication and authorization to protect sensitive data or restrict access to certain endpoints. You can use libraries like jsonwebtoken
or next-auth
to implement authentication strategies.
Here's an example using jsonwebtoken
:
// pages/api/protected.js
import jwt from 'jsonwebtoken';
const secretKey = process.env.JWT_SECRET;
export default function handler(req, res) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
res.status(401).json({ message: 'Unauthorized' });
return;
}
try {
const decoded = jwt.verify(token, secretKey);
// Access the authenticated user data from `decoded`
res.status(200).json({ message: 'Protected data' });
} catch (error) {
res.status(401).json({ message: 'Invalid token' });
}
}
In this example, the /api/protected
route checks for the presence of a JWT token in the Authorization
header. It verifies the token using the secret key and grants access to protected data if the token is valid.
Let's go for Part 3 : Deployment and Advanced techniques !
- NextJS Advanced Techniques : https://dev.to/fabrikapp/mastering-nextjs-1314-advanced-techniques-5ba
Or go back to your fundamentals :
Top comments (2)
exactly the kind of a starter documentation I have been looking for. Thank you for putting this up
Why does the frontend use an App rounter and the backend a Page router?