How to deploy Next.js app without Node.js server? - reactjs

I was hoping to deploy a Next.js app with Laravel API. I had developed React apps with CRA and in those I used the API server to serve the index.html of the CRA as the entry point of the app.
But in Next.js, after development I get to know that it needs a Node.js server to serve (which is my bad, didn't notice that). There is an option next export that builds a static representation of the Next.js app and it has an index.html. I am serving the index.html as the entry of the app by my Laravel API. It is serving the page, but just some of the static contents.
What I was hoping to know is it possible to host the aPI and the Next app from a single PHP shared hosting without any node server? If so, how? If not so, what could be the alternatives?

Actually the acepted answer is completly wrong, when you do yarn build and in your package.json is set like "build": "next build && next export", you will get an out folder which all the items in there are used to build without node.js server
Now since you are using laravel, and you use the out folder you will only load half of the page because the routes are not set properly. for that to work you need to edit your next.config.js edit it to
module.exports = {
distDir: '/resources/views',
assetPrefix: '/resources/views',
}
These will set the root directory to the root one in Laravel. now this will work for SPA (single page application) only for the dynamic routes you need to match with a view file for each one that you have in your out folder
For each route that you have you need to create a new "get" route in laravel
Route::get('/', function () {
return require resource_path('views/index.html');
});
Route::get('/contacts', function () {
return require resource_path('views/contacts.html');
});
Route::get('/post/{slug}', function () {
return require resource_path('views/post/[slug].html');
});
Notice that you can pass a wildcard for dynamic routes and they are all gonna work. once you do that and you deploy route out folder inside /resources/views in Laravel it's going to work

Apparently there is no alternative to nodejs server, which is not an option for me currently, so I unfortunately had to abandon next.js and create a CRA app and used as much from the next.js as I could.

Related

Configure React Dev Server within an ASP.NET Core Application

I have an existing ASP.NET Core application (that uses razor pages) and I am trying to convert it, one component at a time, to React until I can completely make it a SPA. The idea is to create an entry point for each of my razor pages until I can combine them all into one SPA. I have most of this working except for the use of webpack-dev-server to serve my bundles. The problem I am having is the ASP.NET app runs on port 44321 and the dev server runs on port 8080 so the script tags in my .cshtml files cannot see the bundles that are being hosted from webpack.
I can temporarily change them from:
<script src="./dist/[name].bundle.js"></script>
To something like:
<script src="http://localhost:8080/[name].bundle.js"></script>
To get around this, but this is not long term solution.
I have created a sample application to showcase what I am trying to accomplish here: https://github.com/jkruse24/AspNetReact.
Is there any way to either get my ASP.Net application to listen on the port that webpack-dev-server is serving to (without changing my script tags) or to have my webpack-dev-server serve to the port that my ASP.Net app is running on?
I have tried to use the .NET CORE SPA middleware (Microsoft.AspNetCore.SpaProxy) but either I have not configured it correctly or I am misunderstanding what it is used for. Upon adding in the below code (which is commented out in my github sample) my application still looks at the .\dist directory for my bundles (which are still there from running actual builds).
if (env.IsDevelopment())
{
app.UseSpa(spa =>
{
spa.Options.SourcePath = "./ClientApp";
spa.UseReactDevelopmentServer(npmScript: "start");
spa.UseProxyToSpaDevelopmentServer("http://localhost:8080");
});
}
I ended up getting this working using the .NET Core SPA Middleware. When I originally tried to used the middleware, it was working fine, but I didn't have my webpack dev server configured to serve my bundles to the correct location.
As you can see above, I was serving them to
http://localhost:8080/[name].bundle.js
when they needed to be served to
http://localhost:8080/dist/[name].bundle.js
My problem was that my webpack publicPath was not set correctly. I made an update commit on my repository here. More specifically, this was the file diff that solved my problem.

Nextjs 404 error on reload/ refresh action

I'm using Nextjs for a front-end application and dotnet core 3.1 for the Web API. There are some pages that are static and other that are dynamic I followed the official documentation to achieve this. On development mode (local machine) everything works fine. Both static and dynamic routes are working properly and fetching data from the dontnet core Web API.
However, when publishing the Nextjs app following this steps:
yarn build
yarn export
An out folder is generated at the root of the project
The content of that folder is uploaded to the server
After, the deployed files are uploaded and when loging to the app, it redirects to the main page (until here is working OK), but as soon as I click on the reload page botton (Chrome) I am gettint the 404 error.
Looking at the console in the developer tools I got this:
I found this Stackoverflow link with same issue but there the answer is to use Express for server routing. In my case I am using dotnet core Web API for server requests. So, not sure how to do that.
Is there a way to fix this from the client side? Might be a configuration is missing?
The only thing I noticed while doing the export was a message saying: No "exportPathMap" found. Not sure if that would the the reason.
I had got similar issue in react when all of my pages after building and exporting had ".html" extensions. I solved it by the following code in next.config.js file.
next.config.js
module.exports = {
exportTrailingSlash: true,
}
Note: Do not work with the above code while in development. Use it just before building the project.
You can find the documentation link here: https://nextjs.org/docs/api-reference/next.config.js/exportPathMap#adding-a-trailing-slash.
UPDATE
The above code was for next.js v9.3.4 which I was using at that time. For newer versions below code should be used according to docs.
next.config.js
module.exports = {
trailingSlash: true,
}
it has been fixed update your nextjs package
npm install next#latest
based on the current version of Next js you have, visit here to see if there's any breaking change before updating what you have
I had a similar issue where after deploying the out folder created by next export all URL's would redirect me to the homepage. Everything was working fine during development and all URL's were accessible with next/link but in order to access pages with a URL I had to add a .html extension at the end of the URL.
Because I needed a quick workaround I added a useEffect block in the _app.tsx file for rerouting so that upon landing on the homepage it would act as if a Link component was clicked redirecting to the entered URL.
useEffect(()=>{
router.push(window.location.href)
},[])

Deploying Create React App to a subdirectory but make API calls to parent path

I'm deploying my Create React App to a specific path in a larger non-React webapp. For example, I will say the webapp path is www.example.com and the React app is deployed at www.example.com/react/
I have done this by setting the "homepage" property in package.json of the React app to "homepage": "/react", which does properly serve the static files from the /react/ path on my server.
However, when I make API calls from my react app, they go to /react/api/etc instead of /api/etc.
I can configure axios to use a hardcoded base path of www.example.com, but I deploy this to multiple environments with different URLs and need a solution that doesn't rely on a hardcoded value.
I could also write a workaround on the server side, but it would be less clean / mess with my logging and request statistics.
I would love a clean solution if one exists.
what if you used the window.location property in your axios config object:
{
baseURL: `${location.hostname}/api/` // or window.location.hostname
}

Serve react project on dev througth proxy

I've split my react application in 3 different projects, using CRA for all of them, auth, X and Y. User is first sent to auth, then I redirect him to either X or Y based on some info.
It works perfectly on PRODUCTION environment (because they run on the same domain), but on dev, X and Y failed to authenticate the user, because they run on different ports (different domains) the data in local storage is not shared between auth, X and Y.
I've tried to find a way to use a reverse proxy (http-proxy) to host the React dev servers on the same domain, but failed too, because the services could not find the assets/static folder, resulting in 404. Also tried http-proxy-middleware, as it is recommended on the CRA docs page, but failed to do so. Is there an easier way that I'm not seeing?
Edit: Found something new, but also failed. Used react-rewired to override CRA scripts, to use PUBLIC_PATH on DEV, but now my bundle.js returns an index.html file.
The following code does redirect to the accordingly react project, but the assets are requested to the wrong path.
const apiProxy = httpProxy.createProxyServer();
app.all("/login/*", function(req, res) {
console.log('redirecting to Login');
apiProxy.web(req, res, {target: servers.login});
});
app.all("/implementacao/*", function(req, res) {
console.log('redirecting to Implementation');
apiProxy.web(req, res, {target: servers.implementation});
});
So I used react-rewired to change the public path
const {
override,
} = require('customize-cra');
module.exports = {
webpack: override(
(config) => {
config.output.publicPath = '/login/';
return config;
},
),
jest: config => {
return config;
},
devServer: configFunction => (proxy, allowedHost) => {
return configFunction(proxy, allowedHost);
},
paths: (paths, env) => {
return paths;
}
};
Now, the assets requests are made correctly to /login/, but nothing the dev server always return an index.html file.
Even with react-app-rewired, to override config, and use publicPath on dev, the assets will not be served from the publicPath.
There is already a pull request on CRA to use PUBLIC_URL in dev mode.
Is there an easier way that I'm not seeing?
Another approach would be to use multiple React Single Page Applications (SPAs) inside one application, see crisp-react. E.g. instead of 3 CRAs in 3 applications/projects have 3 SPAs in one application/project. The backend surely can get data from other backend servers transparently for each SPA.
how do I migrate from a set of existing CRA projects to using crisp-react ?
Background
crisp-react comes with two stock SPAs called ‘First’ and ‘Second’. Both render some explanatory/sample UI.
Migration overview
1.Pick one CRA project and migrate it to the ‘First’ SPA. When finished, you have two CRAs left and two crisp-react SPAs: ‘First’ (renders your UI) and ‘Second’ (still renders the sample UI). Rename the ‘First’ SPA to give it more meaningful name.
2. Pick another CRA and migrate it. When finished, you have one CRA left and two crisp-react SPAs both rendering your UI.
3.Modify crisp-react to add the third SPA and then migrate the remaining CRA to the third SPA.
Migration steps (sketch)
1.1 Follow crisp-react Getting Started.
1.2 The landing page of the First SPA is rendered by crisp-react/client/src/entrypoints/first.tsx
The landing page of the CRA is rendered by src/index.tsx
Replace the content of the former with the latter.
1.3 The first CRA consists of React components: src/App.tsx and others you added. Copy the components to crisp-react/client/src/components/from-first-cra/
1.4 Ensure crisp-react client app compiles: From crisp-react/client/ execute: yarn compile
1.5 Ensure crisp-react client app builds: From crisp-react/client/ execute: yarn build
1.6 Ensure crisp-react client looks ok without backend data: see client Usage Scenarios.
1.7 Get the backend (e.g. Express) involved: see backend Usage Scenarios.
1.8 Milestone reached: browser can be pointed to backend (Express) and get from it html files and bundles - which results in the first SPA rendering initial UI (to the extent possible without data supplied via API enpoints).
1.9 Decide how the the first SPA will get data from API. 3 basic choices here:
- the API endpoints are implemented in Express so you can retire your backend servers
- Express does expose API endpoints but acts as a reverse proxy getting data from your backend servers
- Express knows nothing about API and data supplied by backend servers that are queried directly by the components inside the first SPA.
2.1 Second SRA
as above
...

Build React app with express backend for domain http://example.com

I have a web application in React that I needed to implement a contact form. The application is created using create-react-app and the server folder added. For the form I used sendgrid mail. Does the server work on port 4567, how do the app build to work on the domain? It is a one-page application.
Thx, it is important.
When running in production, a React app is simple HTML, CSS, and JavaScript. These files are sent from your server to a client when requested in the same way that requests/responses are handled for any web page. There are a few steps that need to be done before your React app is ready for production
1: Create a Production Build
First you need to create a production build of your app. This process takes all of your separate .js or .jsx files and puts them together into a single minified file, and the same for .css. Then your index.html is updated to include a link to the CSS and script to the JS. This is done so that only three files will need to be sent rather than the 10s or 100s that exist in development.
If you used create-react-app to start your application, you can use the command:
npm run build
to do this. Otherwise, you need to have webpack installed, and then run:
node_modules/.bin/webpack --config webpack.prod.js --mode production
(which you might want to add as a script to package.json).
See React: Optimizing Performance for more.
2. Serve your Application
Now your server should have a route for your application and when it receives a request on that route, the server should respond by sending index.html from your client/build/ directory (where client/ is the directory of the React app).
Here is an example with Node/Express as the server (in app.js):
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname), 'client', 'build', 'index.html');
});
Note that this is just the way to send a static file using Node and can easily be done with any server.
Additional
You mentioned you want to submit forms with your application. If your routes for receiving POST requests match the routes that the forms are on (e.g. form is on /form and server listens for POST on /form) you can just use the default HTML form submission. However this is not a great way to do things when using React because then routing will be controlled by your server rather than by React. Instead you should use some sort of AJAX method to submit the form.
Since your server is now serving your React app (rather than React serving itself as in development), you can just make relative requests and those requests will be made to your server. For example the request (using the fetch API):
const models = await fetch('/api/models');
Will be made to your_host/api/models by default.
in the package.json add
"proxy": "http://localhost:4567"

Resources