NextJS - SSR Getting _next/data.json requst - next.js

I have a page uses getServerSideProps and when I request this page directly everything works as expected but when I request this page on the client through next router I see request which has this JSON path like https://****/_next/data/uU96tn57Tno0N1e5kERHx/data.json
How can I prevent this request when accessing the page on the client side through next router?
Should I use shallow routeing? And handle the request on useEffects method?
My page looks like
import React from "react";
import axios from "axios";
const sendGetRequest = async () => {
try {
const resp = await axios.get("https://dog.ceo/api/breeds/image/random");
return resp.data.message;
} catch (err) {
console.error(err);
}
};
function Paged(props) {
return (
<div>
<p>{props.dogstring}</p>
</div>
);
}
export async function getServerSideProps(context) {
const dogstring = await sendGetRequest();
return {
props: {
dogstring: dogstring,
},
};
}
export default Paged;
Accessing the page
<Link href={'/paged'}><a>ssr page</a></Link>
I want to prevent the JSON path request because I don't want to be 2 latency to the SSR page when accessing it through next router on the client side

Related

How do I stop auth middleware in NextJS applying to server-side/pre-rendering requests?

I'm trying to require the user to be logged in to access certain routes.
I have added the following middleware, as per the docs, but am having difficulty getting it to work.
I thought the issue was down to the server-side pre-rendered page always being created while unauthenticated, but I'm no longer sure. Middleware should only run on client-side requests from a browser, right?
When I include the block marked with 🟡s, the redirect does not happen.
When it's removed, the redirect always happens, even if the user is logged in.
Note that we're using "next": "^12.3.3", and we're not ready to upgrade to next v13 yet.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import * as auth from 'src/lib/auth';
const pathsNeedingAuth = [
'/dashboard$',
'/account/(password|reset|delete)$',
];
export default async function middleware(request: NextRequest) {
if (typeof window === 'undefined') { // 🟡
return NextResponse.next(); // 🟡
} // 🟡
const pathNeedsAuth = pathsNeedingAuth.some(
(path) => new RegExp(path).test(request.nextUrl.pathname),
);
if (!pathNeedsAuth) {
return NextResponse.next();
}
const isAuthed = await auth.isAuthenticated();
if (isAuthed) {
return NextResponse.next();
}
const url = request.nextUrl.clone();
url.searchParams.set('redirectUrl', url.pathname);
url.pathname = '/account/login';
return NextResponse.redirect(url);
}
export const config = {
matcher: [...pathsNeedingAuth],
};
Any help will be appreciated! I know the structure of the code is a little odd, but that's simply from changing it while trying various things.
Redirects are incompatible with next export.

How to access query params in Next.js SSG, ISR [duplicate]

I want to get query string from URL on Next.js static site generation.
I found a solution on SSR but I need one for SSG.
Thanks
import { useRouter } from "next/router";
import { useEffect } from "react";
const router = useRouter();
useEffect(() => {
if(!router.isReady) return;
const query = router.query;
}, [router.isReady, router.query]);
It works.
I actually found a way of doing this
const router = useRouter()
useEffect(() => {
const params = router.query
console.log(params)
}, [router.query])
As other answers mentioned, since SSG doesn't happen at request time, you wouldn't have access to the query string or cookies in the context, but there's a solution I wrote a short article about it here https://dev.to/teleaziz/using-query-params-and-cookies-in-nextjs-static-pages-kbb
TLDR;
Use a middleware that encodes the query string as part of the path,
// middleware.js file
import { NextResponse } from 'next/server'
import { encodeOptions } from '../utils';
export default function middleware(request) {
if (request.nextUrl.pathname === '/my-page') {
const searchParams = request.nextUrl.searchParams
const path = encodeOptions({
// you can pass values from cookies, headers, geo location, and query string
returnVisitor: Boolean(request.cookies.get('visitor')),
country: request.geo?.country,
page: searchParams.get('page'),
})
return NextResponse.rewrite(new URL(`/my-page/${path}`, request.nextUrl))
}
return NextResponse.next()
}
Then make your static page a folder that accepts a [path]
// /pages/my-page/[path].jsx file
import { decodeOptions } from '../../utils'
export async function getStaticProps({
params,
}) {
const options = decodeOptions(params.path)
return {
props: {
options,
}
}
}
export function getStaticPaths() {
return {
paths: [],
fallback: true
}
}
export default function MyPath({ options }) {
return <MyPage
isReturnVisitor={options.returnVisitor}
country={options.country} />
}
And your encoding/decoding functions can be a simple JSON.strinfigy
// utils.js
// https://github.com/epoberezkin/fast-json-stable-stringify
import stringify from 'fast-json-stable-stringify'
export function encodeOptions(options) {
const json = stringify(options)
return encodeURI(json);
}
export function decodeOptions(path) {
return JSON.parse(decodeURI(path));
}
You don't have access to query params in getStaticProps since that's only run at build-time on the server.
However, you can use router.query in your page component to retrieve query params passed in the URL on the client-side.
// pages/shop.js
import { useRouter } from 'next/router'
const ShopPage = () => {
const router = useRouter()
console.log(router.query) // returns query params object
return (
<div>Shop Page</div>
)
}
export default ShopPage
If a page does not have data fetching methods, router.query will be an empty object on the page's first load, when the page gets pre-generated on the server.
From the next/router documentation:
query: Object - The query string parsed to an object. It will be
an empty object during prerendering if the page doesn't have data
fetching
requirements.
Defaults to {}
As #zg10 mentioned in his answer, you can solve this by using the router.isReady property in a useEffect's dependencies array.
From the next/router object documentation:
isReady: boolean - Whether the router fields are updated
client-side and ready for use. Should only be used inside of
useEffect methods and not for conditionally rendering on the server.
you don't have access to the query string (?a=b) for SSG (which is static content - always the same - executed only on build time).
But if you have to use query string variables then you can:
still statically pre-render content on build time (SSG) or on the fly (ISR) and handle this route by rewrite (next.config.js or middleware)
use SSR
use CSR (can also use SWR)

How do I use API middlewares to protect API routes from unauthenticated users in Next.js?

I have a next.js app that has several API routes that I am hoping to protect from users who are not logged in. Using next-auth, I understand that I can add the following code to each API route to achieve this.
import { getSession } from 'next-auth/client'
export default async (req, res) => {
const session = await getSession({ req })
if (session) {
res.send({ content: 'This is protected content. You can access this content because you are signed in.' })
} else {
res.send({ error: 'You must be sign in to view the protected content on this page.' })
}
}
However, I was wondering if it is possible to use API middlewares, so I am not repeating the same code over and over again? I read through the Next.js API middlewares documentation (https://nextjs.org/docs/api-routes/api-middlewares) and did the following:
import Cors from 'cors';
import { getSession } from 'next-auth/react';
function initMiddleware(middleware) {
return (req, res) =>
new Promise((resolve, reject) => {
middleware(req, res, async (result) => {
const session = await getSession({ req });
if (!session) {
return reject(result);
}
return resolve(result);
});
});
}
const cors = initMiddleware(
Cors({
methods: ['GET', 'POST', 'OPTIONS'],
})
);
export default async function handler(req, res) {
await cors(req, res);
\* fetching from database *\
Although it works, the following error is returned when I tried to access the API route when unauthenticated, and it feels like I'm not doing it properly.
error - null
wait - compiling /_error (client and server)...
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:561:11)
at DevServer.renderError (/Users/alextung/Desktop/Projects/askit/node_modules/next/dist/server/next-server.js:1677:17)
at DevServer.run (/Users/alextung/Desktop/Projects/askit/node_modules/next/dist/server/dev/next-dev-server.js:452:35)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async DevServer.handleRequest (/Users/alextung/Desktop/Projects/askit/node_modules/next/dist/server/next-server.js:325:20) {
code: 'ERR_HTTP_HEADERS_SENT'
}
error - Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Would really appreciate some help on this given that this is my first time working with middlewares. Thank you!

getServerSideProps does not render dynamic pages in production, and show 404 error

I'm working with next.js, in development mode everything works fine, but in production mode I have a problem when rendering the pages dynamically.
I have the following path inside the pages folder
pages/user/[id], and this component is where I call the getServerSideProps function.
import headers from '../../headers';
export async function getServerSideProps(context) {
const URL = 'https://somewhere...';
let { id } = context.params;
const apiResponse = await fetch(
`${URL}/${id}/detail`,
{
headers: headers,
}
);
if (apiResponse.ok) {
const data = await apiResponse.json();
return {
props: data, // will be passed to the page component as props
};
} else {
return { props: {} };
}
}
My problem is the following, I need to send in headers the authentication token that I only get when I login and I get the 2FA code, so in build time, that info does not exist and I get a 401 error no authorizate when execute npm run build and when I access to /user/34 for example I get a 404 error.
I have checked these questions at stackoverflow:
NextJs: Static export with dynamic routes
https://stackoverflow.com/questions/61724368/what-is-the-difference-between-next-export-and-next-build-in-next-js#:~:text=After%20building%2C%20next%20start%20starts,can%20serve%20with%20any%20host.&text=js%20will%20hydrate%20your%20application,to%20give%20it%20full%20interactivity.
next.js getStaticPaths list every path or only those in the immediate vicinity?
I have some parts in my app that are statics and works fine, but the problem is with the dynamic paths, as next.js is not creating those paths.
EDIT: I'll include a image with other problem, if after the fetch in the if I just say :
if(apiResponse){ //without 'ok'
}
I'll recieve this errror:
return {
props: data, // will be passed to the page component as props
}
props should be object
return {
props: {data} // or props: {data:data}
}

How to deal with slow loading pages in next js

I have a page which requires making an HTTP request to an API which might take more than 10 seconds to respond, and my host limits me to 10 second executions.
Is there a way that I can load a temporary page or something and then asynchronously load the rest of the data? I'm currently doing this:
export async function getServerSideProps({ params }) {
const res = await fetch(`${process.env.API_USER}name=${params['name']}`)
const videos = await res.json()
const tag_res = await fetch(`${process.env.API_TAG}author=${params['name']}`)
const tags = await tag_res.json()
const name = params['name']
return {
props: { videos, tags, name }, // will be passed to the page component as props
}
}
Lets's move your HTTP request from getServerSideProps to client side (your components)
// Functional component
useEffect(() => {
fetch(...)
}, [])
// Class-based component
componentDidMount() {
fetch(...)
}
If you still want to stick with getServerSideProps, maybe you have to upgrade/switch your host, or implement a proxy/wrapper server for handling your HTTP request and return response as fast as it can

Resources