Table of Content#
- API Routes in Next.js
- Server-Side Rendering (SSR) Functions
- Client-Side Functions and React Hooks
- Custom Functions and Reusability
API Routes in Next.js#
Quiz Question#
What is the purpose of API routes in Next.js? A) To handle client-side form submissions B) To create serverless endpoints for data fetching and manipulation C) To style the application components
Answer and Explanation#
The correct answer is B. API routes in Next.js are used to create serverless endpoints. These endpoints can be used to fetch data from a database, perform authentication checks, or manipulate data. For example, if you have a blog application, you can create an API route to fetch all the blog posts from a database.
Common Practice#
- API routes are typically placed in the
pages/apidirectory. Each file in this directory represents an API endpoint. For instance, if you have a file namedpages/api/posts.js, it will be accessible at the/api/postsroute. - You can use standard Node.js modules like
fs(for file system operations if relevant) or database drivers (e.g., for MongoDB, you can use themongodbnpm package) within these API route functions.
Best Practice#
- Error Handling: Always handle errors gracefully in API routes. For example:
export default async function handler(req, res) {
try {
// Your data fetching or manipulation code here
const data = await someAsyncOperation();
res.status(200).json(data);
} catch (error) {
res.status(500).json({ error: 'Internal server error' });
}
}- Security: Sanitize any input coming from requests. If you are using query parameters or request bodies, validate and sanitize them to prevent security vulnerabilities like SQL injection (if using a database with SQL).
Example Usage#
Let's say you want to create an API route to get a list of users from a mock database (for simplicity, we'll use an array here).
pages/api/users.js:
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
];
export default function handler(req, res) {
res.status(200).json(users);
}You can then make a request to http://localhost:3000/api/users (assuming your Next.js app is running on port 3000) to get the list of users.
Server-Side Rendering (SSR) Functions#
Quiz Question#
Which function is used for server-side rendering in a Next.js page?
A) getStaticProps
B) getServerSideProps
C) useEffect
Answer and Explanation#
The correct answer is B. getServerSideProps is a function that runs on each request to a page. It allows you to fetch data on the server-side just before the page is rendered. This is useful when you need to display data that changes frequently (e.g., real-time stock prices).
Common Practice#
getServerSidePropsis defined at the top level of a page component (in thepagesdirectory). For example:
import React from'react';
function MyPage({ data }) {
return <div>{data}</div>;
}
export async function getServerSideProps() {
const data = await fetch('https://api.example.com/data').then(res => res.json());
return {
props: {
data
}
};
}
export default MyPage;- The data fetched in
getServerSidePropsis passed as props to the page component.
Best Practice#
- Caching: If the data doesn't change too frequently, you can implement caching mechanisms. For example, you can use in-memory caching libraries like
lru - cache(Least Recently Used cache) to cache the results of the data fetching operation. This can reduce the number of external API calls and improve performance. - Error Handling: Similar to API routes, handle errors in
getServerSideProps. If the data fetching fails, you can return an appropriate error message as a prop or redirect the user to an error page.
Example Usage#
Suppose you have a news website and want to display the latest news articles on a page. You can use getServerSideProps to fetch the articles from a news API:
import React from'react';
function NewsPage({ articles }) {
return (
<div>
{articles.map(article => (
<div key={article.id}>
<h2>{article.title}</h2>
<p>{article.description}</p>
</div>
))}
</div>
);
}
export async function getServerSideProps() {
const response = await fetch('https://newsapi.org/v2/top-headlines?country=us&apiKey=YOUR_API_KEY');
const data = await response.json();
return {
props: {
articles: data.articles
}
};
}
export default NewsPage;Client-Side Functions and React Hooks#
Quiz Question#
Which React hook is commonly used for side effects in a Next.js component (client-side)?
A) useState
B) useEffect
C) useRouter
Answer and Explanation#
The correct answer is B. useEffect is a React hook that allows you to perform side effects in a component. Side effects can include fetching data after the component has mounted (on the client-side), subscribing to events, or manually changing the DOM.
Common Practice#
useEffectis used to fetch data on the client-side when you don't need server-side rendering. For example:
import React, { useEffect, useState } from'react';
function ClientSideDataFetching() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/client-data');
const result = await response.json();
setData(result);
};
fetchData();
}, []);
return (
<div>
{data? <p>{data.message}</p> : <p>Loading...</p>}
</div>
);
}
export default ClientSideDataFetching;- The empty dependency array (
[]) inuseEffectmakes it run only once (when the component mounts).
Best Practice#
- Cleanup in
useEffect: If your side effect involves something like subscribing to an event (e.g.,window.addEventListener), you should clean it up. For example:
useEffect(() => {
const handleResize = () => {
// Some code to handle window resize
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);- Performance: Be careful with the dependencies in
useEffect. If you include a function in the dependency array that gets recreated on every render (e.g., an inline function), it can cause unnecessary re - runs of theuseEffectcallback.
Example Usage#
Let's say you have a component that needs to update its state based on the window width. You can use useEffect to listen for window resize events:
import React, { useState, useEffect } from'react';
function WindowWidthComponent() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return <div>Window width: {width}</div>;
}
export default WindowWidthComponent;Custom Functions and Reusability#
Quiz Question#
How can you create a reusable function in Next.js for data fetching that can be used in both API routes and page components? A) Create a separate utility file and export the function B) Duplicate the code in each file C) Use a global variable
Answer and Explanation#
The correct answer is A. Creating a separate utility file (e.g., utils/dataFetching.js) and exporting the function allows you to reuse the data fetching logic. For example:
utils/dataFetching.js:
export async function fetchSomeData() {
const response = await fetch('https://api.example.com/data');
return response.json();
}Then, in an API route (pages/api/some - api.js):
import { fetchSomeData } from '../../utils/dataFetching';
export default async function handler(req, res) {
const data = await fetchSomeData();
res.status(200).json(data);
}And in a page component (pages/some - page.js):
import React from'react';
import { fetchSomeData } from '../utils/dataFetching';
function SomePage({ data }) {
return <div>{data}</div>;
}
export async function getServerSideProps() {
const data = await fetchSomeData();
return {
props: {
data
}
};
}
export default SomePage;Common Practice#
- Utility functions can be used for various tasks like data transformation (e.g., formatting dates), authentication checks (e.g., checking if a user is authenticated based on a token), etc.
- Name the utility files and functions in a descriptive way so that it's clear what they do.
Best Practice#
- Testing: Write unit tests for your utility functions. You can use testing libraries like
JestandReact Testing Library(if the function is related to React components). For a simple data fetching utility function, you can mock thefetchfunction and test the return value. - Modularity: Keep the utility functions small and focused on a single task. This makes them easier to understand, maintain, and test.
Example Usage#
Let's create a utility function to format a date (assuming the date is in ISO format):
utils/dateFormat.js:
export function formatDate(isoDate) {
const date = new Date(isoDate);
const options = { year: 'numeric', month: 'long', day: 'numeric' };
return date.toLocaleDateString(undefined, options);
}In a page component (pages/posts.js):
import React from'react';
import { formatDate } from '../utils/dateFormat';
function Posts({ posts }) {
return (
<div>
{posts.map(post => (
<div key={post.id}>
<h2>{post.title}</h2>
<p>Published on: {formatDate(post.publishedDate)}</p>
</div>
))}
</div>
);
}
// Assume getServerSideProps or getStaticProps is used to fetch posts with publishedDate in ISO format
export default Posts;