Custom handler in express.js router - javascript

I'm working for simple meal info page, and I need to run static and dynamic (json) server in one process, like this:
*- root
+- index.html
+- res
| +- main.js
| +- index.css
| `- (and more)
+- meal
| `- custom handler here (json reqestes)
`- share
`- (static files, more)
static files will handled with express.static, Can I route this with express?

all requests without starts with /meal/ should served as static, like /res or (root)/anyfolder/anyfile
app.use('/share', express.static('share')); makes the static handler look in share/, not the project root. It's unusual to share the whole root because people can read your source code. Do you really need to serve the whole folder? Can you e.g. put res/ inside share/, and then make a symlink pointing res -> share/res/, then when a client makes a request res/main.js express knows to look in share/res/main.js.
Anyway #hexacyanide's code should handle your case, just make sure to order your middleware functions such that Express handles routing functions before static files:
app.use(app.router)
app.use('/share', express.static('share'));
app.get('/meal/:param', function(req, res) {
// use req.params for parameters
res.json(/* JSON object here */);
});
// if you want to prevent anything else starting with /meal from going to
// static just send a response:
//app.get('/meal*', function(req, res){res.send(404)} );
app.get('/', function(req, res) {
res.sendfile('index.html');
});

Yes, this can be done with Express. Your setup might look like this:
app.use('/share', express.static('share'));
app.get('/', function(req, res) {
res.sendfile('index.html');
});
app.get('/meal/:param', function(req, res) {
// use req.params for parameters
res.json(/* JSON object here */);
});
Where you have a static file server mounted to /share and routed to the directory called /share, a route at / which sends the file called index.html, and a route that accepts a parameter, that responds with JSON.
Anything that isn't caught by a route will be attempted to be processed by the static file server. If the file server doesn't find a file, then it will respond with a 404.

Related

Calling an endpoint inside the node project itself?

I am creating a MERN app that adds meta tags to React pages without SSR. So, I need to read the query inside the main file of the server and pass the appropriate metadata content to each page.
I am using this in the server.js file:
const indexPath = path.resolve(__dirname, 'build', 'index.html');
// static resources should just be served as they are
app.use(express.static(
path.resolve(__dirname, 'build'),
{ maxAge: '30d' },
));
// here we serve the index.html page
app.get('/*', (req, res, next) => {
fs.readFile(indexPath, 'utf8', (err, htmlData) => {
if (err) {
console.error('Error during file reading', err);
return res.status(404).end()
}
// get post info
const postId = req.query.id;
const post = getPostById(postId);
if(!post) return res.status(404).send("Post not found");
// inject meta tags
htmlData = htmlData.replace(
"<title>React App</title>",
`<title>${post.title}</title>`
)
.replace('__META_OG_TITLE__', post.title)
.replace('__META_OG_DESCRIPTION__', post.description)
.replace('__META_DESCRIPTION__', post.description)
.replace('__META_OG_IMAGE__', post.thumbnail)
return res.send(htmlData);
});
});
Here the getPostById is statically defined in a file. But I want to fetch it from my db.
My file structure is:
server.js
controllers
- posts.js
routes
- posts.js
I've separated the logic from route. So my routes/posts.js file looks like:
import { getPost, createPost } from '../controllers/posts.js';
const router = express.Router();
router.get('/', getPost);
router.post('/', createPost);
export default router;
So, in order to dynamically pass the meta content, I need to read the API endpoint for each request and pass the appropriate data. For this, I need to call the endpoints directly inside my node project. How to do that?
I'd appreciate any help. Thank you.
If you really want to call your own http endpoints, you would use http.get() or some higher level http library (that is a little easier to use) such as got(). And, then you can make an http request to your own server and get the results back.
But ... usually, you do not make http requests to your own server. Instead, you encapsulate the functionality that gets you the data you want in a function and you use that function both in the route and in your own code that wants the same data as the route. This is a ton more efficient than packaging up an http request, sending that request to the TCP stack, having that request come back to your server, parsing that request, getting the data, forming it as an http response, sending that response back to the requester, parsing that response, then using the data.
Instead, if you have a common, shared function, you just call the function, get the result from it (probably via a promise) and you're done. You don't need all that intermediate packaging into the http request/response, parsing, loopback network, etc...

Does app.use(express.static("public")) call the middleware for every request?

Does using app.use(express.static("public")) call the middleware for every request, even if it wasn't a request for a static resource?
It will only get called if a route hasn't dealt with the request already.
Keeping in mind that routes are tested in the order they are registered, take this example:
const express = require('express');
const app = express();
const port = 3000;
app.get('/foo', (req, res) => {
console.log('Foo!');
res.send('Foo!');
});
app.use(function (req, res, next) {
console.log('middleware triggered');
next();
});
app.get('/bar', (req, res) => {
console.log('Bar!');
res.send('Bar!');
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
If I request http://localhost:3000/foo then the server will log:
Foo!
The /foo endpoint matched the request and then called res.send().
If I request http://localhost:3000/bar then it logs:
middleware triggered
Bar!
The middleware kicks in (because it matches the route), called next() to go to the next function that matches the route, and then the /bar handler is called.
It is important to position your static middleware carefully.
If you put it before the route you want to match the request then there are two possible negative effects:
You'll call it when it isn't needed which is inefficient
A static file will match a route instead of an actual route handler
On the other hand, if you put it last then you'll solve the efficiency problem, but some bad route design might mean that something creates a URL which matches an already existing static file and masks it.
It's a good idea to specify a directory that you know will never conflict with a route (e.g. app.use('/static', express.static('public'));) to avoid that possibility. As a bonus it means that any broken links which would normally 404 won't have to go through the static middleware unless the link is pointing in the /static path in the first place.
When registering it like that it, the middleware will run on every request, yes.
Basically because that statement is actually the same as:
app.use("/", express.static("public"))
Calling express.static returns a classic middleware function that will be run on every path you specify in app.use.
If you want it only to kick in on a specific path, you could register it like this:
app.use('/static', express.static('public'));

nodejs: display an image not in public folder

I would like to display an image which is not in the public folder in my webroot.
My hirarchy:
Webroot
--core
----views
----public <- Here is where stylesheets and other images are
------index.ejs <- Here I want to display the file.jpg
--data
----userdata
------username <- this folder is named by the user
--------assignment <- this folder is named by the assignment
----------file.jpg
I have no idea, i could move this into the public folder and rule it by the robots.txt, but I thought, maybe there is a better solution.
You can serve the data directory as a static directory, like your public directory -- Serving static files in Express. You'll probably want to set up some authentication middleware before the static route or everyone will be able to see each others data.
Here's an example of what that might look like:
// User authentication middleware
app.use(function(req, res, next) {
// Some implementation that determines the user that made the request.
req.username = 'foo';
next();
});
// Serve the public assets to all authenticated users
app.use(express.static('public'));
// Prevent users from accessing other users data
app.use('/data/userdata/{username}/*', function(req, res, next) {
if (req.username === req.path.username) {
next();
} else {
res.sendStatus(401); // Unauthorized
}
});
// Serve the data assets to users that passed through the previous route
app.use('/data', express.static('data'));

In Express.js, how do I set a wildcard route to not match for asset files?

I'm building my first real Express.js app, and stuck on the routing.
I am enabling static hosting:
app.use("/", express.static("public"));
And then I have a wildcard route:
router.get("/:page", function(req, res) {
// do stuff
});
This route is matching for urls like "/about" and "/contact" - which I want. But it appears that it's also trying to match for "/style.css" and other static asset files - which is unnecessary.
How do I make this wildcard not match for asset files?
One solution I found, is to search for a "." inside the query and then set a flag if it's found:
router.get("/:page", function(req, res) {
if (req.render_view) res.render("index");
});
router.param("page", function(req, res, next, page) {
// if request is not an asset file
if (page.indexOf(".") == -1) {
// do stuff
// set a flag
req.render_view = true;
}
next();
});
But I would like to find a cleaner solution, possibly using a regular expression in the router.get?
if I understand what you want to do ,you should just do something like :
app.js
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/static'));
app.use('/:page',function(){}..)
this configuration works when ur public and static folder is relative to app.js, when browser request for static file , first server will look at public folder , if file is no there will continue to static folder and finally to /:page

how can i render html file in node.js

i have tried to render html file but i got this error . i have login.html within public folder.how to render html file.thanks in advance.
my server side coding
var express = require('express');
var app = express();
app.configure(function(){
app.set("view options", {layout: false});
app.use(express.static(__dirname + '/public'));
});
app.get('/', function(req, res) {
res.render('login.html');
});
app.listen(8000)
Error: Failed to lookup view "login.html"
at Function.render (/home/aware/local/src/node_modules/express/lib/application.js:493:17)
at ServerResponse.render (/home/aware/local/src/node_modules/express/lib/response.js:753:7)
at /home/aware/local/src/health/demo2.js:17:9
at callbacks (/home/aware/local/src/node_modules/express/lib/router/index.js:161:37)
at param (/home/aware/local/src/node_modules/express/lib/router/index.js:135:11)
at pass (/home/aware/local/src/node_modules/express/lib/router/index.js:142:5)
at Router._dispatch (/home/aware/local/src/node_modules/express/lib/router/index.js:170:5)
at Object.router [as handle] (/home/aware/local/src/node_modules/express/lib/router/index.js:33:10)
at next (/home/aware/local/src/node_modules/express/node_modules/connect/lib/proto.js:199:15)
at resume (/home/aware/local/src/node_modules/express/node_modules/connect/lib/middleware/static.js:60:7)
The closest you can get to using html for node views is EJS (Embedded JavaScript)
This way you write your normal HTML, pass your data through node and display them using tags like: <%= var %>
http://embeddedjs.com/
https://github.com/visionmedia/ejs
You can render HTML page with Nodejs using Embedded JavaScript(ejs).
here you can go through -the Documentation of ejs
for using ejs, first of you create directory with the name views, In that directory create a file with .ejs extension.
The files you keep inside your public folder will not be rendered because they are set as static, so they will be served as is. All other files outside of this will be rendered. Remember to set the rendering engine.
I just came in contact with connect today & wanted to solve a simillar issue, serving a simple HTML file. In my case, I wanted to display a 404 when any previous middlewares hadn't finished the request.
I create a function, show404 and then you can do something like app.use(show404). Here's my CoffeScript for that mostly inspired in connect errorHandler source code.
show404 = (req, res, next) ->
res.statusCode = 404
res.setHeader 'Content-Type', 'text/html; charset=utf-8'
html_path = require("path").resolve('.tmp/404.html')
require('fs').readFile html_path, (err, data)->
throw err if err
console.log "DATA:", data
res.end data

Categories

Resources