Vercel / Nextjs middleware request object is just a string - next.js

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?

Related

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

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
}

next js middleware error on getting the query from the url

im trying to take the token from the url of a route in my site but the middleware log 3 times the token and in the second time the token is null
while i logged the req url my url that im in is http://localhost:3000/auth/newPassword
but in the second render its http://localhost:3000/_next/static/chunks/pages/auth/newPassword.js?ts=1667894719054
someone knows what is the problem here?
i sent an email for new password to the user email with query token in the url
i wan the middleware to check if the token is valid before accessing the new password route
and then verify it there but its render null for me in the second time and that crashed my project
import { NextResponse, NextRequest } from "next/server";
import { verify } from "jsonwebtoken";
const secret = process.env.JWT_SECRET!;
export default async function middleware(req: NextRequest) {
const url = req.url;
const token = await req.nextUrl.searchParams.get("token")!;
const cookies = req.cookies;
if (url.includes("/auth/newPassword")) {
console.log(url);
if (token === undefined) {
return NextResponse.redirect("http://localhost:3000/auth/signin");
}
try {
verify(token, secret);
return NextResponse.next();
} catch (e) {
return NextResponse.redirect("http://localhost:3000/auth/signin");
}
}
}
That because when you load http://localhost:3000/auth/newPassword next will make a request to http://localhost:3000/_next/static/chunks/pages/auth/newPassword.js?ts=1667894719054 (i guess for hydration), this file basically contains only react related javascript stuff, and you wont that your middleware will match this route, while currently it is (because it contains auth/newPassword).
I sueggest to use middleware with negative matchers :
export const config = {
matcher: [
/*
* Match all paths except for:
* 1. /api routes
* 2. /_next (Next.js internals)
* 3. /fonts (inside /public)
* 4. /examples (inside /public)
* 5. all root files inside /public (e.g. /favicon.ico)
*/
'/((?!api|_next|fonts|500|examples|[\\w-]+\\.\\w+).*)',
],
};
export default function middleware(req: NextRequest) {
const url = req.url;
const token = await req.nextUrl.searchParams.get("token")!;
const cookies = req.cookies;
if (url.includes("/auth/newPassword")) {
console.log(url);
if (token === undefined) {
return NextResponse.redirect("http://localhost:3000/auth/signin");
}
try {
verify(token, secret);
return NextResponse.next();
} catch (e) {
return NextResponse.redirect("http://localhost:3000/auth/signin");
}
}
}

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 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

How can I protect multiple Next.js API routes in next-auth

I build a simple API in Next.js and I use next-auth for authentication.
So far I have to use something like this in every API route:
const session = await getSession({ req });
if (session) {
... do something ...
} else {
... send back a 401 status
}
This seems to go against the DRY principle. Is there a clever way to apply protection to a number of routes in one place, such as Laravel route groups?
Make a middleware!
Disregard the typing if your not using TS
import { NextApiRequest, NextApiResponse } from 'next/types'
import { getSession } from 'next-auth/client'
export const protect = async (
req: NextApiRequest,
res: NextApiResponse,
next: any
) => {
const session = await getSession({ req })
if (session) {
console.log(session)
next()
} else {
res.status(401)
throw new Error('Not authorized')
}
}
Create a middleware that gets the session otherwise returns 401.
See NextJS docs on api middleware.
You can also check out their example in the github repo.

Resources