How to redirect using getStaticProps? - reactjs

I have a nextjs application with simple user authentication. That means there are some protected routes that I don't want logged out user to get access to. The app compiles successfully in development build and works as expected. But when I use next build, I get an error saying -
Error occurred prerendering page "/createNewAdditionalInfo". Read more: https://err.sh/next.js/prerender-error
Error: 'redirect' can not be returned from getStaticProps during prerendering (/createNewAdditionalInfo).
Here's the code -
export async function getStaticProps(ctx) {
let userObject;
let id;
const cookie = parseCookies(ctx);
if (cookie.auth) {
userObject = JSON.parse(cookie.auth);
id = userObject.id;
}
if (!id) {
return {
redirect: {
permanent: false,
destination: '/',
},
};
}
return {
props: {},
};
}

redirect from getStaticProps in nextJS
export async function getStaticProps({ params }) {
const db = await getDB()
//get id from url
const id = params.postId
//find post in db
const post = await db().collection("posts").findOne({ _id: id })
// The Redirect Happens HERE
//if no post was found - go to Home page
if (!post) {
return {
redirect: {
destination: "/",
},
}
}
return {
props: {
post: JSON.stringify(post),
},
}
}
export async function getStaticPaths() {
return {
paths: [],
fallback: true,
}
}

From the asker's comments:
I need some way to redirect page from server side.
Next.js provides this, see the documentation.
You can achieve this by just switching the function getStaticProps to getServerSideProps. The entire function will execute on the server and your redirection will happen.
e.g.
export async function getServerSideProps() {
return {
redirect: {
permanent: true,
destination: 'https://giraffesyo.io',
},
}
}

I was trying to run getStaticProps and getServerSideProps on firebase hosting. Firebase hosting only supports static site hosting. For anything backend related you have to use firebase functions.
I switched my hosting provider from firebase to vercel and everything works as it should be.

Related

Protected Route by checking JWT saved in user's cookie

I just finished implementing Google social authentication in my NextJS + DjangoRest project following this blog post. I am trying to figure out how to make protected routes that will redirect users if they’re not logged in.
This is how I did it so far:
when user logs in, it saves the jwt_token in the cookie as httponly
uses axios with “withCredentials: true” to access the API endpoint which returns current user data(i.e. email)
saves the user data as a useContext(). When protected page loads, check if UserContext is empty or not and redirects to login page if it is empty.
The obvious problem is the UserContext is reset whenever user refreshes the page, even when the JWT token is still present in the cookies. And I have a feeling this isn’t the right way to implement this.
So how would I implement a similar feature in a non-hacky way? I cannot read jwt-token from cookies in the frontend as it is httponly. Is there a safe way to read user’s JWT token from cookies to test for authentication?
So if I am reading your question right then you can use getServerSide props on your page to detect if the user is authenticated with your api.
function Page({ isAuth }) {
return (
<>
<div>My secure page</div>
//if you return data from your token check api then you could do something like this
<div>Welcome back {isAuth.name}</div>
</>
)
}
export default Page
export async function getServerSideProps(context) {
const isAuth = await tokenChecker(context.cookies.jwt) // In your token checker function you can just return data or false.
if (!isAuth) { //if tokenChecker returns false then redirect the user to where you want them to go
return {
redirect: {
destination: `/login`,
}
};
}
//else return the page
return {
props: {
isAuth,
},
}
}
If this is not what you mean let me know and i can edit my answer.
I modified #Matt's answer slightly and typescript-friendly to solve my problem. It simply checks the user's cookies if they have a jwt_token value inside.
import cookies from 'cookies'
export const getServerSideProps = async ({
req,
}: {
req: { headers: { cookie: any } };
}) => {
function parseCookies(req: { headers: { cookie: any } }) {
var parsedCookie = cookie.parse(
req ? req.headers.cookie || '' : document.cookie
);
return parsedCookie.jwt_token;
}
const isAuth = parseCookies(req);
if (typeof isAuth === undefined) {
return {
redirect: {
destination: `/auth/sign_in`,
},
};
}
return {
props: {
isAuth,
},
};
};

Trouble Building Dynamic Redirects in Next. JS With Server Side Redirects

I tried following the Next.js docs on middleware redirects and have issues when pulling redirects from the server hosted data on contentful.
I was able to perform redirects using middleware with hard coded values, however I would like to pull the redirects from contentful which is not working. Does anyone have any advice on how to perform dynamic redirects using next js being the data is pulled from contentful? My middleware looks like this and the hard coded values work, I tried looping through a JSON object containing urls and pathnames and it is not successful:
import { NextResponse, NextRequest, NextFetchEvent } from "next/server";
import { urlsToRedirect } from "../ursToRedirect";
export async function middleware(req: NextRequest, ev: NextFetchEvent) {
const { pathname } = req.nextUrl;
if (pathname.includes("/orders/")) {
var match = pathname.match(/\/(\d*)\/orders\/(\S*)/);
return NextResponse.redirect(
`https://example.com/${match[1]}/orders/${match[2]}`
);
}
if (
pathname.includes("/page/example") ||
pathname.includes("/collections/example")
) {
return NextResponse.redirect(
`https://example.com/example/`
);
}
if (pathname !== "/") {
for (const item of urlsToRedirect) {
if (item.url.includes(pathname.replace("/", ""))) {
return NextResponse.redirect(
`https://example.com/products/${item.replace}`
);
}
}
}
return NextResponse.next();
}
According to the next.js docs, redirects can be configured in the next.config.js as so:
async redirects() {
return [
{
source: "/pages/deal",
destination: "/deals",
statusCode: 301,
},
{
source: "/pages/:path*",
destination: "/page/:path*",
permanent: true,
},
{
source: "/account/:path*",
destination: "https://example.com/account/:path*",
permanent: true,
},
];
},
However, I need to pull my data from the server and provide it to this funciton. I tried using getStaticPaths or getInitialProps but cannto seem to find where to put this functionality in my app. Any advice is much appreciated!

Redirecting from exported handle hook in sveltekit

I have a sveltekit app and I want to check if the user has an accesstoken from the cookie. I can access it through event.request.headers.get('cookie'); and redirects them to a certain route but I can't find specific syntax from the sveltekit docs.
src/hooks.ts
export async function handle({ event, resolve }) {
const reqCookie = event.request.headers.get('cookie');
const cookieName = 'userid';
const keeperCookie = reqCookie.split(';')
.find((c: string) => c.trim().startsWith(cookieName));
const response = await resolve(event);
if (!reqCookie || !keeperCookie) {
return response.headers.set('location', '/create');
}
return response.headers.set('location', '/login');
}
Redirect doesn't work for me and gives me an error in the console
I just got it using native Response
`return Response.redirect(baseUrl+"/login", 303);`
return new Response('Redirect', {status: 303, headers: { Location: '/login' }});
So you don't need the base url :)

Next.js server side props not loading in time

I'm using supabase and trying to load the user session on the server side. If you refresh the page, it catches there is a user but not on first load (e.g. like when coming from a magic link). How can I ensure it does load before he page?
List item
Here is the page:
import router from "next/router";
import { supabase } from "../utils/supabaseClient";
function Home() {
const user = supabase.auth.user()
if (user){
//router.push('/admin') failsafe, not ideal
}
return (
<div className="min-h-screen bg-elkblue dark:bg-dark-pri">
marketing
</div>
);
}
export async function getServerSideProps({ req }) {
const { user } = await supabase.auth.api.getUserByCookie(req);
if (user) {
return {
redirect: {
destination: "/admin",
permanent: false,
},
};
}
return {
props: { }, // will be passed to the page component as props
};
}
export default Home;
You can use the auth-helpers for help with server-side rendering https://github.com/supabase-community/supabase-auth-helpers/blob/main/src/nextjs/README.md#server-side-rendering-ssr---withpageauth
Do note that it however needs to render the client first after OAuth because the server can't access the token from the URL fragment. The client will then read the token from the fragment and forward it to the server to set a cookie which can then be used for SSR.
You can see an example of that in action here: https://github.com/vercel/nextjs-subscription-payments/blob/main/pages/signin.tsx#L58-L62

Is there a way to redirect 404s from a nested [slug] path to use the parent file in Next.js?

If i have a file structure in this fashion:
/shop/index.ts
/shop/pants/[slug].ts
is there a way to redirect 404s to /shop/ that way the user just sees the parent directory instead of a 404 page?
It depends on what data fetching methods you are using (or not using), for example with getServerSideProps it can be something like that:
export async function getServerSideProps(context) {
// You are probably fetching data somehow from api?
const res = await getData(context.params.slug)
if (res.status === 404) {
return {
redirect: {
destination: '/shop',
permanent: false,
},
}
}
return {
props: {
// Your default case with data
}
}
}
Same works for getStaticProps too

Resources