Ganesh - @hackerone
Web developervar http = require('http'); http.createServer((req, res) => { res.write('hello world'); res.end(); }).listen(3000);Writing a simple server hello world in NodeJS is pretty straight forward. NodeJS API provides a built-in http module, which let's you create a server. The createServer takes in a callback, which gets 2 parameters request and response. as the name suggests, request gives you info about the request and response helps us write a response, in most web apps to the browser. And here we listen to port 3000.
curl http://localhost:3000 hello worldWhen you point your browser at localhost port 3000, you'll get an hello world. it's as simple as that.
var http = require('http'); http.createServer((req, res) => { if(req.url == '/') { res.write('hello world'); } else if(req.url == '/tom'){ res.write('hello tom') } else if(req.url == '/joe'){ res.write('hello joe') } res.end(); }).listen(3000);But a web app has multiple end points or pages. So,when we need more URLs, we tend to add a logic inside the callback and that makes the code a bit ugly and makes it un-maintainable. We need an abstracted layer which let's us build web apps in a more manageable way.
npm install express
var http = require('http'), express = require('express'); var app = express(); app.use('/tom', (req, res, next) => { res.write('hello tom'); res.end(); }); app.use((req, res, next) => { res.write('hello world'); res.end(); }); http.createServer(app).listen(3000);This is where express comes in. Express provides an abstraction here. instead of passing your method, you can pass the express instance to the http createServer method, and express will handle your request from there. let's get into the code here. npm install express will download the express module and it's dependencies into your project. so you can then require it from your code. Express uses concept of middlewares to control the flow of the request. we'll look at middlewares a bit later. but for now, app.use will add middlewares to the app's request flow. If you look at the code there, the first middleware which returns hello tom will get executed for '/tom'. There is case which doesn't have a URL parameter, this will get executed for all cases, other than tom.
curl http://localhost:3000/tom hello tom curl http://localhost:3000/mary hello world curl http://localhost:3000/ hello world
app.get('/tom', (req, res, next) => { res.write('hello tom'); res.end(); }); app.post('/tom', (req, res, next) => { res.write('creating tom...'); res.end(); });In most web apps, you would want to differentiate between the type of Request, for a typical website you'd want to handle a GET and a POST differently. and for a RESTful service, you'd want to support PUT and DELETE and sometimes pre-flight HEADs too. Express routing supports this. So instead of using app.use, we could use app.get and app.post
curl http://localhost:3000/tom hello tom curl -X POST http://localhost:3000/tom creating tom...So, when you make a GET request to the url, you get hello tom, while a post will return "creating tom..."
app.get('/:name', (req, res) => { var name = req.params.name; res.write('hello '+ name); res.end(); });ExpressJS Routing mechanism doesn't stop here. we can pass parameters in the URL's and can use them in functions. so, the code above will match /tom /mary /joe /everyone and prints "hello "+ name
curl http://localhost:3000/tom hello tom curl http://localhost:3000/joe hello joe curl http://localhost:3000/mary hello marySo, we've added support for more names in the URL.
app.get('/:name(\\w{3})', (req, res) => { var name = req.params.name; res.write('hello '+ name); res.end(); });Routing even supports regex matching. So i can be rude and say hello only to people who's names are exactly 3 letters long.
curl http://localhost:3000/tom hello tom curl http://localhost:3000/joe hello joe curl http://localhost:3000/mary hello world
var express = require('express'); var router = express.Router(); router.get('/profile', (req, res) => { res.write('profile'); res.end(); }); router.get('/status', (req, res) => { res.write('active'); res.end(); }); app.use('/user', router);When the app grows, you'd want to manage the routes in a modular way. And express provides a separate smaller router component to achieve this. In case of the example above, we're adding a sub-router for /user so, any url's that has /user as the first part will be handed over to the router.
curl http://localhost:3000/user/profile profile curl http://localhost:3000/user/active active
var express = require('express'), user = require('./user'), book = require('./book'); var app = express(); app.use('/user', user); app.use('/book', book);In a bigger application, you can separate the routing logic and create controllers and bootstrap the controllers like above. In the above case, book would export a book router and will handle all the urls starting with /book. while the user router will handle all the urls starting with user.
var express = require('express'), cons = require('consolidate'), app = express(); app.engine('html', cons.handlebars); app.set('view engine', 'html'); app.set('views', __dirname + '/views'); app.get('/', (req, res) => { res.render('index', { name: 'world' }); });ExpressJS uses consolidate.js library to support template engines. And since consolidate.js suppports about 14+ NodeJS template engines, they can be utilized in the express app. let's go through the snippet, then we call app.engine method, which tells express use handlebars template engine to parse all the html files. we call app.set, which sets let's you manage express config. then we set 'view engine', 'html' which is the engine we just defined and 'views' to the absolute path of directory which contains all the view files. then inside our middleware, we call res.render, which takes in 2 parameters, the view and the data that needs to be passed to the view. the template engine, in our case handlebars will render the view file with the data passed.
// index.html <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello {{name}}</title> </head> <body> <h1>Hello {{name}}!</h1> </body> </html>And that'll render process the handlebars template and renders the output to the client. Consolidate provides support for most of the famous template engines like jade, handlebars, nunjucks. so you choose whatever flavour you like. Also, all the layout options and the way you manage layouts will be handled by the template engine.
res.json({ message: 'hello', name: 'world' }); res.redirect('hello/world'); res.redirect(301, 'hello/world');Apart from the views, express also offers a bunch of response helper methods for returning a json or redirect header and cookies etc.
app.use((req, res, next) => { req.name = 'tom'; next(); }); app.use((req, res, next) => { console.log(req.name); // tom res.end(); }); app.use((req, res, next) => { // this middleware will not be run console.log('hello'); res.end(); });middleware is a function, that alters the flow of the request by modifying the request / response variables. it gets 3 parameters, the request, the response and next. request will have all the request info like the url, headers etc. response parameter will let you write / output your response. the third parameter next, calling next will execute the next middleware in the stack. calling app.use will let you add middleware to the request stack. so whenever your app get a request, all the middlewares that match the request will get executed. if you look at the example, we have 3 middlewares added to the flow, the first one adds a key called 'name' and set's it a value 'tom'. since it calls next(). the next middleware in the stack will get executed. in this case, the second middleware prints the name set by the first middleware and it ends the request. since the request end in the second middleware and the it doesn't call next, the third middleware does not get called.
var bodyParser = require('body-parser'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.post('/', (req, res) => { console.log(req.body); });But we don't have to write our own middlewares, there's a large number of middlewares out there which can let you accomplish most common tasks. The express team themselves support a few essential middlewares. Since expressJS on its own cannot parse the content of a POST or a cookie, it requires a middle-ware. For example, the body parser.
var cookieParser = require('cookie-parser'); app.get('/', (req, res) => { console.log(req.cookies); // undefined }); app.use(cookieParser()); app.get('/', (req, res) => { console.log(req.cookies); });This is a cookie parser middleware for getting the cookie information from the request header.
var auth = (req, res, next) => { if(req.headers['let-me-in'] == true) { next(); } else { res.render('error'); } } app.use(auth); app.get('/', (req, res, next) => { res.render('index'); });Middlewares can also be used for authentication, for example you can have an auth middleware which checks if an auth header is present and blocks the flow.
var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/library'); var Book = mongoose.model('Book', { name: String }); app.post('/book', (req, res) => { var book = new Book({ name: req.body.name }); book.save((err) => { if(err) // handle it res.redirect('/book/'+book._id); }) });
app.get('/book/:id', (req, res) => { Book.findOne({_id: req.params.id}, (err, book) => { if(err) // handle it res.render('book', {book: book}); }); });You can use expressjs with any of the nodejs database drivers. Or you could use an odm like mongoose. using an ODM gives you some cool features like input validation, pre-save post-save events and a bunch of cool features. Things that we'll need to care about, when handling database operations is - all the database operations are asynchronous - we'll be getting into the callback sooner than we think.
var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/library'); var Book = mongoose.model('Book', { name: String }); app.post('/book', (req, res) => { var book = new Book({ name: req.body.name }); var operation = book.save(); operation.then((book) => { res.redirect('/book/'+book._id); }); });
app.get('/book/:id', (req, res) => { var query = Book.findOne({_id: req.params.id}); query.then( (book) => { res.render('book', {book: book}); }); });Try to use promise whenever possible. this makes the code maintainable.
npm install yo -g npm install generator-express -g yo express // and you'll have an app running in seconds!So, we saw a bunch of things about expressJS. most are essential for any web app. So, do i have to write everything myself? Luckily not, people have come up with Yeoman generator, which scaffolds the app with all the essentials including templates and database. use them! but don't trust them blindly, read through the code they generate