I'm Developing a full-stack web application with Node js and express js as the backend and using Next Js for the front end and JWT for the authentication, I'm setting the JWT token with the cookie in the browser. The cookie is set successfully in the browser, but whenever I try to get the cookie from the Express server to check whether the user is valid or not, the cookie does not receive from the browser. I tried several ways to get the cookie from the browser but it doesn't respond. but it is receiving the cookie token from the Postman/thunder client.
note: The frontend port is 3000 and the backend port is 5000.
You can suggest another way for the user authentication with next js with external express server.
res.cookie("userToken", token, {
expires: new Date(Date.now() + 9000000000),
})
and the JWT verify code is here
const jwt = require("jsonwebtoken");
const jwtToken = (req, res, next) => {
try {
const userToken = req.cookies?.userToken;
if (userToken) {
const verify = jwt.verify(userToken, process.env.JWT_SECRET);
req.user = verify;
}
next(); // for getting the api result back to client
} catch (error) {
next(error);
}
};
module.exports = jwtToken;
I am at early stages setting up a next.js application, I only had experience using react so far.
I setup docker with a frontend app (next.js) on localhost:3000 and a backend app (node.js/express) on localhost:5000. They both work.
Now I am trying to call an express endpoint from the frontend, what I am doing is:
const registerUser = async event => {
event.preventDefault()
const res = await fetch(
process.env.NEXT_PUBLIC_SERVER + '/user/signup',
{
body: JSON.stringify({
username: event.target.name.value,
email: event.target.email.value,
password: event.target.password.value
}),
headers: {
'Content-Type': 'application/json'
},
method: 'POST'
}
)
result = await res.json()
}
and I am getting an error saying
Access to fetch at 'http://localhost:5000/user/signup' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
just a note: the endpoint works as expected using Postman.
I made some research and I find a few resources saying I should call an internal next.js endpoint (pages/api), and from there call my api. Is this the best practice with next.js? In react I just use to call the api directly.
Other than just how to solve this, I would like to know what's the best practice in this case? Thanks.
If you have separate servers for frontend and backend (for example, next.js and express) that cannot listen on the same port, there are two broad alternatives:
Either the browser loads the frontend from one server and makes API requests to the other server
next.js <-- browser --> express
This requires the backend app to set CORS headers, for example, using cors and the statement
app.use(cors({origin: "host of next.js", ...}));
Or the browser makes all requests to the port of next.js, and this forwards all API requests to the other server
browser --> next.js --> express
No CORS is necessary in this case, but API requests take more hops than before. So it is simplicity vs performance (like so often).
First of all, are you sure you need an Express BE? The power of Next.js relies in its serverless approach, most of times, unless you have a very complex BE, you can do everything with serverless functions.
If you really need to have a separate express server for your Next application remember that you will lose some important Next features:
Before deciding to use a custom server, please keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like serverless functions and Automatic Static Optimization.
Usually to address the CORS issues in dev environment, since you need FE to run on a different PORT from BE to have Hot Reload, when you use React the best approach is the proxy approach, you can just add an entry to package.json on the React project,
"proxy": "http://localhost:5000" (if your server runs on PORT 5000)
Source: https://create-react-app.dev/docs/proxying-api-requests-in-development/
This way all the http traffic is going to be redirected on port 5000 and will reach your Express server, while keeping having hot reload features and your client files running on port 3000.
By the way, that's the case if you have a standard React FE and a custom Express BE, if you are using NextJS even with a custom Express Server, you need to create the server and to connect it using Next:
/ server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const hostname = 'localhost'
const port = 3000
// when using middleware `hostname` and `port` must be provided below
const app = next({ dev, hostname, port })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer(async (req, res) => {
try {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
if (pathname === '/a') {
await app.render(req, res, '/a', query)
} else if (pathname === '/b') {
await app.render(req, res, '/b', query)
} else {
await handle(req, res, parsedUrl)
}
} catch (err) {
console.error('Error occurred handling', req.url, err)
res.statusCode = 500
res.end('internal server error')
}
}).listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://${hostname}:${port}`)
})
})
source: https://nextjs.org/docs/advanced-features/custom-server
Again, I suggest you to deeply evaluate if you really need a custom express server for your app, because most of times you don't, and development experience is much smoother in a serverless environment!
I'm trying to make a request to the express/nodejs backend using nextjs
in pages/reader.js, I have
Reader.getInitialProps = async ({query}) => {
const res = await fetch('http://localhost:3000/api/books/reader/' + query.id);
const json = await res.json();
return {book: json}
};
Unfortunately, that overwrites the cookies stored in the request object on the backend. When I do a console.dir(req.cookies) in the backend node js, express code, I get undefined in book.js where the reader code is.
How can I fetch without overwriting the request object in the express backend?
Look at the example in https://github.com/zeit/next.js/blob/canary/examples/auth0
In the file ssr-profile.js it shows how you can forward your cookies in the request to the server:
// To do fetches to API routes you can pass the cookie coming from the incoming request on to the fetch
// so that a request to the API is done on behalf of the user
// keep in mind that server-side fetches need a full URL, meaning that the full url has to be provided to the application
const cookie = req && req.headers.cookie
const user = await fetchUser(cookie)
I am able to do the following on PostMan
1) POST method to login to company server.
2) Make other requests as a logged in user on company server.
I have created a nodejs app to communicate with the company server.
I am using axios library to make said communications.
after Logging in to company server, any other calls don't recognize me as an authorized user.
What could be the differences that i could in turn recreate on axios to have that session persistance?
In the browser you use withCredentials in axios - this option automatically saves your session between requests. But in node.js this parameter does not work because axios uses the http node.js module instead of XHR.
In node.js, you can use an axios instance for save cookie between requests.
Simplest way is:
Create instance
const BASE_URL = "https://stackoverflow.com";
// Create instance of axios which utilizes BASE_URL
const axiosInstance = axios.create({ baseURL: BASE_URL });
Write createSession function
const createSession = async () => {
console.log("create session");
const authParams = {
username: "username",
password: "password"
};
const resp = await axios.post(BASE_URL, authParams);
const cookie = resp.headers["set-cookie"][0]; // get cookie from request
axiosInstance.defaults.headers.Cookie = cookie; // attach cookie to axiosInstance for future requests
};
And make call with session cookie
// send Post request to https://stackoverflow.com/protected after created session
createSession().then(() => {
axiosInstance.post('/protected') // with new cookie
})
Be careful, your authorization method may differ from the presented - in this case you can just change the createSession method. If your session has expired, you can login again directly or using axios.interceptors - I attached a link to gist.
Also you can use cookie-jar with axios (link below)
For more info:
https://github.com/axios/axios#creating-an-instance
https://github.com/axios/axios#interceptors
https://gist.github.com/nzvtrk/ebf494441e36200312faf82ce89de9f2
https://github.com/3846masa/axios-cookiejar-support
I have some issues with a server that does not support IPv6 requests from Apple Application Review. So they reject my update.
And i'm thinking of making a request handler as a middle server, with nodejs.
So my app will send the requests in my new server, which server will send the request to the old server, take the response json back, and serve it back as well in my app.
So lets say the old webserver request was the following
https://www.example.com/example/api/index.php?action=categories&subaction=getproducts&category_id=100304&limit=0,30
But the request parameters are not always the same!
It may vary but the main URL is always the same
https://www.example.com/example/api/index.php?
The question is how to get the request params dynamically, make a request to the old webserver and return the response to the request of the new webserver?
You just need a very simple proxy like this;
const express = require('express')
const request = require('request')
const app = express()
const BASE_URL = 'http://www.google.com' // change accordingly
app.use('/', (req, res) => {
request({
url: BASE_URL + req.originalUrl
}).pipe(res)
})
app.listen(8900, () => console.log('Listening...'))
req.originalUrl will allow to concatenate the path + the query string to your base url