My dev box is my local machine. The angularjs front-end makes api calls to the express backend and for this I store the api route and other details in a configuration file :
'use strict';
var env = process.env.NODE_ENV || 'development';
if (env === 'development') {
exports.serverPort = 3000;
exports.baseUrl = 'http://localhost';
exports.apiPath = 'http://localhost:3000/';
...//database path
.... // secret key etc
....
} else {
exports.serverPort = process.env.PORT;
exports.baseUrl = 'https://secret-ef-12.herokuapp.com';
exports.apiPath = 'https://secret-ef-12.herokuapp.com:' + process.env.PORT + '/';
....
}
my dev box has NODE_ENV set to 'development'.
On my heroku account, i have NODE_ENV set to 'production'.
Things work fine on dev machine. However on the heroku account (which is the production machine), the application fails to pickup production values.
please note that after deploying to Heroku I am rebuilding the code using my gulpfile.
//a snippet from package.json
"scripts": {
"start": "node server/server.js",
"postinstall": "node node_modules/.bin/gulp build"
},
To reiterate:
After 'git push heroku master', heroku rebuilds my application. No errors are reported.
The angularjs application opens fine when executing 'heroku open'. However all database calls that the application makes go to
http://localhost:3000
instead of using production values.
I do not understand why the angularJs application on the heroku platform does not pickup production values.
Eventually found the issue and solution. Browserify does not read node environment variables. Hence the code above would always return 'development'.
I configured my build file (Gulpfile.js) to generate a development or production build as per a flag passed to it.
What this meant was that my build file (Gulpfile.js) was not searching the environment to tell it whether it should build for dev or prod.
my gulp file is now configured as
gulp build --dev
or
gulp build --production
After deploying code to Heroku, i placed the following line in package.json
"postinstall": "node node_modules/.bin/gulp build --production"
For now this is working for me. It also keeps things simple which i prefer.
Related
I am trying to deploy my React app on a server via PM2. My React app currently has 2 environments: prod and dev. So, in my app folder, I have an environments folder with 2 files: .dev.env and .prod.env. First one is used for calling local APIs, and the second one is for production API URLs. prod.env is shown below:
When I want to start the app, I use the command npm run start:dev or npm run start:prod, depending on which environment I want my app on.
The question here is, when I try to deploy my app via PM2, what command or modification should I do so that I am certain that my app is deployed in production/prod mode?
My current PM2 config file looks like this:
{
apps : [
{
name : "random_app", //name of my app
script : "npm",
interpreter: "none",
args: "run start:dev"
}
]
}
For a production environment you should be serving the built version of the React app (run npm run build and it will output a production-ready version of your app to the build folder).
In order to separate the environment variables, you should use .env.development and .env.production files (the built version will automatically use the .env.production file) - see here for more information.
Then to serve it, you would use pm2 and serve (make sure serve is installed as well npm install -g serve) with a command like pm2 serve <path/to/build/folder> <port> --spa - more info can be found here.
Backstory: I was originally going to switch to parcel v2, ran into a lot of issues, and really starting to dislike parcel. I managed to resolve some issues even though their error messages are very unintuitive. Got to a point that "parcel build" is working but "parcel serve" just doesn't, and can't find an answer online. At that point, I decided to switch to Create React App because it's more "industry" standard. (Thank you for listening to me rant.)
When I was using parcel v1, my setup for the dev environment is running "parcel index.html" (which has hot module replacement), and I'm serving the static files in my backend.
But I don't know how to do that with create-react-script. "react-scripts start" doesn't build to the "build" folder, "react-scripts build" only build once and no reload. I still want to serve the static file. What should I do?
If you're just doing this for development purposes, you could use a proxy from your backend server, pointing to the server run by the Create React App.
For example, with Fastify, you can serve your React app on 1234, and proxy to that from your server, with something like fastify-http-proxy:
const proxy = require('fastify-http-proxy');
// Normal routes here
fastify.register(proxy, { upstream: 'http://localhost:1234' });
fastify.listen(process.env.PORT, '0.0.0.0');
I haven't been able to take the default bare-skin razzle-app (https://github.com/jaredpalmer/razzle) and make it work when I deploy it to Google App Engine. If anyone has experience deploying isomorphic react apps to GAE, then any inputs would be great.
After deployment, it serves the html from the server.js (it only serves the html text for the route but nothing else - no application nor any assets on the page). As a proof of concept, I was returning different html content for different routes, and it apparently works as the html text content as well as head tags were different. However, there are no static assets on the site (no images, css or js).
Build process output:
razzle build
Creating an optimized production build...
Compiling client...
Compiled client successfully.
Compiling server...
Compiled server successfully.
Compiled successfully.
File sizes after gzip:
48.22 KB build/static/js/bundle.c798792d.js
333 B build/static/css/bundle.659481d8.css
the build directory contains:
> build
> public
> static
> css
> js
> media
> favicon and robots
> static
> media (same one as public/staic)
> assets.json
> server.js
Notice that the build/static/js doesn't exist. It's inside build/public/static/js. Is it weird?
On accessing the site (only html text from the server), I checked the head if the client bundle waas sent or not. Itis fetching from a wrong location.
<script src="http://localhost:8081/static/js/bundle.js" defer="" crossorigin=""></script>
My package.json looks like :
{
"name": "my-razzle-app",
"version": "0.1.0",
"license": "MIT",
"scripts": {
"start": "razzle start",
"build": "razzle build",
"test": "razzle test --env=jsdom",
"start:prod": "NODE_ENV=production node build/server.js"
},
"dependencies": {standard
"express": "^4.17.1",
"razzle": "^3.0.0",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-router-dom": "^5.1.2"
}
}
Note that GAE runs npm run start.
I'm currently using flex , standard never worked for me at all. app.yaml looks like:
env: flex
runtime: nodejs
service: web
Running locally :
razzle start
WAIT Compiling...
✔ Client
Compiled successfully in 2.14s
✔ Server
Compiled successfully in 153.86ms
ℹ 「wds」: Project is running at http://localhost:3001/
ℹ 「wds」: webpack output is served from undefined
ℹ 「wds」: Content not from webpack is served from /home/anz/Cogi/timecloud-client/my-app
ℹ 「wds」: 404s will fallback to /index.html
✅ Server-side HMR Enabled!
🚀 started
I can access it at localhost:3000, not localhost:3001.
Use a custom runtime to deploy your Razzle app on App Engine flexible environment. They key to the issue with deployments on App Engine with razzle apps relies on the fact that the process.env.PORT build-time variable's default is set to 3000 and in order to use App Engine you need to make your application's configuration to listen to port 8080 as stated on the documentation.
The process of deployment would consist of creating a Dockerfile, making sure your that the index.js of you razzle app listens to port 8080, building the docker image, pushing the image to Container Registry, creating an app.yaml for the custom runtime, and finally deploying the application to App Engine.
Assuming that your Google Cloud Platform project is ready to deploy applications on App Engine for the sake of simplicity you could use this existing Dockerfile to deploy a simple razzle app, by following this steps:
On your Google Cloud Platform console open the Cloud Shell, set your project config with gcloud config set project YOUR-PROJECT-NAME and run export PROJECT_ID=YOUR-PROJECT-NAME to set the PROJECT_ID enviroment variable to your Project ID.
git clone https://github.com/Sach97/razzle-k8s.git
cd razzle-k8s/web/src
Modify the index.js file in order to make sure the server listens to port 8080.
Change this part of the code const port = process.env.PORT || 3000; to const port = 8080 or just hard code the 8080 value:
export default express()
.use((req, res) => app.handle(req, res))
.listen(8080, function(err) {
if (err) {
console.error(err);
return;
}
console.log(`> Started on port ${port}`);
});
Go back to the razzle-k8s/web folder and build the image with docker build docker build -t gcr.io/${PROJECT_ID}/razzle:v1 . (the . at the end of the command is included).
Push the image to Container Registry docker push gcr.io/${PROJECT_ID}/razzle:v1
Create a deploy folder on the razzle-k8s directory mkdir deploy
Go to the deploy folder cd deploy and create an app.yaml with the following configuration.
runtime: custom
env: flex
service: razzle
Run gcloud app deploy --image-url gcr.io/${PROJECT_ID}/razzle:v1 to deploy your application with a custom runtime and type (Y) for yes to deploy your app using your razzle image.
To view your application simply type gcloud app browse -s razzle and go to the URL. It should be of the form https://razzle-dot-your-project-name.appspot.com.
we're running Next.js in Docker.
When building the image locally and using the production backend, TTFB is virtually zero, but when we deploy this image, TTFB can be up to 30 seconds in some cases.
The production servers are warm servers that don't need to spin up.
I ran four tests to measure TTFB against the same route with the same production backend.
Locally in production mode with docker
Locally in production mode with yarn build && yarn start
Locally in dev mode with yarn run dev
On production
When testing production mode both in and out of docker, TTFB is near zero.
When testing locally in dev mode, TTFB is nearly identical to production.
I've SSH'd into production and confirmed that dev packages are not being installed, so I don't think it's possible that we're running in dev mode.
I would expect production TTFB to be closer to what we're experiencing locally.
What all could be causing the discrepancies between local and production?
Dockerfile
FROM node:10.13.0-alpine
WORKDIR /app
EXPOSE 8080
CMD [ "yarn", "start" ]
COPY . .
RUN yarn install
RUN yarn build
server.js snippet
const express = require('express');
const next = require('next');
const cookieParser = require('cookie-parser');
const jwtDecode = require('jwt-decode');
const { join } = require('path');
const admin = require('firebase-admin');
// serviceAccount used for local development
// https://firebase.google.com/docs/admin/setup
const path = require('path');
const { fetchNewToken } = require('./src/services/Firebase');
let serviceAccount;
const dev = process.env.NODE_ENV !== 'production';
if (dev) {
serviceAccount = require('./credentials/serviceAccountKey.json')
}
admin.initializeApp({
credential: dev ? admin.credential.cert(serviceAccount) : admin.credential.applicationDefault(),
});
const app = next({ dev });
package.json snippet
"scripts": {
"start": "NODE_ENV=production node server.js",
"build": "next build",
"dev": "NODE_ENV=development node server.js",
},
Two things come to mind to check.
The load balancer comes to mind, as if the new image isn't live as soon as it "ought" to be.
If production is serving requests at that time, the new image isn't live yet until all existing requests are finished.
Are you having the issue on every single request even after the new image has been up awhile? Like idk some sort of security check going on?
Is NextJS calling out to a third party provider for idk image compression or somesuch
I have a project handled by Webpack and Gulp. All the API's use a singular ROOT_URL variable and append strings to fetch information from the backend.
For multiple deployments I have different back end servers serving from different databases. Currently for each update I have to change the ROOT_URL and bundle the project in each individual server.
Ideally I would like to just pass a bundle to each deployed server so that each one will reference a local JS file containing their respective ROOT_URLs. Is there a way to do this and/or is there an industry standard?
Anytime you have a something that varies by server generally you would use environmental variables. If you need it in your javascript spa's you will have to pass the environmental variable from your server to your javascript(possibly through a templating engine like ejs if your using node). Ultimately how to do this depends on what backend technologies your using.
With Webpack you can specify the config file location. I use this to have separate dev and production configs. You could use this to have a separate config file for each environment that handles updating the ROOT_URL for you.
"scripts": {
"dev": "webpack --config ./src/config/dev.config.js",
"prod": "webpack --config ./src/config/prod.config.js"
},
You can define environment variables using Webpack's DefinePlugin
Imagine you have a deployment script npm run deploy
In your webpack.config.js add a DefinePlugin
module.exports = {
entry: 'app.js',
plugins: [
new webpack.DefinePlugin({
'process.env.API_ROOT': JSON.stringify(process.env.API_ROOT)
})
]
}
Then in your code:
api.js
var url = process.env.API_ROOT + '/resource'
fetch(url).then(function(){
// do stuff
})
Then when you deploy you can define different API roots
API_ROOT=https://api1.com npm run deploy (will deploy code with API 1)
API_ROOT=https://api2.com npm run deploy (will deploy code with API 2)
.. and so forth. Webpack will automatically replace all the occurrences of process.env.API_ROOT in your code with the correct URL.