Next.js Middleware matcher, only use when path contains string (%) - next.js

I have a problem where some website redirects to my website with all entities encoded. I can't change it on their end so I wanted to fix it with a middleware.
Anyone has an idea how i can use the Next.js middleware to only invoke when the path contains a encoded string?
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
console.log('invoked middleware', request.nextUrl.pathname)
const decoded = decodeURIComponent(request.nextUrl.pathname)
if (decoded !== request.nextUrl.pathname) {
return NextResponse.redirect(new URL(decoded, request.url).href)
}
}
export const config = {
matcher: '//%/', // this doesn't work
}

Related

How to use nextjs middleware function properly with getServerSideProps function?

I am trying to use nextjs middleware function. Here I create middleware file and add this code
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
export function middleware(req: NextRequest) {
const { nextUrl: url, geo } = req
const country = geo?.country || 'US'
url.searchParams.set('country', country)
return NextResponse.rewrite(url)
}
export const config = {
matcher: '/'
}
In Index page-
export default function Home(props: any) {
console.log(props)
return (
<h1 className={styles.title}>
Welcome to {props.country}
</h1>
)
}
export const getServerSideProps = ({ query }: any) => ({
props: query,
})
Here I am showing country name into h1 tag from props.country. It works perfectly.
But Here we know in geo object, middleware gives us-
city,
region,
latitude,
longitude,
country
In middleware function I want to sent full geo object and receive from page components to showing all those geo information. How can I do that.
I am trying to sent by this
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
export function middleware(req: NextRequest) {
const { nextUrl: url, geo } = req
//Here I am try to sent full geo object, not only country as string
//Please tell me any way to sent object from here to page component.
url.searchParams.set('country', geo)
return NextResponse.rewrite(url)
}
export const config = {
matcher: '/'
}
From the page component, I want to receive all field such as city, region, latitude, longitude and country. Please help me and I hope I can clear my question.
Passing an object as the value to url.searchParams.set won't work because this value will be serialized in the process.
Instead, you can use JSON.stringify to turn the object into a string. Like so:
url.searchParams.set("geo", JSON.stringify(geo));
Then in your component, you use JSON.parse to turn it into an object again:
const geo = JSON.parse(props.geo);

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)

Vercel / Nextjs middleware request object is just a string

I have a nextjs / vercel middleware that looks like this:
import { NextResponse, type NextRequest } from "next/server";
export function middleware(req: NextRequest) {
console.log('test----> ', req)
return NextResponse.next();
}
I expect this to log some info about the request, but instead it just logs this string,
test----> { sourcePage: '/src/middleware' }
what is going on here?

How can I use auth0 getSession() from nextjs middleware function, or is there some other way to get user particulars via middleware

I have this code in /pages/api/_middleware.js:
import { getSession } from '#auth0/nextjs-auth0'
export default async function middleware(req, ev) {
const session = await getSession(req)
console.log(session)
return NextResponse.next()
}
Whenever I run an API call that hits this I get this message:
error - node_modules#auth0\nextjs-auth0\dist\index.browser.js?b875 (11:0) # Object.getSession
Error: The getSession method can only be used from the server side
I'm not sure it's possible with the #auth0/nextjs-auth0 lib, but I'm lazily just checking if the appSession cookie is in storage like so:
import type { NextRequest } from 'next/server'
export function middleware(req: NextRequest) {
if (req.nextUrl.pathname === '/' && req.cookies.appSession) {
return Response.redirect('/app')
}
if (req.nextUrl.pathname === '/app' && !req.cookies.appSession) {
return Response.redirect('/')
}
}
you can get the session inside of the middleware like this.
import { NextRequest, NextResponse } from 'next/server';
import { withMiddlewareAuthRequired, getSession } from '#auth0/nextjs-auth0/edge';
export default withMiddlewareAuthRequired(async (req: NextRequest) => {
const res = NextResponse.next();
const user = await getSession(req, res);
if (user) {
// Do what you want...
}
return res;
});
// only work on the '/' path
export const config = {
matcher: '/',
};
Found it here, hope it helps!
https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md

Handle 401 error in react-redux app using apisauce

The problem: i have many sagas that do not handle an 401 error in response status, and now i have to deal with it. I have apiservice based on apisause and i can write an response monitor with it to handle 401 error (like interceptors in axios). But i cant dispatch any action to store to reset user data, for example, because there is no store context in apiservice. How to use dispatch function in apiservice layer? Or use put() function in every saga when i recieve 401 response status is the only right way?
you can use refs for using navigation in 'apisauce' interceptors
this is my code and it works for me ;)
-- packages versions
#react-navigation/native: ^6.0.6
#react-navigation/native-stack: ^6.2.5
apisauce: ^2.1.1
react: 17.0.2
react-native: ^0.66.3
I have a main file for create apisauce
// file _api.js :
export const baseURL = 'APP_BASE_URL';
import { create } from 'apisauce'
import { setAPIInterceptors } from './interceptors';
const APIClient = create({ baseURL: baseURL })
setAPIInterceptors(APIClient)
and is file interceptors.js I'm watching on responses and manage them:
// file interceptors.js
import { logout } from "../redux/actions";
import { store } from '../redux/store';
import AsyncStorage from '#react-native-async-storage/async-storage';
export const setAPIInterceptors = (APIClient) => {
APIClient.addMonitor(monitor => {
// ...
// error Unauthorized
if(monitor.status === 401) {
store.dispatch(logout())
AsyncStorage.clear().then((res) => {
RootNavigation.navigate('login');
})
}
})
}
then I create another file and named to 'RootNavigation.js' and create a ref from react-native-navigation:
// file RootNavigation.js
import { createNavigationContainerRef } from '#react-navigation/native';
export const navigationRef = createNavigationContainerRef()
export function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.replace(name, params);
}
}
// add other navigation functions that you need and export them
then you should to set some changes in you App.js file:
import { NavigationContainer } from '#react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}
finally in anywhere you can call this function for use react native navigations
full focument is in here that explain how to Navigating without the navigation prop
Navigating without the navigation prop

Resources