How can I style/customize connect/middleware-directory output? - node.js

I'm trying to build a simple searching/browsing service for a server and I thought it could be interesting to use express for that to get familiar with it.
For the problem I'm considering, I have express#3.4.1 and a very simple app.js
var express = require("express");
var app = express();
//var express = require('express')
//, app = express.createServer();
app.configure(function() {
var hourMs = 1000*60*60;
app.use(express.static(__dirname + '/public', { maxAge: hourMs }));
app.use(express.directory(__dirname + '/public', {hidden: true, icons: true}));
app.use(express.errorHandler());
});
app.listen(1333);
How can I style/customize the HTML/CSS that the directory middleware produces ?
Should I overwrite express.directory.html in my app.js or is there a better/cleaner way ?
Edit : after looking at the code it seems overriding the function is not the way to go. the file it uses seems rather hard coded and I may end up copying the whole file to override what I want. So, any idea ?

You could extract all relevant code from the Express middleware, make your own adjustments, and save it as part of your app (as ./lib/middleware/directory.js or something).
If you're going to modify the files inside the node_modules/ directory you can be sure that you'll lose any modifications when you update.
You could rewrite express.directory.html(), but you might still run into the problem that any updates make it stop working because of internal changes.

I believe directly editing the node_modules/express/node_modules/connect/lib/public/style.css and directory.html would be the cleanest way of customizing the default express directory style/html.

Related

Router.use() requires middleware functions

I use express to serve angular html file. I want to test if angular can get data from express backend.
app.component.html
<h1>
{{title}}
</h1>
<a class="button" href="/Books/getall">Get all books</a>
server.js
const express = require('express');
var app = express();
var staticRoot = __dirname;
app.set('port', (process.env.PORT || 3000));
var bookRouter = require('./bookController');
app.use(express.static(staticRoot));
app.get('/', function(req, res) {
res.sendFile('index.html');
});
app.use('/Books', bookRouter);
app.listen(app.get('port'), function() {
console.log('app running on port', app.get('port'));
});
bookController.js
var express = require('express');
var mongoose = require('mongoose');
var Books = require('./books.model');
var bookRouter = express.Router();
var router = function(){
bookRouter.use('/getall')
.get(function(req, res){
var url = 'mongodb://admin:password#ds019076.mlab.com:19076/booksdbtest';
mongoose.connect(url, function(err){
Books.find()
.exec(function(err, results){
res.send(results);
mongoose.disconnect();
});
});
});
};
module.exports = router;
But I got some error like I mentioned on the title. I have read some articles and discussions that says i need some proxy when using express and angular 2 together but I really don't know how to implement that. Can someone help me here on what is wrong with the code? That would be so much appreciated.
EDIT:
I have found what caused the error. I have changed bookRouter.use -> bookRouter.route and the error is gone. Now another problem appears. If I click the link it will load continously and nothing happens. Anyone know why?
If you are using Angular2 with Express you need to hand off the front end routes to Angular2 and then use Express for the back end points.
In most cases you will receive problems not using some sort of template engine. I like to use Express-Handlebars.
npm install hbs
Once you have this installed you can set it up in your app.js file. The file you set Express up in.
// view engine setup
app.set('views', path.join(__dirname, 'public'));
app.set('view engine', 'hbs');
Also make sure you have a public directory. If not create one.
/public
Then in the app.js
app.use(express.static(path.join(__dirname, '/public')));
Now you can put css, js, or images in this directory and call them inside your app.
Also inside the public directory you can put your index.hbs file.
Express-Handlebars uses hbs files. In our case you are only going to need one.
This is the file you would need to put the head of your html file in. As well as the
<my-app>Loading...</my-app>
We will pass this off to Angular2.
Now that we have that setup lets make a route to pass it off.
/routes/index.js
var express = require('express');
var router = express.Router();
// pass front end off to Angular2
router.get('/', function(req, res, next) {
res.render('index');
});
module.exports = router;
So now when the app loads it will load that index page. So that is the only route we need for now to pass the front end off. At this point we will configure angular2.
Webpack
What is Webpack?
Webpack is a powerful module bundler. A bundle is a JavaScript file
that incorporate assets that belong together and should be served to
the client in a response to a single file request. A bundle can
include JavaScript, CSS styles, HTML, and almost any other kind of
file.
I do not know how you have included Angular2 into your project. But if you have not set it up yet and do not know how I suggest using Webpack
Follow this tutorial to get it setup. Sometimes it is easier to start over.
Angular2
If you go into your Angular2 application now which for me is at
/assets/app/
You will set up your components and their own routing. Any links created for the front views will be done here now. So in the /assets/app directory you will need a app.routing.ts file.
Routing in Angular2
There are many ways to build this file. I like to use templates then child templates which would be to much to explain in this answer. For you though a basic routing file will look like this.
import { Routes, RouterModule } from "#angular/router";
import { AboutComponent } from './about.component';
import { HomeComponent } from './home.component';
const APP_ROUTES: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
];
export const routing = RouterModule.forRoot(APP_ROUTES);
Links
Now that you have routes setup in Angular2 you can create links in your html files or home.component.html like this,
<li class="nav-item p-x-1">
<a class="nav-link" routerLinkActive="active" [routerLink]="['/home']"> Home</a>
</li>
I realize this has not basically given you code that will compile which in a perfect world we would all share like that. But I really understand the struggle and want to help. There is just to much to explain to be able to hand you over that much and it make sense. Please use the tutorial I gave you above. If you have any questions ask. Try and use my answer as a baseline to understand the different components of putting together an application like this.
Disclaimer
You do not have to use Express-Handlebars. If you find in that tutorial they are doing something different just follow along so you can get a working app you understand. I like hbs because I only use it for one file then handle everything on the front end with Angular2. The reason I use it instead of just an html file is because it is a huge pain to configure Express to work right with Angular2. For example if you just use an html file then add it to the public directory then run your app and refresh the page you will get a 404 error. Unless you add something like,
HashLocationStrategy
Which adds ugly hash tags to your url and is annoying in a whole different way. Using a template engine for that small task makes everything work so much easier and better. The configuration is minimal as you can see above.
For a seed project I use,
git clone https://github.com/wuno/md-recruit.git
in the terminal navigate to the new directory you just cloned.
type,
webpack
Then
npm start
Then navigate to localhost:3000
Make sure you have webpack installed globally
npm install webpack -g
It is very important that what ever version of webpack is installed locally in your project has to match the global install.

Using express STATIC to server filesin NodeJS

Im attempting to serve files with the assistance of express.static and am having difficulty getting it to work.
My code includes the following:
var app = express();
app.use(express.static(__dirname + '/public'));
var app_router = express.Router();
app_router.get('/', function(req, res){
res.sendfile('/test.html');
});
app.use('/', app_router);
This does not function in serving test.html, however if I replace it with ./public/test.html it is served correctly(Basically bypassing express.static)
Any assistance would be appreciated. Ive looked around and found code similar to mine that appears to be working however I must be missing something relatively simple and would appreciate another set of eyes. Thanks!
You're misunderstanding express.static().
res.sendFile() takes a path to a file on disk, and has nothing to do with express.static().
express.static() handles requests to paths matching files in the folder you pass it.

How does express know the difference between a static and a dynamic request?

This is the code that I see everywhere for a simple static web server using node.js and express:
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.listen(8080);
My question is how does express know when the request is for a static page vs. a dynamic page? For example, why would /index.html be served from the public folder and not from dynamic templates?
Thanks.
You can think of the routes you define as a chain of checks on the path defined in the URL. Express iterates one by one through each check looking for a match; once it finds one, it executes that callback.
In this case, express.static is defining a bunch of path checks for each file in the public directory. If you app.use(express.static(__dirname + '/public')); before your app.get('/index.html', function(req, res) { ... code, and there is an index.html file in there, it will use the static file over the dynamic one.
That's basically it.

Node.js / Express.js - How does app.router work?

Before I ask about app.router I think I should explain at least what I think happens when working with middleware. To use middleware, the function to use is app.use(). When the middleware is being executed, it will either call the next middleware by using next() or make it so no more middleware get called. That means that the order in which I place my middleware calls is important, because some middleware depends on other middleware, and some middleware near the end might not even be called.
Today I was working on my application and had my server running in the background. I wanted to make some changes and refresh my page and see the changes immediately. Specifically, I was making changes to my layout. I couldn't get it to work so I searched Stack Overflow for the answer and found this question. It says to make sure that express.static() is beneath require('stylus'). But when I was looking at that OP's code, I saw that he had his app.router call at the very end of his middleware calls, and I tried to figure out why that was.
When I made my Express.js application (version 3.0.0rc4), I used the command express app --sessions --css stylus and in my app.js file the code came setup with my app.router above both the express.static() and require('stylus') calls. So it seems like, if it comes already setup that way, then it should stay that way.
After re-arranging my code so I could see my Stylus changes, it looks like this:
app.configure(function(){
//app.set() calls
//app.use() calls
//...
app.use(app.router);
app.use(require('stylus').middleware(__dirname + '/public'));
app.use(express.static(__dirname + '/public', {maxAge: 31557600000}));
});
app.get('/', routes.index);
app.get('/test', function(req, res){
res.send('Test');
});
So I decided that the first step would be to find out why it is important to even have app.router in my code. So I commented it out, started my app and navigated to /. It displayed my index page just fine. Hmm, maybe it worked because I was exporting the routing from my routes file (routes.index). So next I navigated to /test and it displayed Test on the screen. Haha, OK, I have no idea what app.router does. Whether it is included in my code or not, my routing is fine. So I am definitely missing something.
So Here Is My Question:
Could somebody please explain what app.router does, the importance of it, and where I should place it in my middleware calls? It would also be nice if I got a brief explanation about express.static(). As far as I can tell, express.static() is a cache of my information, and if the application can't find the requested page, it will check the cache to see if it exists.
Note: This describes how Express worked in versions 2 and 3. See the end of this post for information about Express 4.
static simply serves files (static resources) from disk. You give it a path (sometimes called the mount point), and it serves the files in that folder.
For example, express.static('/var/www') would serve the files in that folder. So a request to your Node server for http://server/file.html would serve /var/www/file.html.
router is code that runs your routes. When you do app.get('/user', function(req, res) { ... });, it is the router that actually invokes the callback function to process the request.
The order that you pass things to app.use determines the order in which each middleware is given the opportunity to process a request. For example, if you have a file called test.html in your static folder and a route:
app.get('/test.html', function(req, res) {
res.send('Hello from route handler');
});
Which one gets sent to a client requesting http://server/test.html? Whichever middleware is given to use first.
If you do this:
app.use(express.static(__dirname + '/public'));
app.use(app.router);
Then the file on disk is served.
If you do it the other way,
app.use(app.router);
app.use(express.static(__dirname + '/public'));
Then the route handler gets the request, and "Hello from route handler" gets sent to the browser.
Usually, you want to put the router above the static middleware so that a accidentally-named file can't override one of your routes.
Note that if you don't explicitly use the router, it is implicitly added by Express at the point you define a route (which is why your routes still worked even though you commented out app.use(app.router)).
A commenter has brought up another point about the order of static and router that I hadn't addressed: the impact on your app's overall performance.
Another reason to use router above static is to optimize performance. If you put static first, then you'll hit the hard drive on every single request to see whether or not a file exists. In a quick test, I found that this overhead amounted to ~1ms on an unloaded server. (That number is much likely to be higher under load, where requests will compete for disk access.)
With router first, a request matching a route never has to hit the disk, saving precious milliseconds.
Of course, there are ways to mitigate static's overhead.
The best option is to put all of your static resources under a specific folder. (IE /static) You can then mount static to that path so that it only runs when the path starts with /static:
app.use('/static', express.static(__dirname + '/static'));
In this situation, you'd put this above router. This avoids processing other middleware/the router if a file is present, but to be honest, I doubt you'll gain that much.
You could also use staticCache, which caches static resources in-memory so that you don't have to hit the disk for commonly requested files. (Warning: staticCache will apparently be removed in the future.)
However, I don't think staticCache caches negative answers (when a file does not exist), so it doesn't help if you've put staticCache above router without mounting it to a path.
As with all questions about performance, measure and benchmark your real-world app (under load) to see where the bottlenecks really are.
Express 4
Express 4.0 removes app.router. All middleware (app.use) and routes (app.get et al) are now processed in precisely the order in which they are added.
In other words:
All routing methods will be added in the order in which they appear. You should not do app.use(app.router). This eliminates the most common issue with Express.
In other words, mixing app.use() and app[VERB]() will work exactly in the order in which they are called.
app.get('/', home);
app.use('/public', require('st')(process.cwd()));
app.get('/users', users.list);
app.post('/users', users.create);
Read more about changes in Express 4.
Routing means determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on).
Each route can have one or more handler functions, which are executed when the route is matched.
In Express 4.0 Router, we are given more flexibility than ever before in defining our routes.
express.Router() is use multiple times to define groups of routes.
route used as middleware to process requests.
route used as middleware to validate parameters using ".param()".
app.route() used as a shortcut to the Router to define multiple requests on a route
when we are using app.route(), we are attaching our app with that router.
var express = require('express'); //used as middleware
var app = express(); //instance of express.
app.use(app.router);
app.use(express.static(__dirname + '/public')); //All Static like [css,js,images] files are coming from public folder
app.set('views',__dirname + '/views'); //To set Views
app.set('view engine', 'ejs'); //sets View-Engine as ejs
app.engine('html', require('ejs').renderFile); //actually rendering HTML files through EJS.
app.get('/', function (req, res) {
res.render('index');
})
app.get('/test', function (req, res) {
res.send('test')
})
In express Version 4 we can easily define routes in the following manner:
server.js:
const express = require('express');
const app = express();
const route = require('./route');
app.use('/route', route);
// here we pass in the imported route object
app.listen(3000, () => console.log('Example app listening on port 3000!'));
route.js:
const express = require('express');
const router = express.Router();
router.get('/specialRoute', function (req, res, next) {
// route is now http://localhost:3000/route/specialRoute
});
router.get('/', function (req, res, next) {
// route is now http://localhost:3000/route
});
module.exports = router;
In server.js we imported the router object of the route.js file and apply it in the following manner in server.js:
app.use('/route', route);
Now all of the routes in the route.js have the following base URL:
http://localhost:3000/route
Why this approach:
The main advantage of taking this approach is that now our app is more modular. All the route handlers for a certain route now can be put into different files which makes everything more maintainable and easier to find.
An article by #kelyvinn from 2016, with the intent to demonstrate modularity, includes this code:
// controllers/apis/dogs/index.js
const
express = require('express'),
dogService = require('../../../services/dogs');
let router = express.Router();
router.get('/', dogService.getDogs);
router.get('/:id', dogService.getDogWithId);
module.exports = router;

Is it possible to set the filesystem root and/or the document root to some subdirectory of the filesystem?

I would like to know if it is possible to specify a subdirectory for the filesystem root or the document root for requests of static resources (if there's any such distinction) in node.js.
I know that I can do it by concatenating an absolute path from the root, but I'm wondering if it can be done on an application-wide level.
I haven't found anything in the documentation supports it, but perhaps I'm overlooking it.
EDIT: I should mention that I'm not interested in using a 3rd party library at this point.
Check out expressjs
http://expressjs.com/guide.html#configuration
Specifically
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
Express/connect has a 'static' middleware for this use case. There are other smaller packages just for static file serving, however, express is nice and well maintained.
The API does not allow you to do what you're asking for directly. You will need to use string concat.
I've tried the following script with nodejs and works well. it takes the current path as document root to serve.
app.js
var http = require('http');
var express = require('express');
var app = express();
app.use(express.static('./'));
var server = http.createServer(app);
server.listen(8080,'127.0.0.1',function() {
console.log('listen to 127.0.0.1:8080');
});
the reference is here:
http://expressjs.com/starter/static-files.html

Resources