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
Related
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');
Every time I install a new dependency with npm it's like cutting the read wire and hoping the bomb doesn't go off.
This error keeps coming up now:
[BABEL] React Refresh Babel transform should only be enabled in
development environment. Instead, the environment is: "dev_local". If
you want to override this check, pass {skipEnvCheck: true} as plugin
options.
It's a Gatsby app and I can't figure out how or where to achieve {skipEnvCheck: true} I've tried various things in gatsby-node.js such as:
exports.onCreateWebpackConfig = ({ actions, plugins }) => {
actions.setWebpackConfig({
plugins: [
plugins.define({
skipEnvCheck: true,
}),
],
});
};
But it doesn't help. I have different development .env configs (e.g. this "dev_local" environment) for testing with different settings and I want to keep doing that.
Or am I just chasing ghosts here and really there is some other problem with the dependencies?
I ran into the same error when attempting to deploy a Gatsby app to Heroku. A frustrating rabbit hole ensued, but I eventually narrowed the fix down to using gatsby serve instead of the default gatsby develop for the start script.
In package.json:
"scripts": {
"start": "gatsby serve --port $PORT --host 0.0.0.0",
"build": "gatsby build",
...
},
I've issue in connecting react and express nodejs. On production they are fine together but on dev they are not connected. I've read a lot of articles and none of them worked with me. I'm deploying on heroku. And on production how can I replace local host end points with heroku url.
const express = require("express");
const path = require("path");
const app = express();
const publicPath = path.join(__dirname, "", "../build");
app.use(express.static(publicPath));
app.get("*", (req, res) => {
res.sendFile(path.join(publicPath, "index.html"));
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log("Server is up on port " + port);
});
for package.json/
"scripts": {
"start": "node server/server.js",
"dev": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
Assuming your reactjs and node applications are deployed seperately, you can follow these steps:
For backend dotenv package is very popular, and easy to setup.
1-) add .env file to the root folder with your environment variables like this:
MONGO_URL=mongodb://localhost:27017/
Please note that there should be no empty space.
2-) In your main file, import dotenv as soon as possible before reading any variables.
require("dotenv").config();
3-) Now you can accesss your variables like this:
const dbUrl = process.env.MONGO_URL;
4-) put your .env file to the .gitignore file
5-) Specify the version of node.js by adding an engines section in package.json that will be used to run your application on Heroku.
For example:
"engines": {
"node": "10.1"
}
6-) After you deploy your node application to the heroku, add your variables using PROD values as described here in heroku docs.
If you created your reactjs app using create-react-app you can follow these steps:
1-) Add .env file to the root folder. Your variable names must start with REACT_APP_...
REACT_APP_API_URL=http://localhost:3001/api
2-) Access your variables like this in your code:
process.env.REACT_APP_API_URL
3-) put your .env file to the .gitignore file
4-) After you deploy your reactjs app to the heroku, set prod variables in Config Variables section, as we did for node app.
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.
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.