The release of NextJS 13 brought about a plethora of new and impressive features, with one standout being the updated data fetching and management process. The fetch API replaced the more complicated functions, including getServerSideProps
, getStaticProps
, and getInitialProps
.
Video
If you prefer watching a video, you can check out the Youtube video posted on the same
fetch() API
React recently introduces support for async/await in Server Components. You can now write Server Components using standard JavaScript await syntax by defining your component as an async function and that is what turned out to be a big plus point for NextJS 13.
Server Components
This is all you need to do to fetch data in NextJS now:
import { Post } from "@/lib/types";
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
const getPosts = async (): Promise<Post[]> => {
const data = await fetch("https://jsonplaceholder.typicode.com/posts");
const posts = await data.json();
return posts;
};
export default async function Posts() {
const posts = await getPosts();
console.log(posts);
return (
<div className={inter.className}>
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
The data is no more serialised so you can pass any type of data, including Dates, Maps, Sets, etc.
Also, note the fact that you no longer need to do this on the page level, like if you did before using getStaticProps
or getServerSideProps
. You can do this inside any component
Client Components
For now, if you need to fetch data in a Client Component, NextJS 13 recommends using a third-party library such as SWR or React Query.
React also introduced the use
hook that accepts a promise conceptually similar to await. use handles the promise returned by a function in a way that is compatible with components, hooks, and Suspense.
"use client";
import { Post } from "@/lib/types";
import { Inter } from "next/font/google";
import { use } from "react";
const inter = Inter({ subsets: ["latin"] });
const getPosts = async (): Promise<Post[]> => {
const data = await fetch("https://jsonplaceholder.typicode.com/posts");
const posts = await data.json();
return posts;
};
export default function ClientPosts() {
const posts = use(getPosts());
return (
<div className={inter.className}>
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
Static Data Fetching
fetch
by default caches the data. So, even if the data from the API changes, when you refresh the page, the site doesn't update the data. This works great for sites which have static data which seldom changes. The example above demonstrates how to do this because it is the same as doing
fetch("https://jsonplaceholder.typicode.com/posts", {
cache: "force-cache",
});
Dynamic Data Fetching
You can tell the fetch
API to never cache the data by changing force-cache
to no-cache
or no-store
(both signify the same in NextJS).
fetch("https://jsonplaceholder.typicode.com/posts", {
cache: "no-cache"
});
Dynamic Params Data Fetching
Say, you link all the posts to another page /posts/[postId]
and fetch the data here once you're in there. You'd do something like this:
// app/posts/[postId]/page.tsx
import { Post } from "@/lib/types";
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
// params: {
// postId: aksdjlkjasd
// }
const getPost = async (id: string): Promise<Post> => {
const data = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
const post = await data.json();
return post;
};
export default async function PostPage({
params: { postId },
}: {
params: {
postId: string;
};
}) {
const post = await getPost(postId);
return (
<div className={inter.className}>
<h1>{post.title}</h1>
<p>Post ID: {postId}</p>
<p>{post.body}</p>
</div>
);
}
Now, this works great, but when you try to build it, look at the response:
It says that /posts/[postId]
is server-side rendered at runtime even though /posts
is static. This is because NextJS doesn't know what all routes(postIds) exist. So, to enhance this further, you can add this function to the /posts/[postId]/page.tsx
page
export const generateStaticParams = async () => {
const data = await fetch("https://jsonplaceholder.typicode.com/posts");
const posts = await data.json();
return posts.map((post: Post) => ({
params: {
postId: post.id.toString(),
},
}));
};
This now tells NextJS, that all these postId
s exist and we want it to statically generate them at build time if possible. Now, if we try rebuilding the app, this happens
Now, see that /posts/[postId]
is static HTML + JSON! And, that would further optimise your app really well.
Revalidating Data
Say, your app is not fully dynamic but still sometimes the data does change, you can add this to your /posts/[postId]/page.tsx
page
export const revalidate = 3600; // in seconds
This will tell NextJS to revalidate the data every hour. And the interesting this is, you can do this on the page level as well as the layout level.
You can also do this per fetch by tweaking the fetch call as follows
fetch("https://jsonplaceholder.typicode.com/posts", {
next: {
revalidate: 3600,
},
});
And that is it!
Conclusion
It takes a while to wrap your head around all this, but this is what NextJS does so well and one of the biggest plus points about it, and once you get a hold of it, it's really powerful.
You can find all the code over here
https://github.com/kavinvalli/data-fetching-next13
Do give the video a look if you like that more, and stay tuned for more such posts!
Top comments (0)