How to self-host Gatsby v4 with Server Side Rendering (SSR) capabilities - reactjs

I'm trying to set up a site to host static content, using GatsbyJS. Some of my pages use SSR. When I run it using gatsby serve from the project root, i'm able to view these pages. I'm not sure how I can deploy and host this application with SSR capabilities. according to this page, gatsby serve is to be used only to test the production build, which infers that there may be a different strategy to host actual production.
Our goal is to deploy to a virtual private server (vps) or Azure App Service, where we have more or less full control of our environment.
I was able to host the static site using this script on Azure App Service (win-node16):
const express = require('express');
const gatsbyExpress = require('gatsby-plugin-express');
const app = express();
const port = process.env.PORT || 8080;
const dev = process.env.NODE_ENV !== "production";
// serve static files before gatsby
Express app.use(express.static('public/'));
app.use(gatsbyExpress('config/gatsby-express.json',
{
publicDir: 'public/',
redirectSlashes: true,
}));
app.listen(port, function() {
console.log(`App started on port ${port}`);
});
this seems serve the static pages properly, but all pages with SSR returns 404. I surmise it may be due to the fact that those pages did not have html stubs generated. as I'm new to Express, i'm also not sure if this is the right approach
if anyone has advice on hosting that would be appreciated.

unfortunately this plugin gatsby-plugin-express is 2 or 3 years old and therefor doesn't support server-side rendering.
You can see this question How to properly run gatsby with SSR in a production environment for more options.
TL;DR: There is no option for self-hosting it unless you will write it yourself.

Related

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

Configuring a React-Express App on Heroku to Pull Data in an Ajax Request

I have a React-Express app that pulls data from a MongoDB database on mLab.
On my server.js file, I have the api port set as such:
var port = process.env.PORT || 3001;
And it listens as such:
app.listen(port, function() {
console.log(`api running on port ${port}`);
});
Currently, in my React app, one of the components makes an AJAX call to the database on mLab using the url of "http://localhost:3001/api/data", which works fine and pulls the data I requested.
However, when I deploy the app to Heroku, I'm not sure how to configure the server.js and the url in the React app, so the React app is able to pull the data from the database.
I've conferred with mLab, and there are no issues, and I've conferred with Heroku, and this is beyond the scope of their support.
UPDATE: Is it that the process.env.PORT variable needs to be set or redirected?
Any ideas what I need to do?
Thanks!
If your express app is serving both your bundled react app and your api, you need to make sure that express knows that the /api endpoint needs to be NOT served to the react app.
Not sure what your server code looks like, but this has worked for me:
if (process.env.NODE_ENV === 'production') {
app.get(/^\/(?!api).*/, (req, res) => { // don't serve react app to api routes
res.sendFile(PATHTOREACTBUNDLE));
});
};
Basically, you want to tell express that, if in production mode (deploy on heroku), serve all endpoints, except the api endpoint (/^/(?!api) to your react bundle.

Yarn / IIS URL rewrite

I have a React app deployed to production as a bundle.js and index.html pair, served up by IIS under a url like:
https://my-app.com
This talks to a .Net WebAPI backend, served up by IIS as a virtual application at
https://my-app.com/api
This allows the React app to make requests to the relative url /api/XXX and successfully hit the API.
My problem is when developing with these 2 projects running locally.
The API is run in Visual Studio 2015 in debug mode and IIS Express makes it available at http://localhost:56585/api/ (easily configurable).
While developing the React app I use JS development tools such as yarn, which provides a webserver hosting the app at http://localhost:3000.
The different webservers and different ports precludes the use of a relative url to contact the API when running locally. This means I have to hardcode the url my api hits, and change it before I build based on the target environment. Eg:
//const base_url = '/api/'; // production
const base_url = 'http://localhost:56585/api/'; // development
Is there any way to configure some kind of rewrite rule such that requests my client makes to /api/XXX get remapped to http://localhost:56585/XXX, without rewriting requests to the root url (eg http://localhost:3000/index.html)?

Setting environment for AngularJS development with existing OSGi/Rest serverices

How to setup the dev environment where the UI is to be re-done using AngularJS and typescript etc but we already have an existing set of services hosted in rest/osgi bundles.
All the development models with AngularJS and type script talks about node/npm etc but how do we hit existing services with that? do i need to enable cors etc for development?
how is UI development done in these kind of projects as i believe not all projects are done from the beginning and have liberty to use node at server.
Well, usually from an angular app you define some kind of angular service that talks to your api in a standard way.
It's true that most "Frontend" projects use a mocking server during development but it isn't hard to hard to use a real server for this, provided it's not you own production server, obviously.
About the cors issue, I use to let CORS fully open during development ,and have a minimally accesable configuration on production, depending on your project.
After some research we have finalized the DevEnv and it's working out very well.
used angular cli for development
used proxy server to make the calls made to 4200 port to redirect to express server running at port 3000
package.json:
"start": "ng serve --proxy-config proxy.conf.json",
finally wrote a small express server to login to existing server and then pipe all requests!
This was our code:
var app = express();
//enable cors
var cors = require('cors')
app.use(cors());
//relay all calls to osgi server!!
app.use('/a/b/c/rest', function (req, res) {
var apiServerHost = "https://" + HOST + ":" + PORT + "/a/b/c/rest";
try {
var url = apiServerHost + req.url;
req.pipe(request(
{
headers: headers,
url: url,
"rejectUnauthorized": false
})).pipe(res);
} catch (error)
{
console.log("Error " + error);
}
} // Added by review
No mock required

Different values configuration constants

I have an AngularJS website in different environments (dev, prod). I also have some configuration constants that are different depending on the environment (i.e. to send requests to an API).
I have read many posts that solve this with Grunt by creating a dynamic file. Is there any other "more homemade" approach?
UPDATE
In the client side, Im runníng my AngularJS client application in an Nginx web server.
You could make your Node.js server decide which configuration file to serve to your client.
var app = express();
app.set('env', process.argv[2] || 'development');
//...
app.get('/libs/config.js', function(req,res){
var env = app.get('env');
if(env === 'development') {
res.sendfile('config/dev-config.js');
} else if (env === 'production') {
res.sendfile('config/prod-config.js');
} else {
res.send(404);
}
});
And in you angular client you do:
<script src="libs/config.js"></script>
And your servers you run your node doing
node app.js development
node app.js production
Also, I think Express has direct support to configure your current environment using process.env.NODE_ENV, so most likely, all you have to do is to set the NODE_ENV=production environment variable in your production Node.js server and you won't need to pass the configuration from the command line directly.
This is a hack, but my home made approach is to override the address of my API using a static hosts entry.
/etc/hosts
192.168.1.100 api.mydomain.com
It is dirty but it works.

Resources