I have a problem with dynamic routing with express.js in my react app. Everything works on localhost but when im deploying i get 400. Here's link: https://shoppyshop.herokuapp.com/item/1
As you see in console there is 400 error. My goal is to fetch data properly from that route that i did setup in my index.js of express.
app.get('/api/item/:id', function (req, res) {
let find = data.filter((el => {
return el.product.id == req.params.id
}))
res.json(find)
console.log('found item')
})
I understand that there is a problem with express. Im probably using wrong method so it wants to fetch icon and rest files from wrong path. Was googling, couldn't find similar problem. How to fix that? Here's component that fetches data:
export default class ItemInfo extends Component {
constructor(props) {
super(props)
this.state = {
data: []
}
}
componentDidMount = () => {
axios.get(`/api/item/${this.props.match.params.id}`)
.then(res => {
const data = res.data;
this.setState({
data,
fetched: true
})
})
}
render() {
console.log(this.props.match.params.id)
return (
<div className="App">
<SideBar pageWrapId={"mainpage"} outerContainerId={"MainPage"} />
<div id="mainpage">
<TopBar />
<More data={this.state.data} fetched={this.state.fetched}/>
<Footer />
</div>
</div>
)
}
}
Here's branch with code that im working on: https://github.com/KamilStaszewski/shoppy/tree/itemrouting/client
My express.js file:
const express = require('express');
const path = require('path');
const data = require('./apiData');
const app = express();
// Serve the static files from the React app
app.use(express.static(path.join(__dirname, 'client/build')));
app.disable('etag');
app.get('/category/:category', function (req, res) {
let find = data.filter((el => {
return el.product.category == req.params.category
}));
res.json(find);
console.log('found category');
});
app.get('/api/item/:id', function (req, res) {
let find = data.filter((el => {
return el.product.id == req.params.id
}))
res.json(find)
console.log('found item')
})
// An api endpoint that returns a short list of items
app.get('/api/data', (req, res) => {
var questions = data
res.json(questions);
console.log('Sent list of items');
});
// Handles any requests that don't match the ones above
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/public/index.html'));
});
const port = process.env.PORT || 5000;
app.listen(port);
console.log('App is listening on port ' + port);
UPDATE:
After many commits and pushing I found the answer. I dont know if its right way, but it works. The problem was with:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/public/index.html'));
});
Instead of serving index from build i was serving index from public which had no js inside it. To make it works i changed to:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/build/index.html'));
});
Now it works.
I am pretty sure that this is a problem with how you are serving static files in express.
From the express docs:
app.use('/static', express.static('public'))
Now, you can load the files that are in the public directory from the
/static path prefix.
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html
However, the path that you provide to the express.static function is
relative to the directory from where you launch your node process. If
you run the express app from another directory, it’s safer to use the
absolute path of the directory that you want to serve:
app.use('/static', express.static(path.join(__dirname, 'public')))
Your code has the following for serve static:
app.use(express.static(path.join(__dirname, 'client/build')));
So if I am reading this correctly, as I look at your code, I would understand that to be the following:
When you have an request that comes in for /, the rest of the call will search within client/build for the data. In your case, for your get request to /api/item/:id, that would probably be read by your server to try and find a static file in client/build/api/item/:whatevertheitemidis.
Instead, to serve static files, I would perhaps consider putting them in a directory called 'public' or something, and change your serve static to something like:
app.use('/public', express.static(path.join(//Wherever you keep your static files)));
With this all said, I may have been mistaken regarding some of the nuances of express.static so be sure to checkout the documentation. Either way, I hope to at least have pointed you in the right direction. If you comment out your serve static line and make a request with postman, you should see it working as expected.
UPDATE
I took a look at your code and noticed a couple of things:
I just pulled down your code and the endpoints are working. I tried http://localhost:5000/api/data and it provided the expected data.
It looks like your problem with serving static assets -- such as your favicon -- is because of the %PUBLIC_URL%/ part of your favicon url in your html. So far as I can find, there is nothing in your code that would translate that to an actual route. As soon as I turned that to /favicon, everything started working as expected.
Related
I am trying to use express to render a few different html files from my public folder. These are all static files. I also want to render a 404 page if a route is an invalid route is called. Here is my code.
const express = require("express")
const app = express()
app.use(express.static("public"))
app.get("/", (req, res) => {
res.render("index")
})
app.get("/about", (req, res) => {
res.render("about")
})
app.get("/contact-me", (req, res) => {
res.render("contact-me")
})
app.get("*", (req, res) => {
res.status(404).send("404 for all pages not defined in routes")
})
app.listen(8080)
The first route to render index.html works and the 404 status works, but all the other routes give me an error of "No default engine was specified and no extension provided." I tried added an ejs view engine, but the code still doesn't work. All html files are named properly and live in the "public" folder. Any help on this would be amazing! Thanks much!
You need to use handlebars for handling it. To see an example check this repo
I have a project made with create-react-app and i would like to use it within express ( as i need some back end functionnality).
basically, my server render a blank page when i run it....
here is the code of my server:
const express = require('express');
const path = require('path');
const app = express();
// Serve the static files from the React app
app.use(express.static(path.join(__dirname, 'public')));
// An api endpoint that returns a short list of items
app.get('/api/getList', (req,res) => {
var list = ["item1", "item2", "item3"];
res.json(list);
console.log('Sent list of items');
});
// Handles any requests that don't match the ones above
app.get('*', (req,res) =>{
res.sendFile(path.join(__dirname , 'public', 'index.html'));
});
const port = process.env.PORT || 5000;
app.listen(port);
console.log('App is listening on port ' + port);
if i enter the endpoint http://localhost:5000/api/getList, i can see the result, but when i want to display my react app for the other end point, the page is blank.
The react app use different Route and i also added a proxy in the package.json.
my server source file is at the root of the react app like the image.
Does someone has an idea why the react app doesnt show?
Thanks
You have two lines that are kind of doing the same thing:
// Serve the static files from the React app
app.use(express.static(path.join(__dirname, 'public')));
Will serve your index.html file to http://localhost:5000/index.html
According to the Express documentation https://expressjs.com/en/starter/static-files.html
And then it seems like you have the second route:
app.get('*', is formatted incorrectly.
It would need to be app.get('/*'.
If you aren't directing the user to multiple routes then it might be better to just use the root, app.get('/', rather than trying to catch all routes.
I am trying to use static files in my project, but when the user makes a get request to my "/" landing page. I want to send non-static things like a json. But for some reason it just automatically sends my index.html in my static file.
const express = require("express");
const app = express();
app.use(express.static("public"));
app.get("/", (req, res) => { //This sends the user to the index.html even tho i want to send 123
res.send("123");
});
app.listen("3000");
As your code is written, the express.static() middleware looks at the / request, finds an index.html file in the public directory and serves that as the response for the / request. This is a feature of express.static() that is causing a conflict for you with your custom route for "/".
You have at least four choices for a solution here:
You can specify an option for express.static() to disable the index.html feature so it will avoid the "/" route and pass control on to your subsequent route handlers.
You can move the express.static() middleware AFTER your app.get("/", ...) route so that your custom route gets first dibs at the request.
You can remove the index.html file from your public directory so express.static() won't find it. Using a template system for all your HTML files that locates all HTML template files in some other directory that express.static() can't see (such as a views directory) would cause this to happen too or just moving it to some private directory and using it from your code from the private directory would work too.
Give ALL your static resources a common path prefix so there is never a URL conflict between static resources and custom route handlers.
The first option (disable index.html feature in express.static()) would look like this:
const express = require("express");
const app = express();
// disable index.html feature
app.use(express.static("public"), {index: false});
app.get("/", (req, res) => {
res.send("123");
});
app.listen("3000");
The second option (change the order of route definitions/middleware) would look like this:
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("123");
});
// put this after custom route definitions so they take precendence
app.use(express.static("public"));
app.listen("3000");
The fourth option (give all static resources a common path prefix) would look like this:
const express = require("express");
const app = express();
app.use(express.static("/static", "public"));
app.get("/", (req, res) => {
res.send("123");
});
app.listen("3000");
With this fourth option, you'd then have to prefix all your static URLs with the static prefix (which you can make anything you want) and thus "/" would never be a match for express.static().
Is there any particular reason that you have an index.html file inside your public directory and are serving something else? The convention is serve static assets such as CSS, JS, images etc. from your public folder, and HTML / templates in folder which would be a sibling of public such as views.
The browser will always default to rendering an index.html file if it is found in the root of the public directory. Move the HTML file to a separate folder and the route should work
app.use is a middleware, so whenever you're running a app, it always first go through the middleware, Try the below code
const express = require("express");
const app = express();
app.get("/", (req, res) => { //This sends the user to the index.html even tho i want to send 123
res.send("123");
});
app.use(express.static("public"));
app.listen("3000");
I'm trying to set a static folder for my index.html file and other folders like css, img and js scripts.
but i don't manage to set a static folder successfully.
this is my app.js code:
const path = require('path');
const express = require('express');
const app = express();
app.use(express.static(path.join(__dirname, 'httpdocs')))
// app.get('/', (req, res) => {
// res.sendFile('index.html')
// });
const PORT = process.env.PORT || 5000
app.listen(PORT, (err) => {
if (err) {
console.log(err);
}
console.log(`Listening on port ${PORT}`);
})
my file tree is like this:
---node/
------httpdocs (i want this to be static folder
---css/
---js/
---img/
--index.html (this file should be loaded when loading the root link)
---app.js (nodejs script)
p.s: im using plesk on windows so if this makes any difference tell me.
I can see the only error is in below line.
app.use(express.static(__dirname + 'httpdocs'))
Try to print below tow different method using console :
console.log(__dirname+ 'httpdocs');
console.log(path.join(__dirname, 'httpdocs'));
Output:
...\nodehttpdocs
...\node\httpdocs
I hope you get the solution.
If you are trying to manually merge path then you have to add path separator '\' externally
Ex: app.use(express.static(__dirname + '\httpdocs'));
Or else use below method
Ex: app.use(express.static(path.join(__dirname, 'httpdocs')));
I suggest using path.join method. Because it will add path separator based on the operating system. Or else you have to manage manually.
Introduction
So far I have three files, one test.js is a file where I have built three functions that work.
But now I am trying to structure using MVC or at least some pattern. So now I router.js and app.js
Question
Should I put my promise functions from test.js in my config.js or server.js or something else, Im just interested in how people would do this and whats the correct way of structuring NodeJS.
server.js
In here start the server and apply the routes to my app
var configure = require('./router');
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
// get an instance of router
var router = express.Router();
configure(router);
app.listen(port);
console.log('Server has started!! ' + port);
// apply the routes to our application
app.use('/', router);
config.js
In here I build my routes
module.exports = function (router) {
// route middleware that will happen on every request
router.use(function (req, res, next) {
// log each request to the console
console.log(req.method, req.url);
// continue doing what we were doing and go to the route
next();
});
// home page route (http://localhost:8080)
router.get('/', function (req, res) {
res.send('im the home page!');
});
// sample route with a route the way we're used to seeing it
router.get('/sample', function (req, res) {
res.send('this is a sample!');
});
// about page route (http://localhost:8080/about)
router.get('/about', function (req, res) {
res.send('im the about page!');
});
// route middleware to validate :name
router.param('name', function (req, res, next, name) {
// do validation on name here
console.log('doing name validations on ' + name);
// once validation is done save the new item in the req
req.name = name;
// go to the next thing
next();
});
// route with parameters (http://localhost:8080/hello/:name)
router.get('/hello/:name', function (req, res) {
res.send('hello ' + req.params.name + '!');
})
// app.route('/login')
// show the form (GET http://localhost:8080/login)
.get('/login', function (req, res) {
res.send('this is the login form');
})
// process the form (POST http://localhost:8080/login)
.post('/login', function (req, res) {
console.log('processing'); // shows on console when post is made
res.send('processing the login form!'); // output on postman
});
};
test.js
In here is a list of functions that are a chain of promises getting data and API Keys
(small function, one of many that feed into each over)
var firstFunction = function () {
return new Promise (function (resolve) {
setTimeout(function () {
app.post('/back-end/test', function (req, res) {
console.log(req.body);
var login = req.body.LoginEmail;
res.send(login);
resolve({
data_login_email: login
});
});
console.error("First done");
}, 2000);
});
};
My recommended structure is to put everything except server.js in lib directory so all your app is lib/ plus server.js - everything else is package.json, dependencies in node_modules (created on npm install, not in the repo), .gitignore, config files for Travis, Circle, Heroku or whatever service you're using, some README.md and things like that.
Now, server.js is just bare minimum that requires lib/app:
const app = require('./lib/app');
and starts the server with something like:
const server = app.listen(app.get('port'), () => {
logger.info('%s listening on port %s', app.get('name'), app.get('port'));
});
server.on('error', (err) => {
logger.error(err.message || err);
process.exit(1);
});
where logger is some higher lever logger like Winston or something like that.
That's it. Now, lib/app.js is minimum code that loads the middleware like body parsers etc., creates the express app and sets the variables for port and name and then uses a router that is exported by lib/routes:
const routes = require('./routes');
// ...
app.use('/', routes);
The lib/app should be enough to use for testing with tools like supertest but it doesn't listen on any port - server.js does. This is important to simplify testing.
The router exported by lib/routes is used for everything and you can start with a single lib/routes.js file and then convert it to lib/routes/index.js plus several files in lib/routes as needed.
The routes only define the actual routes and input validation with a module like e.g. express-validation and register controllers that are exported by lib/controllers - that can start as lib/controllers.js and get converted to lib/controllers/index.js plus lib/controllers/*.js as needed - just like the routes.
Then I would add top level spec or test or tests directory where all of the tests go. The tests can require your lib/app to run the tests on it with no need to listen on actual TCP ports - those will test your routes with actual controllers. Other tests will require lib/util and run some unit tests on your utilities etc. Make sure to use a tool like istanbul or nyc to calculate the test coverage.
The database schemas and data models would go to lib/schemas and lib/models, some utility helpers in lib/util, some config loading code in lib/config etc.
This is quite flexible layout and works pretty well. You can start with just few files:
README.md
LICENSE.md
package.json
server.js
lib/app.js
lib/routes.js
lib/controllers.js
lib/config.js
etc. and easily convert all of the xxx.js file into xxx/index.js with entire folder of smaller xxx/*.js files as needed.
The main difference from your approach is that I recommend exporting routers and using them by higher level routers instead of passing the high level router into lower lever modules that export functions that take routers to work on.
So instead of:
const moreSpecific = require('more-specific');
const moreGeneral = express.Router();
moreSpecific(moreGeneral);
and then in more specific:
module exports = (router) => {
router.use('/abc/xyz', ...);
};
I would recommend exporting a more specific router in a file e.g. routes/abc.js:
const router = express.Router();
router.use('/xyz', ...);
module exports = router;
and then in more general router e.g. in routes/index.js:
const abc = require('abc');
const router = express.Router();
router.use('/abc', abc);
// and export the main router for other modules like app.js to use:
module.exports = router;
to have a route like /abc/xyz.