I'm running a two container docker setup; NextJS for the public facing web, and Django backend for admins to add content. The routes are nicely working with getInitialProps which fetches the added content. As the content references static images, they are connected via docker volumes (./static/media on django container and ./public/media on nextjs container).
However when a new image appears in ./public/media, the started NextJs server returns a 404 response for those images.
// EDITED SOLUTION: As suggested by #Pierfrancesco
The workaround solution is to create a custom server which dynamically serves those files
// server.js
const express = require('express')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.get('/media/images/*', (req, res) => {
// Disallow travelling up in the file tree
let target = req.originalUrl.replace("..", "")
return res.sendFile(__dirname + '/public' + target);
})
server.all('*', (req, res) => {
return handle(req, res)
})
server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
Could this be a feature request or is this a bug in NextJS?
Related
const port = process.env.PORT || ("http://localhost:3002")
const postUrl=`${port}/post`;
addData=()=>{
console.log(postUrl)
console.log(process.env,"AS")
Axios.post(postUrl,this.state.form)
.then((response)=>{
this.setState({errorMessage:"",successMessage:response.data})})
.catch((err)=>{
this.setState({successMessage:"",errorMessage:err.response.data.message})
})
}
When I am calling the backend in the production process.env.PORT is blank.
(process.env.NODE_EV= production which is absolutely correct exactly like in the backend)
My backend is completely fine as it getting the process.env.PORT correctly.
But my frontend is not getting the process.env.PORT that's why it keeps calling the other address("http://localhost:3002").
App will completely work fine if I keep open my local machine backend because the "http://localhost:3002"
is available to serve. But in production, Heroku keeps changing the process.env.PORT which is showing its value in the backend, not in the frontend
How can I make my frontend to call my backend server properly in production??
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const port = process.env.PORT || 3002;
const cors = require("cors");
const path=require("path");
const routing = require("./routing/route");
require('dotenv').config();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use("/",routing);
app.use(function (err, req, res, next) {
console.log("hii")
res.status(500).send({ message: err.message });
});
if(process.env.NODE_ENV ==="production"){
app.use(express.static("client/build"));
app.get("*",(req,res)=>{
res.sendFile(path.resolve((__dirname,"client","build","index.html")));
});
}
app.listen(port, () => {
console.log(` Server is started at http://localhost:${port}`);
});
server file
If your React application is served from your Node.JS application as you said, you could just use window.location. window.location is an object that stores statistics about the current page that the user is on, and you could use that to construct a URL and send the server a request, like so:
// This URL uses a template literal, which is a new feature of ES6.
// All but Internet Explorer supports it.
// This is using window.location.protocol, which is either `http:` or `https:`,
// depending on the protocol that the page was loaded with. window.location.host
// is the host that the page was loaded from, with the port number.
const postUrl =
`${window.location.protocol}//${window.location.host}/post`;
// And then requesting with the URL.
addData = () => {
console.log(postUrl);
console.log(process.env, "AS");
Axios.post(postUrl, this.state.form)
.then((response) => {
this.setState({errorMessage: "",successMessage: response.data});
})
.catch((err) => {
this.setState({successMessage: "",errorMessage: err.response.data.message});
});
}
I deployed my MERN project on heroku app but when I tried to submit my form it send me this error in console:
Access to XMLHttpRequest at 'localhost:8000/api/products' from origin
'https://thebeuter.herokuapp.com' has been blocked by CORS policy:
Cross origin requests are only supported for protocol schemes: http,
data, chrome, chrome-extension, https. Form.jsx:69 Error: Network
Error
at e.exports (createError.js:16)
at XMLHttpRequest.d.onerror (xhr.js:83)
Here is my server.js:
const express = require("express"),
app = express(),
cors = require("cors"),
port = process.env.PORT || 8000,
db = "beuter",
path = require("path"),
server = app.listen(port, () => console.log(`Listening to on port ${port}`));
app.use(cors());
app.use(express.json());
if (process.env.NODE_ENV === "production") {
app.use(express.static('beuter/build'))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'beuter', 'build', 'index.html'));
})
}
console.log(port)
require("./server/config/database.config")(db);
require("./server/routes/product.route")(app);
and here is my Form.jsx:
const addProduct = (e) => {
e.preventDefault();
const product = {
title,
title_url,
price,
description1,
description2,
description3,
description4,
description5,
img_url1,
img_url2,
img_url3,
img_url4,
size,
size2,
fit,
fit2,
category,
};
axios
.post("localhost:8000/api/products", product)
.then((res) => {
if (res.data.errors) {
setErrors(res.data.errors);
} else {
navigate("/");
}
})
.catch((err) => console.log(err));
};
return (
...
...
...
)
How can I fix this?
Here is my project github:
https://github.com/nathannewyen/the-beuter
Thank you!
Updated:
ShopAllProducts.jsx:
useEffect(() => {
const fetchItems = async () => {
setLoading(true);
const res = await axios.get("http://localhost:8000/api/products");
setProducts(res.data);
setLoading(false);
};
document.title = `Shop - The Beuter`;
fetchItems();
}, [props]);
the answer for this question is to have env files for development and production
for development
create the file called .env.development in the root folder of your frontend app
in .env.development add this line
REACT_APP_BASE_URL="http:localhost:5000"
and in .env.production add another line as
REACT_APP_BASE_URL="https://algorithammer.herokuapp.com"
or your website (here i am showing the sample)
now make sure that you have a variable called baseURL as global variable
example:
authAPI.js (example)
exports.baseURL = process.env.REACT_APP_BASE_URL;
in Login.js (example)
import {baseURL} from "./authAPI.js"
axios
.post(`${baseURL}/login`, {
data: "sample data"
})
.then((res) => console.log(res))
.catch((err) => console.log(err));
dont forget to push the changes and deploy the heroku app again
I’ve built an app with Express and React which has GET and POST routes which work perfectly locally. I’ve deployed through Heroku and nothing is working anymore! I’m just getting a 404 error. I’ve tried to create a static.json file which hasn’t worked, although I didn't use Create-React-App to set it up anyway.
This is my index.js:
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const app = express()
const router = require('./config/router')
const { port, dbURI } = require('./config/environment')
const errorHandler = require('./lib/errorHandler')
const logger = require('./lib/logger')
app.use(express.static(`${__dirname}/public`))
app.use(express.static(`${__dirname}/dist`))
mongoose.connect(dbURI, { useNewURLParser: true, useCreateIndex: true})
app.use(bodyParser.json())
app.use(logger)
app.use('/api', router)
app.use(errorHandler)
app.listen(port, () => console.log(`Up and running on port ${port}`))
module.exports = app
router.js
const vacancies = require('../controllers/vacancies')
router.route('/vacancies')
.get(vacancies.index)
.post(vacancies.create)
router.route('/vacancies/:id')
.get(vacancies.show)
module.exports = router
controller:
//tested in insomnia - works
function indexRoute(req, res, next) {
Vacancy
.find(req.query)
.then(vacancies => res.status(200).json(vacancies))
.catch(next)
}
//tested in insomnia - works
function showRoute(req, res, next) {
Vacancy
.findById(req.params.id)
.then(vacancy => res.status(200).json(vacancy))
.catch(next)
}
//tested in insomnia - works
function createRoute(req, res) {
Vacancy
.create(req.body)
.then(vacancy => res.status(201).json(vacancy))
.catch(err => res.status(422).json(err))
}
module.exports = {
index: indexRoute,
show: showRoute,
create: createRoute
}
and lastly, environment file:
const port = process.env.PORT || 4000
const dbURI = process.env.MONGODB_URI || `mongodb://localhost/dxw-job-board-${process.env.NODE_ENV || 'dev'}`
module.exports = { port, dbURI }
This is for a code test for a job I really want to super anxious about it not working properly - any help would be greatly appreciated!
I ran into this and pulled my hair out when I first tried to deploy an app on Heroku (was my first experience with any sort of deployment actually). On the production server (Heroku) you should be serving the static files from the build directory, not the /public directory.
Something like this:
if (process.env.NODE_ENV === "production") {
// react build directory /public
} else {
// Local production env react /public dir
}
This should tell Node that it's on Heroku (or whatever platform you use) and needs to use the production build and not the development version.
Hope that solves the issue for you.
I'm relatively new to React, and trying to deploy an application to an Openshift environment. The app consists of a React JS front-end which makes API calls to a nodejs back end restful api. That's the idea at least.
Most of the ways I've found suggest using docker, but I have no idea where to start with that. The create-react-app documentations gives an example of an Node/Express server to render the index.html, which I am much more familiar with and have got working, but I can't seem to set a proxy to route the api calls to the back-end.
I initially tried setting the proxy in the package.json, but that didn't work. I then found express-http-proxy which looked promising, but can't get it to work.
My front end server looks like this:
const express = require('express');
const path = require('path');
const app = express();
const proxy = require('express-http-proxy');
const PORT = process.env.OPENSHIFT_NODEJS_PORT || 8080;
const API_PROXY_URL = process.env.APIURL
app.use(express.static(path.join(__dirname)));
app.use('/api', proxy(API_PROXY_URL));
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
app.listen(PORT, () => {
console.log(`Server Started on port ${PORT}`);
console.log(`Proxy in use of ${API_PROXY_URL}`);
});
I have a generic api function which is making the calls using axios:
export function apiCall(method, path, data) {
return new Promise((resolve, reject) => {
return axios[method.toLowerCase()](path, data)
.then(res => {
return resolve(res.data);
})
.catch(err => {
return reject(err.response.data.error);
});
});
}
For example, my when I try and sign in it is trying to do a post call to <<REACTURL>>/api/auth/signin when I want it to send to <<APIURL>>/api/auth/signin.
I feel I'm missing something really obvious.
I couldn't get this to work as I wanted to ended up declaring the full URL in the API request to get it working. Not pretty, but does the job:
export function apiCall(method, path, data) {
let url = <<APIURL>> + path
return new Promise((resolve, reject) => {
return axios[method.toLowerCase()](url, data)
.then(res => {
return resolve(res.data);
})
.catch(err => {
return reject(err.response.data.error);
});
});
}
I'm attempting to setup a NodeJS application that is using the Next framework to utilize client and server side rendering. I'm trying to get the client and server side rendering to prepend a path to the routes/URLs it generates. The server side render seems to be working by setting up the express server GET function to listen for requests made on route and then passing that along to node by stripping out the prepended route value. However when it comes the rendering on the client the prepended value is missing even when the as="{somestring}" is added to the .js pages for elements like Link so when the external Next javascript files are referenced in the render it's missing the prepended value.
The purpose for the routing is to allow us to run multiple micro-services on one domain each hosted on different instances in AWS and being routed using Target Groups and an ALB.
Essentially what I want to do is replace / with /{somestring} and I need this to be included not only in the server side rendering but in the client side rendering.
URL Example:
www.example.com -> www.example.com/somestring
HTML Render:
www.example.com/_next/960d7341-7e35-4ea7-baf6-c2e7d457f0db/page/_app.js -> www.example.com/somestring/_next/960d7341-7e35-4ea7-baf6-c2e7d457f0db/page/_app.js
Edit/Update
I've tried to use app.setAssetPrefix and while it renders the requests for the assets correctly and the pages load the assets themselves are 404ing.
Here is my server.js file:
const express = require('express');
const next = require('next');
const port = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
// Port
const server = express();
app.setAssetPrefix('test1');
// ======
// Routes
// ======
server.get('/test1/:id', (req, res) => {
const actualPage = `/${req.params.id}`;
const queryParams = { id: req.params.id };
app.render(req, res, actualPage, queryParams);
});
server.get('/test1', (req, res) => {
app.render(req, res, '/');
});
server.get('*', (req, res) => {
handle(req, res);
});
// =============
// End of Routes
// =============
server.listen(port, err => {
if (err) throw err;
console.log(`>Listening on PORT: ${port}`);
});
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
});
You need custom routing. Parse the incoming url and replace it with what you want.
Here is is an example to make /a resolve to /b, and /b to /a
https://github.com/zeit/next.js#custom-server-and-routing