Hi, all,
Happy 2024!
I am asking for help as I am lost on how to fetch data on the server side.
Referring to the LastestIssue
component, I know directly getting data using prisma works. But I do not like it because:
- it skips the api
- no error handling
I tried to implement my own data fetching using fetch
and axios
, but was unsuccessful.
import axios from "axios";
const LatestIssues = async () => {
// Fetch data using axios
// Make sure
// - use the full url
// - no `/` at the end of the url
const response = await axios.get("http://localhost:3000/api/issues")
const issues = response.data;
return (
<>
<div>Latest Issues</div>
<ul>
{issues.map((issue) => (
<li key={issue.id}>{issue.title}</li>
))}
</ul>
</>
);
};
export default LatestIssues;
While data is fetched, it can not be rendered because of the async nature of data fetching. issues
are not available for rendering.
We usually solve this on the client side using useEffect
and useState
to set up a loading
state. But this is not possible unless I change this component into a client side component.
I also looked into using getServerSideProps()
, but it works only with files in `/pages’, and I think with this tutorial we are moving away from the old page routing.
So, is there a way to keep this as a server component and fetch data from API using fetch
/ axios
?
Many thanks, and happy learning!
Sawfiz
Hi, all,
After some research, I have come to some clarity.
Next.js app router vs. page router
With the introduction of the new Next.js app router, many things are simplified.
- no more use of
@/app/pages
- special files, e.g, pages. tsx, error.tsx, loading.tsx, etc.
- much simpler data fetching
– components can be `async’
– directly fetch data using prisma, axios, fetch
Recommendation from Next.js
Whenever possible, we recommend fetching data on the server with Server Components. This allows you to:
- Have direct access to backend data resources (e.g. databases).
- Keep your application more secure by preventing sensitive information, such as access tokens and API keys, from being exposed to the client.
- Fetch data and render in the same environment. This reduces both the back-and-forth communication between client and server, as well as the work on the main thread on the client.
- Perform multiple data fetches with single round-trip instead of multiple individual requests on the client.
My understanding is, it is recommended to fetch data using prisma.
I list below 3 ways to fetch data in the case of LatestIssues
Fetching using Prisma
[!success] Pro
- This is the simplest way when the backend had direct access to the database.
- It is also the recommended way as without going through the API, it is faster and more secure.
- Also, there is no need to explicitly define the type of
issues
[!failure] Con
- Only works when the backend had direct access to the database.
import prisma from "@/prisma/client";
const LatestIssues = async () => {
// Fetch using Prisma
const issues = await prisma.issue.findMany({
orderBy: { createdAt: "desc" },
take: 5,
});
return (
<>
<div>Latest Issues</div>
<ul>
{issues.map((issue) => (
<li key={issue.id}>{issue.title}</li>
))}
</ul>
</>
);
};
Note:
The data fetching can not be put into a try...catch
block, doing so will make issues
inaccessible in return
Fetching using axios
[!success] Pro
[!failure] Con
- Need to add explicit type definition of
issues
- Complex URL, easy to make mistakes
import axios from "axios";
const LatestIssues = async () => {
// Fetch data using axios
// Make sure
// - use the full url
// - no `/` at the end of the url
const response = await axios.get(
"http://localhost:3000/api/issues?orderBy=updatedAt&sort=desc&page=1&pageSize=5"
);
const issues: Issue[] = response.data.issues;
return (
<>
<div>Latest Issues</div>
{/* <ul>
{issues.map((issue) => (
<li key={issue.id}>{issue.title}</li>
))}
</ul> */}
</>
);
};
export default LatestIssues;
Fetch using fetch
[!success] Pro
[!failure] Con
- Need to parse json and deconstruct
res
- Type definition of
issues
is more complex
- Complex URL, easy to make mistakes
import {Issue} from '@prisma/client'
const LatestIssues = async () => {
// Fetch data using fetch
// Make sure
// - use the full url
// - no `/` at the end of the url
const res = await fetch(
"http://localhost:3000/api/issues?orderBy=updatedAt&sort=desc&page=1&pageSize=5"
);
// Deconstruct issues.issues
const { issues }: { issues: Issue[] } = await res.json();
return (
<>
<div>Latest Issues</div>
<ul>
{issues.map((issue) => (
<li key={issue.id}>{issue.title}</li>
))}
</ul>
</>
);
};
export default LatestIssues;
Outstanding issues
- For some reason, I can not use
orderBy=createdAt
in the urls used in axios and fetch. Other fields, e.g., status and updatedAt work…
- I need to figure out how to add error handling in each of the above 3 ways.
Thanks,
Sawfiz
It seems like you’re trying to fetch data from an API within a server-side component in your Next.js application. Since server-side rendering doesn’t support asynchronous operations directly within the component, you need to handle data fetching differently. One way to achieve this is by using getServerSideProps
in Next.js.
Here’s how you can refactor your component to use getServerSideProps
:
import axios from “axios”;
const LatestIssues = ({ issues }) => {
return (
<>
Latest Issues
{issues.map((issue) => (
- {issue.title}
))}
</>
);
};
export async function getServerSideProps() {
try {
const response = await axios.get(“http://localhost:3000/api/issues”);
const issues = response.data;
return {
props: { issues },
};
} catch (error) {
console.error(“Error fetching data:”, error);
return {
props: { issues: }, // Return empty array if there’s an error
};
}
}
export default LatestIssues;