I have a custom 500 error page called 500.tsx and I want to able to show this page when request from client fails with status of 500, but keeping client on old url (for example /auth/register). I have a axios listener on error which should do all the work. But could not find a way to do this using next/router. Any help appreciated
Didn't realize OP wanted to show 500 page at first.
You could look into router.isFallback but I am not entirely sure if that's appropriate for this.
It's supposed to be for Page Loading Indicators, but in your case, since the page will never load, it could work.
https://nextjs.org/docs/basic-features/data-fetching#fallback-true
return router?.isFallback
? <Custom500Page/>
: <RegularPage/>
You can set this variable from getStaticPaths method, which runs during build time and is usually used for [slug] pages.
// This function gets called at build time
export async function getStaticPaths() {
// Fetch paths to your slug pages here
return {
// paths,
fallback: true
}
}
Showing 404 page is quite straight forward & This is how we do.
Rendering 404 page
You can conditionally export a variable called notFound as true from getStaticProps method.
If the URL is invalid and you don't data to render the page, you simply pass that notFound prop as true and then the default or custom 404 page will show up automatically preserving your invalid url.
// This function gets called at build time
export async function getStaticProps({ params, preview = false }) {
let pageData
try {
// fetch pageData from API here
} catch (error) {
// catch errors
}
// check validity or emptyness of data
// invalid URL (slug) -> empty data from API
// valid URL (slug) -> valid data from API
return isObjectEmpty(pageData)
? { notFound: true }
: {
props: {
pageData
// pass other props to the page
}
}
}
NextJS Docs on getStaticProps & not-found
https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation
Related
I am stuck on an issue. Let's say I have a home page. From this home page, I want to route to either page A or page B.
I have a list of items on the home page and what I need to do is when I click on any item in the list, it makes a GET API call, and based on one field in the response which is a boolean, I either need to redirect to page A or page B.
Basically, I need to call an API on the click of the item and get the response before it is routed to either Page A or Page B.
Many thanks in advance
if you're using Next.JS, use useRouter prop to achieve this.
for example
import {useRouter} from "next/router";
export default function Page() {
const router = useRouter()
async function route() {
let res = await apiFunctionCall();
if (res) {
await router.replace({
pathname: '/page1'
})
} else {
await router.replace({
pathname: 'page2'
})
}
}
}
The closest solution I could get is to create a Lazy loaded component which calls the API and then based on the response, routes to either page A or Page B.
Before routing to the correct page based on the data that has been returned, the data first needs to be stored in a variable. The data will be stored in the res variable. The if else statement will then route to either page A or B based on the data.
<code>const Response = res.data;
if(Response.type === 'A'){
this.props.history.push('/page-a');
} else {
this.props.history.push('/page-b');
}
</code>
I'm having a problem with getServerSideProps in NextJS not updating values on page load. It seems to be having the same effect as using getStaticProps when calling from data that is an imported JSON file.
Here's the applicable code:
/update/[...id].js
import { hasPermission, getPermissions } from '../../lib/permissions'
...
//in the page function I use the ternary to check permission of the user
{ (hasPermission(session.user.email, `${params.id[0]}-edit`, permissions)) ? <HasPerm /> : <NoPerm /> }
...
export async function getServerSideProps(context) {
const params = context.query
const permissions = getPermissions()
return {
props: { params, permissions }
}
}
/lib/permissions.js
import permissions from '../db/users.json'
export function hasPermission(username, group, perm = false) {
if (!perm)
perm = permissions
if (username in perm && perm[username].groups.includes(group)) {
return true
}
if (perm.all.groups.includes(group)) {
return true
}
return false
}
export function getPermissions() {
return permissions
}
For SSR pages that were understandably doing this to begin with, I pass in the permissions to the page in getStaticProps and pass those values into my hasPermission() function in those pages so that they are revalidated on permissions change. Yet there is no revalidation option in getServerSideProps so the data is not revalidated at all. I was using getInitialProps before but pretty much everyone discourages that because it disables ASO.
How do I use getServerSideProps such that my imported json is at least revalidated?
I ended up not using the json import and used fs instead for other reasons but the issue is resolved by using dynamic imports in the getServerSideProps:
export async function getServerSideProps(context) {
const permissions = await import('../db/users.json')
}
Also don't make my same mistake of passing the whole permissions database to every user as was being done by using a function like getPermissions in getStaticProps to get the data to the hasPermission function in the page. Instead, only serve data with getServerSideProps if the user has permission. In other words, ensure you're holding the data back from the server-side. Then you can also pass through an individual's permissions to the page if you need to, like for displaying a message saying you don't have permission.
We are currently working on a Nextjs project which includes a number of dynamic pages. We have come to a stage where we are defining the dynamic paths using getStaticProps and getStaticPaths. So far we managed to define 404 pages in dynamic pages with a similar structure to the below:
export async function getStaticProps(context) {
const { id } = context.params
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
const user = await response.json()
return {
props: { user },
notFound: Object.keys(user).length === 0
}
}
export function getStaticPaths() {
return {
paths: [],
fallback: 'blocking'
}
}
Using version 10's notFound value and fallback: blocking allows us to have the page return a 404 status code (for some reason fallback: true returns a 200 status code).
The issue we currently have is to display a 404 page to pages that depend on user data such as username or user locale. One way we thought we could solve this is by having those pages as Server Side generated rather than Static Site generated by using getServerSideProps:
export async function getServerSideProps(context) {
const userId = getCookie('user_id')
const response = await fetch(`___/checkUserAccess/${userId}`)
const access = await response.json()
return {
props: { ... },
notFound: !access.userAllowed
}
}
I suppose I am answering my own question here as going for Server Side for these pages to show 404 pages based on user data seems logical. However the concern is the impact it may have on the website as a whole. These pages could easily end up being hundreds of pages.
Should we be concerned about this? Or maybe there's another way to go about solving this?
This question already has an answer here:
Fetch error when building Next.js static website in production
(1 answer)
Closed 11 months ago.
I am using Next.js with the Vercel deployment workflow, I am following this guide to try and setup page generation at buildtime. The specific section shows the following example to generate pages based on an external API's response:
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map(post => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return { props: { post } }
}
I want to do this exactly, however I wrote my API as a Node.js serverless function within the same code repository, it is not an external api.
I tried to do the following to call on my api:
// This function gets called at build time
export async function getStaticPaths() {
const res = await fetch('/api/get-designs');
const designs = await res.json();
// Get the paths we want to pre-render based on posts
const paths = designs.map(design => ({
params: { id: design.id },
}))
return {
// Only posts returned by api are generated at build time
paths: paths,
// Enable statically generating additional pages
fallback: true,
}
}
However I get an error that the fetch api url must be absolute. Because of the way Vercel deploys, I won't always have the same deployment URL, so I don't think I can just use a hardcoded value here. Also, I am suspecting that because this function runs at buildtime, that my function is not running yet, therefore can not be called. I am still trying to wrap my head around this Next.js statically generated site workflow, but basically I am confused because they seem to encourage using serverless functions, and this getStaticPaths method for page generation, but they don't seem to work together unless I am missing something.
Is there a way I can run my api to get these results at build time? Any guidance would be much appreciated!
Thanks!
In this case, we can extract the server logic into a function and that function can be used directly inside your api route file. So, for CR we can use /api/whateverRoute and inside getStaticPaths we can use that function itself directly.
I try to get from a list of users to only one user and display his profile on another page.
I want to do so with the routerLink and passing on an id of this specific user to the next page.
The routing is working, Im directed to the profile page but when I log the results of the http request I still get back the whole list of users like in the users page instead of the details of one user.
I have tried many things like changing the path of the url in my user.service.ts but that didn't solve the problem I even got 404 request errors when using this path ${this.url}/users/${id}/ instead of ${this.url}/users/?i=${id}/ (where its working).
The api docs is saying though that in order to retrieve one single user its http://1234//users/{id}/ it this scheme while id is an integer. But when I want to apply that scheme I get the 404 error.
Thats why I have to use the ?I= version but there the problem is I only get the full list of users on the next page.
MY CODE:
user.service.ts
// get a user's profile
getUserDetails(id): Observable<any> {
return this.http.get(`${this.url}/users/?i=${id}/`); // why add ?i
}
user.page.ts
// get all users in the users page
getAllUsers() {
this.userList = this.userService.getList()
.pipe(map(response => response.results));
}
user.page.html
<ion-avatar class="user-image" slot="start" [routerLink]="['/','profile', 'user.id']">
<ion-img src="assets/22.jpeg"> </ion-img>
</ion-avatar>
profile.page.ts
information = null;
...
ngOnInit() {
// Get the ID that was passed with the URL
let id = this.activatedRoute.snapshot.paramMap.get('id');
// Get the information from the API
this.userService.getUserDetails(id).subscribe(result => {
this.information = result;
console.log(result);
});
}
It seems like the url is wrong. If it was me I would console.log the url and compare it to the docs. Heres a snippet to try a few variations:
const id = 1;
const options = [
`${this.url}/users/?i=${id}/`,
`${this.url}/users/?i=${id}`,
`${this.url}/users/i/${id}/`,
`${this.url}/users/i/${id}`,
`${this.url}/user/?i=${id}/`,
`${this.url}/user/?i=${id}`,
`${this.url}/user/i/${id}/`,
`${this.url}/user/i/${id}`,
];
for (const option of options) {
try {
const response = await this.http.get(option);
console.log(options, response);
} catch (e) {
}
}
I would also consider dropping the second http request. If the first request returns all the required data you could just store it in a variable on the service.