Next JS route is not resolving in production build - express

I have a following route pattern in the custom server of NextJs project. Everything works fine in the development. But in production build the route is not recognised and goes to 404 page.
Routes
{ name: 'business-with-key', pattern: '/:keyword-in-:location', page: 'packages/index' },
In the development I can get both the params with this code.
packages/index.tsx
export const getServerSideProps: any = async (ctx: NextPageContext) => {
const { query, req } = ctx;
const { keyword, location } = query;
But when I build the project in docker and run it, the route http://localhost:3000/photo-in-london reaches 404 when deep linked. Works fine with SPA navigation.
Any idea what I'm doing wrong here?

Related

How do I mock server-side API calls in a Nextjs app?

I'm trying to figure out how to mock calls to the auth0 authentication backend when testing a next js app with React Testing Library. I'm using auth0/nextjs-auth0 to handle authentication. My intention is to use MSW to provide mocks for all API calls.
I followed this example in the nextjs docs next.js/examples/with-msw to set up mocks for both client and server API calls. All API calls generated by the auth0/nextjs-auth0 package ( /api/auth/login , /api/auth/callback , /api/auth/logout and /api/auth/me) received mock responses.
A mock response for /api/auth/me is shown below
import { rest } from 'msw';
export const handlers = [
// /api/auth/me
rest.get(/.*\/api\/auth\/me$/, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
user: { name: 'test', email: 'email#domain.com' },
}),
);
}),
];
The example setup works fine when I run the app in my browser. But when I run my test the mocks are not getting picked up.
An example test block looks like this
import React from 'react';
import {render , screen } from '#testing-library/react';
import Home from 'pages/index';
import App from 'pages/_app';
describe('Home', () => {
it('should render the loading screen', async () => {
render(<App Component={Home} />);
const loader = screen.getByTestId('loading-screen');
expect(loader).toBeInTheDocument();
});
});
I render the page inside the App component like this <App Component={Home} /> so that I will have access to the various contexts wrapping the pages.
I have spent about 2 days on this trying out various configurations and I still don't know what I might be doing wrong. Any and every help is appreciated.
This is probably resolved already for the author, but since I ran into the same issue and could not find useful documentation, this is how I solved it for end to end tests:
Overriding/configuring the API host.
The plan is to have the test runner start next.js as custom server and then having it respond to both the next.js, as API routes.
A requirements for this to work is to be able to specify the backend (host) the API is calling (via environment variables). Howerver, access to environment variables in Next.js is limited, I made this work using the publicRuntimeConfig setting in next.config.mjs. Within that file you can use runtime environment variables which then bind to the publicRuntimeConfig section of the configuration object.
/** #type {import('next').NextConfig} */
const nextConfig = {
(...)
publicRuntimeConfig: {
API_BASE_URL: process.env.API_BASE_URL,
API_BASE_PATH: process.env.API_BASE_PATH,
},
(...)
};
export default nextConfig;
Everywhere I reference the API, I use the publicRuntimeConfig to obtain these values, which gives me control over what exactly the (backend) is calling.
Allowing to control the hostname of the API at runtime allows me to change it to the local machines host and then intercept, and respond to the call with a fixture.
Configuring Playwright as the test runner.
My e2e test stack is based on Playwright, which has a playwright.config.ts file:
import type { PlaywrightTestConfig } from '#playwright/test';
const config: PlaywrightTestConfig = {
globalSetup: './playwright.setup.js',
testMatch: /.*\.e2e\.ts/,
};
export default config;
This calls another file playwright.setup.js which configures the actual tests and backend API mocks:
import {createServer} from 'http';
import {parse} from 'url';
import next from 'next';
import EndpointFixture from "./fixtures/endpoint.json";
// Config
const dev = process.env.NODE_ENV !== 'production';
const baseUrl = process?.env?.API_BASE_URL || 'localhost:3000';
// Context
const hostname = String(baseUrl.split(/:(?=\d)/)[0]).replace(/.+:\/\//, '');
const port = baseUrl.split(/:(?=\d)/)[1];
const app = next({dev, hostname, port});
const handle = app.getRequestHandler();
// Setup
export default async function playwrightSetup() {
const server = await createServer(async (request, response) => {
// Mock for a specific endpoint, responds with a fixture.
if(request.url.includes(`path/to/api/endpoint/${EndpointFixture[0].slug}`)) {
response.write(JSON.stringify(EndpointFixture[0]));
response.end();
return;
}
// Fallback for pai, notifies about missing mock.
else if(request.url.includes('path/to/api/')) {
console.log('(Backend) mock not implementeded', request.url);
return;
}
// Regular Next.js behaviour.
const parsedUrl = parse(request.url, true);
await handle(request, response, parsedUrl);
});
// Start listening on the configured port.
server.listen(port, (error) => {
console.error(error);
});
// Inject the hostname and port into the applications publicRuntimeConfig.
process.env.API_BASE_URL = `http://${hostname}:${port}`;
await app.prepare();
}
Using this kind of setup, the test runner should start a server which responds to both the routes defined by/in Next.js as well as the routes intentionally mocked (for the backend) allowing you to specify a fixture to respond with.
Final notes
Using the publicRuntimeConfig in combination with a custom Next.js servers allows you to have a relatively large amount of control about the calls that are being made on de backend, however, it does not necessarily intercept calls from the frontend, the existing frontend mocks might stil be necessary.

Nuxt js dynamic route not work after generate

in my project i am using nuxt js. I have a route looks like
service/:slug
After build and generate my all route works perfectly.Bellow code I use to generate dynamic route on generate
generate: {
routes(callback) {
axios
.get('url')
.then(res => {
const routes = res.data.data.map(service => {
return '/services/' + service.slug
})
callback(null, routes)
})
.catch(callback)
axios
.get('https://url')
.then(res => {
const routes = res.data.data.map(offer => {
return '/campaigns/' + offer.slug
})
callback(null, routes)
})
.catch(callback)
}
}
But problem occurs when I create another new item from admin panel after build and generate.
seems I have three route when I run nuxt generate
service/cash
service/profit
now after host my dist folder in server then I hit www.url/service/cash and its work perfectly.
Now I create a new service item in admin panel called send-money
Then when I hit on browser using www.url/service/send-money
Its not working and get 404.
now I cant understand how can I solve this situation.
When using SSG nuxt only generates the available pages in your project. This is how SSG works. Therefore, you need to create a custom script in your server to run ‍yarn build && yarn generate command after creating new page.
For example, let us assume you are creating a blog. When you use ‍‍‍yarn generate, nuxt generates the posts fetched from the database at that specific time and moves them inside dist folder. Therefore, you need to attach a custom script -which you need to somehow create in the backend- to run yarn build && yarn generate after creation of a new post.
If you're using something like firebase static hosting...you only need to make sure you have wildcard rewrites rules in place to prevent 404 errors. In this case this is what I have in my firebase.json files.
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]

Problem with deep linking when published with Expo

can you please give me a hand with Deep Linking. When I work in a development environment it works for me with the URL: exp://192.168.100.4:19000/--/resetPassword/rzBsP3tJ, however, when I publish it, I open it with the URL: exp://exp.host/#user/#nameApp/--/resetPassword/rzBsP3tJ?release-channel=testing, it just opens the app, but it doesn't redirect me to the screen I want. What I can do?
An assumption here: you talk about screens so assuming you are using react-navigation and making use of LinkingOptions.
The issue is caused by a mismatch between how Expo defines a linking URL and how react-navigation defines LinkingOptions. LinkingOptions is in terms of a path, not a URL that has any query params. In development there are no query params, but an app that is is published to a channel has the 'release-channel' query param so this will never match in react-navigation's LinkingOptions.
The solution is just to remove the release-channel query param from the URL returned by Linking.createURL:
import * as Linking from 'expo-linking';
import { URL } from 'react-native-url-polyfill';
const urlWithReleaseChannel = Linking.createURL('/')
const parsedUrl = new URL(urlWithReleaseChannel)
parsedUrl.searchParams.delete("release-channel")
export const resetLinkingOptions = {
prefixes: [parsedUrl.toString()],
config: {
screens: {
ResetPassword: {
path: 'resetPassword/:id'
}
}
}
}
This works for both development and deployed apps: if present, release-channel then behaves as a regular query param. Note that react-native-url-polyfill is required as a project dependency as out of the box the RN URL is not fully featured and lacks in this case the ability to delete a query param.

cannot GET /pageName when using React Navigation on web project

Browser page refreshes break my application.
I'm using react navigation in an expo universal application. When a user refreshed the browser on route "/", everything works as intended. When a user refreshed on any other route, e.g. "/search" I see this served:
Cannot GET /search
I have looked at deep linking
import * as Linking from 'expo-linking'
...
const prefix = Linking.createURL('/')
const linking={
prefixes: [prefix],
config: {
screens: PathsByName,
},
}
<NavigationContainer linking={linking}>
...
</NavigationContainer>
as well as a custom webpack
config.devServer.historyApiFallback = {
index: 'index.html'
}
but I still cant get simple page refreshes to load properly.
The problem was I customized the webpack config incorrectly.
This config works,
const createExpoWebpackConfigAsync = require('#expo/webpack-config')
module.exports = async function (env, argv) {
const config = await createExpoWebpackConfigAsync(env, argv)
// avoid improper settings here
return config
}

CRA embedded in express app breaks express routing

I have created an CRA app and have a couple express routes loading the CRA build files, for example:
app.get('/files', async (req, res, next) => {
...
try {
res.format({
html: function() {
const fileLoc = './public/react_ui/build/index.html';
const stream = fs.createReadStream(path.resolve(fileLoc));
stream.pipe(res);
}
});
} catch (e) {
next(e);
res.redirect(SEE_OTHER.http_status, '/login');
}
});
Prior to added the CRA, the express app exposed the /public folder like this:
// access to express App code files
app.use(express.static(__dirname + '/public'));
Now that I have the CRA app embedded, I wanted to expose the build files like this, otherwise the index.html file created by building the CRA does not know where the /static/js/* are:
// access to React App build files
app.use(express.static(__dirname + '/public/react_ui/build'));
However, it breaks the express routing. For instance, when I logout of the app, it is supposed to send me to the endpoint / and this checks if I am logged in or not, if not, then it is supposed to send me to the login page like this:
app.get('/', function(req, res) {
...
isLoggedIn(req, function(status) {
switch (status.status) {
case 200:
res.redirect(303, '/loader');
break;
default:
res.redirect(303, '/login');
}
});
});
However, this is what is breaking. If I remove the command to expose the /build folder above, then the routing works again and I am sent to the login page, but accessing the CRA pages breaks, because the build files are NOT FOUND.
// access to React App build files - if removed, routing works again
app.use(express.static(__dirname + '/public/react_ui/build'));
Does anyone have any suggestions as to why this is happening? I don't know if this is a react app issue, an express issue, or something else. Any insights would be helpful.
You have conflicting routes.
app.js
app.use('/', express.static(__dirname + 'path/to/static/build'));
// Dont use '/' as it used for static route.
app.use('/auth', (req, res) => {
...
isLoggedIn(req, function(status) {
switch (status.status) {
case 200:
res.redirect(303, '/loader');
break;
default:
res.redirect(303, '/login');
}
});
})
Note you can use whatever route for static build. I have given general convention.