Testing API's With React Bundled With Webpack - node.js

I'm having trouble trying to run my API's when running my frontend react code. I am using webpack and webpack dev server but the problem seems to be that they run their own server while my apis are run by another. I think I can either make my application run entirely in my express backend but having trouble how or somehow use webpack dev server to run both. my backend express node looks like this
const express = require('express');
const bodyparser = require('body-parser');
const app = express();
require('./api/findMedia.js')(app)
var PORT = process.env.PORT || 8080;
const server = app.listen(PORT, () => {
console.log('server is working : ')
})
my webpack config looks like this. You can ignore my proxy key. I was testing webpack dev server to simultaneously run my express server.
var webpack = require("webpack");
var path = require('path');
module.exports = {
entry: {
app: "./src/app.js"
},
output: {
filename: "build/bundle.js",
sourceMapFilename: "build/bundle.map"
},
devtool: '#source-map',
devServer : {
historyApiFallback : true,
hot : true,
inline: true,
host: 'localhost',
port: 3000,
stats: 'errors-only',
proxy : {
'/api': {
host: 'localhost',
protocol: 'http',
port: 8080
}
}
},
plugins : [new webpack.HotModuleReplacementPlugin({multiStep: true})],
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015']
}
},
{
test: /\.css$/,
loader: 'style!css'
}
]
}
}
and finally I call it in the front end with a simple post request using axios and inside my componentdidmount
componentDidMount(){
axios({
method: 'post',
url: '/find/media'
}).then((response)=>{
console.log('post request to mongo data from front end a success: ' + response)
console.log(response.data.findArticles)
}).catch((error)=>{
console.log('error'+error)
})
}

webpack-dev-server creates it's own server, like you said. If you want to run both webpack-dev-server and express, then you will need the proxy key in the devServer configuration object.
proxy : {
'/api': 'http://localhost:8080'
}
With your setup, what that would do is proxy any request beginning with /api to http://localhost:8080/api. So from your React code, you would do:
axios({
method: 'post',
url: '/api/find/media'
...
})
which webpack-dev-server would proxy to http://localhost:8080/api/find/media.
If your express router is listening for just '/find/media' the devServer.proxy config object has a rewritePath key.
proxy : {
'/api': {
target: 'http://localhost:8080',
rewritePath: {'^/api' : ''}
}
}
If you want express to handle everything, then I think you can use webpack-dev-middleware.

Related

I can't setup proxy in mern stack(react-vite) and do not know the reason

I'm developing mern stack web application with react from vite.js and have a problem to deal with proxy.
my client side runs at http://localhost:3000 and server side runs at http://localhost:5000.
usually I use http-proxy-midlleware to connect my server and client like below
src/setupProxy.jsx
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app){
app.use(
createProxyMiddleware('/api', {
target: 'http://localhost:5000',
changeOrigin: true
})
)
};
However, It didn't work and still sent to localhost:3000 when I post data to server with axios. I googled it and figured out that with vite.js I need to use vite.config.js
so I set up vite.config.js like below
import { defineConfig, HttpProxy } from 'vite'
import react from '#vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
host: true,
port : 3000,
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true
}
}
},
})
and tried axios call again.
const result = await axios.post('api/users/login', dataToSubmit)
.then(res => res.data);
return result;
However, contrary to my expectation, It still sent to 3000 and I do not have a clue about what is going wrong :/
xhr.js:210 POST http://localhost:3000/api/users/login 404 (Not Found)
could you tell me how to fix it? thanks for reading, your help will be appreciated.
It's required to specify secure: false like this. Reference
export default defineConfig({
plugins: [react()],
server: {
host: true,
port : 3000,
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
secure: false
}
}
},
})
I had this problem too.
My backend runs on http://localhost:8080, while my frontend runs on http://localhost:3000.
To resolve the cross-origin issue, I used the proxy alternative, but every request was returning a 404 error.
My solution was to add the rewrite: (path) => path.replace(/^\/api/, "") function in the vite settings file.
Finally, the final result of the file was:
import { defineConfig } from "vite";
import react from "#vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
server: {
proxy: {
"/api": {
target: "http://127.0.0.1:8080",
changeOrigin: true,
secure: false,
ws: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
port: 3000,
},
plugins: [react()],
});
Your port in vite.config.js file is set to 3000. So you have to set your target in src/setupProxy.jsx to 'http://localhost:3000'.
hope it helps

Multiples devServer proxies and multiples ports

I need to query automated routes generated from from https://github.com/o1lab/xmysql on port 3000 from my vue.js dev env running on port 8080 .
vue.config.js :
module.exports = {
devServer: {
proxy: {
"/api": {
target: "http://localhost:80", // Works perfeclty
},
"/": {
target: "http://localhost:80", // Works perfectly
},
"/generated": { // Not working
target: {
port: 3000
},
secure: false,
ws: false,
changeOrigin: true
}
},
hot: true,
liveReload: true,
}
};
xmysql params :
xmysql -h localhost -u root -p password -n 3000 -a /generated/ -d myDatabase
My vue.js axios "get" query :
axios
.get('/generated/meetings', {
headers: {
'Access-Control-Allow-Origin': 'all',
}
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error);
});
The Error :
Cannot GET /generated/meetings
I can access localhost routes on my localhost:3000 into my firefox navigator and they work really well:
Looks like the proxy is not working, any idea ?
I have tried this other vue.config.js params with no luck :
devServer: {
proxy: {
"/api": {
target: "http://localhost:80",
// ,pathRewrite: {'^/api' : ''}
},
"/": {
target: "http://localhost:80",
},
"/generated": {
target: "http://localhost:3000",
pathRewrite: { '/generated': '' },
secure: false,
ws: false,
changeOrigin: true
}
},
hot: true,
liveReload: true,
}
The only thing working is this query :
axios
.get('localhost:3000/generated/meetings', {
headers: {
'Access-Control-Allow-Origin': 'all',
}
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error);
});
But then, there is a CORS problem, and I can't get 'response', even if it gets displayed in the firefox console query, I can only get the error .
sorry, It seems that it was a /api/ conflict caused by a generic mongoDb back end starting by /api/ running in parrallel.
I ended up with this vue.config.js . Now my Mysql queries will go to /api routes, and my mongoDb queries will go to the generic-api routes, so I am able to handle 2 databases in one vue.js app:
module.exports = {
devServer: {
proxy: {
"/api": {
target: "http://localhost:3000", // This is set for xmysql routes generator https://github.com/o1lab/xmysql
},
"/": {
target: "http://localhost:80", // Serving the vue.js app
},
"/generic-api": {
target: "http://localhost:80", // When using generic-crud.js with mongoDb
}
},
hot: true,
liveReload: true,
}
}
And this is my xmysql standard config , now :
xmysql -h localhost -u root -p password -n 3000 -d myDatabase
EDIT : NEW :
Ho no, when I trigger NPM RUN BUILD, my /api routes are not working any more in my vue.js production app!
RESOLVED : Added a NODE EXPRESS proxy like this in my server.js node file :
// -------------------------------
// PROXY ALL API ROUTES QUERIES TO PORT 3000 TO USE WITH MYSQL ROUTES GENERATOR https://stackoverflow.com/questions/10435407/proxy-with-express-js
// -------------------------------
var proxy = require('express-proxy-server');
app.use('/api', proxy('http://localhost:3000/api'));
Now, even my vue.js production app queries are proxied to http://localhost:3000 , so it should work on heroku ...

webpack-dev-server making sockjs request on wrong port

For development, I'm starting my Node application on port 4000 and Webpack Dev Server on port 4001
process.env.NODE_DEBUG --> [undefined][false]
[INFO] Port '4000' is available for http server.
[INFO] Port '4001' is available for dev server statics.
Webpack: Starting ...
In our Node application, whenever a 404 is encountered, we generally retry that error. The issue that's happening is that I'm seeing is that a sockjs-node request is made to the wrong port which causes a cascading series of 404s as the application tries to retry the invalid sockjs url. What I can't figure out is why it's being made to port 4000 instead of 4001 in the first place.
Here is my webpack dev server config
config.entry.client.unshift(`webpack/hot/only-dev-server`);
config.output = {
path: paths.appBuildPublic,
publicPath: 'localhost:4001',
pathinfo: true,
libraryTarget: 'var',
filename: 'static/js/bundle.js',
chunkFilename: 'static/js/[name].chunk.js',
devtoolModuleFilenameTemplate: info => path.resolve(info.resourcePath).replace(/\\/g, '/')
};
config.devServer = {
disableHostCheck: true,
clientLogLevel: 'info',
compress: true,
headers: {
'Access-Control-Allow-Origin': '*'
},
overlay: true,
port: devServerPort,
https: {
...createCertificate()
},
quiet: false,
watchOptions: {
ignored: /node_modules/
}
};
config.plugins = [
...(config.plugins || []),
new ErrorOverlayPlugin(),
new webpack.HotModuleReplacementPlugin({
}),
new webpack.DefinePlugin(dotenv.stringified)
];
In my express router I've configured the following
.use('/sockjs-node', (req: Request, res: Response) => {
if (process.env.NODE_ENV === 'development') {
res.status(200).send();
}
})
However, this is really a hacky way of solving the problem. Does anyone know why this sockjs request is made to my Node server instead of the webpack dev server? Any help would be really appreciated.

NodeJS Subdomain w/ vhost and greenlock-express

I'm new to Node and I want my website, dacio.app, working with subdomains for my college projects using vhost.
However, I need to have it secured due to the requirement for .app domains, so I'm using greenlock-express to automate it.
Don't be frontin', yo! TLS SNI 'giphy.dacio.app' does not match 'Host:
potatoes.dacio.app'
I've tried using the vhost example in the repo, but it doesn't look like server-static supports express apps.
Any tips on how to get this working? I keep hearing about reverse proxies, but I'm not sure if it's worth the effort as I don't even know if it would work - would it help?
server.js
#!/usr/bin/env node
'use strict';
// DEPENDENCIES
const express = require('express');
const vhost = require('vhost');
const path = require('path');
const glx = require('greenlock-express');
// MIDDLEWARE
const app = express();
const giphyApp = require('../giphy-search');
const potatoesApp = require('../rotten-potatoes');
const portfolioApp = require('../dacio.app');
// ROUTES
app.use(vhost('giphy.dacio.app', giphyApp));
app.use(vhost('potatoes.dacio.app', potatoesApp));
app.use(portfolioApp);
// GREENLOCK for HTTPS
glx.create({
version: 'draft-11',
server: 'https://acme-v02.api.letsencrypt.org/directory',
email: 'dacioromero#gmail.com',
agreeTos: true,
approveDomains: [ 'dacio.app', 'giphy.dacio.app', 'potatoes.dacio.app' ],
configDir: '~/.config/acme/',
app: app,
communityMember: false
}).listen(80, 443);
I've switched to using redbird which seems to accomplish everything I was hoping to do.
const path = require('path')
const proxy = require('redbird')({
port: 80,
letsencrypt: {
path: path.join(__dirname, '/certs'),
port: 9999
},
ssl: {
http2: true,
port: 443
}
});
proxy.register('dacio.app', 'http://localhost:8080', {
ssl: {
letsencrypt: {
email: 'dacioromero#gmail.com',
production: true,
}
}
});
proxy.register('giphy.dacio.app', 'http://localhost:8081', {
ssl: {
letsencrypt: {
email: 'dacioromero#gmail.com',
production: true
}
}
})
proxy.register('potatoes.dacio.app', 'http://localhost:8082', {
ssl: {
letsencrypt: {
email: 'dacioromero#gmail.com',
production: true
}
}
});

Webpack production process.env.PORT=undefined on server compile (React app)

I have my React app compiling all fine in dev & prod scripts, however, now that I am deploying on DigitalOcean, I am running into a problem with process.env.port being undefined and therefore falling back to 3000 and not the expected port 80.
Package.json npm script:
"build": "webpack -p --config webpack.config.production.js &&
cross-env NODE_ENV=production node server/server.js"
Webpack.config.production.js script which includes the DefinePlugin() for process.env:
module.exports = {
entry: {
app: ["./src/scripts/index.js"]
},
output: {
filename: "[name]-bundle.js", path: __dirname, publicPath: "/"
},
devtool: "source-map",
module: {
rules: [
{
test: /\.(js)$/,
exclude: /node_modules/,
use: ["babel-loader"]
},
{
test: /\.(scss|css)$/,
use: [
{loader: "style-loader"}, {
loader: "css-loader"}, {
loader: "sass-loader"}, {
loader: "postcss-loader"}
}
]
},
{
test: /\.(jpg|png|svg)$/,
use: {
loader: "file-loader",
options: {
name: "[path][name].[ext]"
}
}
}
]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("production")
}
}),
extractSass,
new UglifyJsPlugin({
sourceMap: true,
uglifyOptions: {
output: {
comments: false
}
}
}),
new HtmlWebpackPlugin({
template: "public/index.html",
favicon: "public/assets/img/favicon.ico"
}),
new ExtractTextPlugin({
filename: "styles/styles.css",
allChunks: true
})
]
};
my express server file:
const path = require("path");
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
let useFolder;
if (process.env.NODE_ENV !== "production") {
useFolder = "/public/";
const webpack = require("webpack");
const config = require("../webpack.config.development");
const compiler = webpack(config);
app.use(
require("webpack-dev-middleware")(compiler, {
noInfo: true,
publicPath: config.output.publicPath,
hot: true
})
);
app.use(
require("webpack-hot-middleware")(compiler, {
log: console.log,
path: "/__webpack_hmr",
heartbeat: 10 * 1000
})
);
} else {
useFolder = "/dist/";
}
app.use(express.static("dist"));
app.get("/", function(req, res) {
res.sendFile(path.join(__dirname, ".." + useFolder + "index.html"));
});
app.listen(port, function(err) {
if (err) {
return console.error(err);
}
console.log("Listening at port: ", port);
});
There are no errors, and when it runs on backup port 3000 all is good on the live server, but I am expecting port 80.
I have tried swapping where NODE_ENV=production gets set: removing from NPM script, and DefinePlugin() but no success either.
Webpack will by default bundle for web. This means it will replace all process.env. statements with what you have defined in the DefinePlugin. You never set PORT to anything, so it will be undefined.
You have two options, either define PORT in the DefinePlugin, or change the webpack target to node and remove the DefinePlugin. This will make webpack use the actual environment variables on runtime.
https://webpack.js.org/concepts/targets/
To create environment variable within the project create a .env file at the root of the project and add values in that file. You may have to re-run the deployment script you have to bundle them all again. We normally comment values related to other environments during the deployment, we have a script to do that.
#dev
PORT=6000
URL=Blahhh.dev.com
#prod
PORT=7000
URL=Blahhh.com

Resources