- Node
- MongoDB
- RESTful Node API (Application Programming Interface)
- Node Authentication - Token Based
Node apps are configured via a package.json file. This is where you set the name, version, repository, author, and package dependancies. The format of the file is an object literal, where properties are defined via a key value pair relationship. The key Main tells node which file to use to start the application.
{
"name" ; "app-name",
"version" : '1.0.0',
"description": "best app ever",
"main": 'server.js',
"repository": {
"type" : 'git',
"url" : "https://github.com/bestappever",
},
"dependancies": {
"express" : "version #"
"mongoose": "version #"
},
"author" : "Liam Neeson"
}
Packages can be installed by manually writing them in the package.json file, or through the command line. When installing with the command line make sure to use the --save modifier to add the package to package.json. Packages will be installed into a directory called node_modules, this is where packages live inside Node projects.
- npm init //Initialize node project and create package.json file
- node server.js //Start node application
- nodemon server.js //Start node app with npm package nodemon to watch for file changes
- npm install package-name --save //Install package via command line.
- npm install package another-package --save //Install multiple packages
- npm install // Install all dependancies listed in package.json file.
Structure
├── nodeApp/
│ ├── index.html
│ ├── server.js
│ ├── package.json
{
"name": "http-server",
"main" : "server.js",
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Basic Node Server</title>
<style>
body{
text-align:center;
background:grey;
padding-top:50px;
}
</style>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
// get the http and filesystem modules
var http = require('http'), fs = require('fs');
// create our server using the http module
http.createServer(function(req, res) {
// write to our server. set configuration for the response
res.writeHead(200, {
'Content-Type': 'text/html',
'Access-Control-Allow-Origin' : '*'
});
// grab the index.html file using fs
var readStream = fs.createReadStream(__dirname + '/index.html');
// send the index.html file to our user
readStream.pipe(res);
}).listen(3000);
// tell ourselves what's happening
console.log('Visit me at http://localhost:3000');
In server.js the http module is used to create a server and the fs module is used to grab index.html and send it as a response to the user. The server is set to listen on port 3000. View index.html at localhost:3000.
Express is a framework for node use to create MVC web apps and REST APIs. Install express using npm install express --save
Structure
├── nodeApp/
│ ├── index.html
│ ├── server.js
│ ├── package.json
{
"name": "expressserver",
"version": "1.0.0",
"description": "",
"main": "server.js",
"author": "your name",
"license": "ISC",
"dependencies": {
"express": "^4.13.3"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Basic Node Server</title>
<style>
body{
text-align:center;
background:grey;
padding-top:50px;
}
</style>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
// load the express package and create our app
var express = require('express')
var app = express();
var path = require('path');
//send out index.html file to the user for the home package
app.get('/', function(req, res){
res.sendFile(path.join(__dirname +'/index.html'));
});
//start the server
app.listen(3000);
console.log('server running on port 3000');
To server multiple pages to users additional routes will be required. The express router can be used to achieve this. The express router provides routing APIs like .use(), .get(), .param(), and .route().
Using the Router(),
Call an instance of express.Router() and define routes on that. For Example in applications an adminRouter is usually created to handle admin specific routes. This is useful because we can create multiple instances of the express router allowing for our basic routes, authenticated routes, and API routes.
var adminRouter = express.Router([options]);
app.use('/admin', adminRouter);
Property | Description | Default | Availability |
---|---|---|---|
caseSensitive |
Enable case sensitivity. | Disabled by default, treating “/Foo” and “/foo” as the same. | |
mergeParams |
Preserve the req.params values from the parent router. If the parent and the child have conflicting param names, the child’s value take precedence. |
false |
4.5.0+ |
strict |
Enable strict routing. | Disabled by default, “/foo” and “/foo/” are treated the same by the router. |
Structure
├── nodeApp/
│ ├── index.html
│ ├── server.js
│ ├── package.json
{
"name": "expressserver",
"version": "1.0.0",
"description": "",
"main": "server.js",
"author": "your name",
"license": "ISC",
"dependencies": {
"express": "^4.13.3"
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Basic Express Routes</title>
</head>
<body>
</body>
</html>
//load the express package and create our app
var express = require('express');
var app = express();
var path = require('path');
//send our index.html file to the user for the home package
app.get('/', function(req,res){
res.sendFile([path.join(__dirname + '/index.html')]);
});
//start the server
app.listen(3000);
console.log("server runninr on port 3000");
// 1. get an instance of the router, 2. apply routes to it, 3. add those routes to our main app
// get an instance of the router
var adminRouter = express.Router();
adminRouter.get('/', function(req,res){
res.send('Dashboard');
});
// users page /admin/users
adminRouter.get('/users', function(req, res){
res.send('Posts')
})
// apply routes to application
app.use('/admin', adminRouter);
Middleware is a way to do something before a request is processed. For example checking if a user is authenticated and logging date for analytics. Make sure you place middleware after your router declaration and before and defined routes. The next() argument is used to tell Express that the middleware function is complete. The order you place your middleware and routes is very important.
adminrouter.use(function(req,res,next){
// logging each request made to console
console.log(req.method , req.url);
// continue to the route
next();
});
Express can handle route parameters. Route parameters can be used to validate data coming into your application. This could be used to validate a token for a REST API.
//user ID is passed into the url /admin/users/:name
adminRouter.get('/users/:name', function(req,res){
res.send('hello'+ req.params.name + '!');
});
Creates middleware that will run for a certain route parameter.
adminRouter.param('name' , function(req, res, next, name){
//do validations http-server
//once validations done save item in the req
req.name = name;
// do the thing
next();
});
//route middleware is acting upon localhost:3000/admin/hello/:name
//When the /hello/:name route is hit the .param() middleware will be used.
adminRouter.get('/hello/:name', function(req ,re) {
res.send('hello' + req.name + '!');
});
routes can be defined on the app variable, like calling express.Router(). This allows you to define multiple actions on a single login route. Routes are applied directly to the main app object.
admin.route('/login')
// show form localhost:3000/login
.get(function(req,res){
res.send('login form');
});
// process the form
.post(function(req,res){
res.send('processing login form')
})
- use express.Router() multiple times to define groups of routes
- apply the express.Router() to a section of the site using app.use()
- use route middleware to process requests
- use route middleware to validate parameters using .param()
- use app.route() to define multiple requests on a route
-
ManualRather than making queries to a table like in a traditional SQL database, queries using Mongo will be made to collections of documents. Stored in JSON style. Mongo will not create a database unless you insert information into that database
mongod - connect to Mongo instance show databases - list all databases db - show current database use db_name - select a database
Create: Creates both database and collection if they do not already exist db.users.save({ name: 'Bob'}); - save one user db.users.save([{name: 'Bob'}, {name: "Tod"}]); - save multiple users Read: db.users.find(); - show all users db.users.find({name: 'Bob'}); - find a specific user Update: db.users.update({name: 'Bob'}, {name:'Bob Tod'}); - update user value Delete: db.users.remove({}); - remove all db.users.remove({name:'Bob'}); - remove one
I use the node package mongoose when working with Mongo.
//grab mongoose package var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/db_name')
Express will be the node framework used, morgan allows loggin requests to the console, mongoose is an ODM for communication with Mongo, body-parser is used to pull POST content from an HTTP request, and bcrypt is used to has passwords to be stored in a mongo document.
Structure - app/ ----- models/ ----------user.js // user model - node_modules/ // dependencies/packages - package.json // app dependencies - server.js // configure application and create routes
npm install express morgan mongoose body-parser bcrypt-nodejs --save { "name" ; "nodeApi", "version" : '1.0.0', "main": 'server.js', "dependancies": { "express" : "version #", "mongoose": "version #", "body-parser": "version #", "bcrypt-nodejs": "version #", } }
// gettin dem packages var express = require('express'); var app = express(); var bodyParser = require('body-parser'); var morgan = require('morgan'); var mongoose = require('mongoose'); var port = process.env.PORT || 3000; //setting up the app mongoose.connect('mongodb://localhost/meancheats') var User = require('./app/models/user'); // body-parser for post requests app.use(bodyParser.urlencoded({extend: true})); app.use(bodyParser.json()); //CORS annoyance protection app.use(function(req,res,next){ res.setHeader('Access-Control-Allow-Origin', "*"); res.setHeader('Access-Control-Allow-Methods','GET, POST'); res.setHeader('Access-Control-Allow-Headers','X-Requested-With,content-type,Authorization'); //carry on next(); }); //gotta see those requests via console app.use(morgan('dev')); //ROUTES API------------------------------------ app.get('/', function(req,res){ res.send('Home Route Ya') }); var api = express.Router() //instance of Router //api route middleware api.use(function(req,res,next){ console.log('api hit'); next() //finish route }) api.get("/", function(req,res){ res.json({important: '/ api route Ya'}); }); //all api routes will start with /api app.use('/api', apiRouter) //fire up the server app.listen(port); console.log('server running on port ' + port );
- create Schema setting name, username, and password as Strings
- setting index and unique prevents the username from being duplicated
- setting select: false on passwords prevents it from being shown when making db queries
- .pre() ensures password is hashed before being saved
- added a passwordCheck method on UserSchema to validate input with stored data
// grabbing packages var mongoose = require('mongoose'); var Schema = mongoose.Schema; var bcrypt = require('bcrypt-nodejs'); // user schema var UserSchema = new Schema({ name: String, username: {type: String, required: true, index: {unique :true}}, password: {type: String, required: true, select:false} }) // using bcrypt to hash the password before it is saved UserSchema.pre('save', function(next){ var user = this; //if user isn't new and password wasn't changed then do not create a hash if(!user.isModified('password')) return next(); // making that hash bcrypt.hash(user.password, null, null, function(err, hash){ if (err) return next(err); //setting the hashed pw user.password = hash next(); }); }); // checking input password with hashed to see if they match UserSchema.methods.passwordCheck = function(password) { var user = this; return bcrypt.compareSync(password, user.password); }; // exporting the model so the rest of the app can use it module.exports = mongoose.model('User', userSchema);
Route Verb Action /api/users GET Get all Users /api/users POST Create a user /api/users/:user_id GET Get a single user /api/users/:user_id PUT Update a user /api/users/:user_id DELETE Delete a user server.js below middleware
// routes that end in /users api.route('/users') //create user post to localhost:3000/api/users .post(function(req,res){ //instance of User model var user = new User(); // set user data to input data user.name = req.body.name; user.username = req.body.username; user.password = req.body.password; //save the user to mongo, whilst doing the checking of the errors user.save(function(err) { if (err) { console.log(err); //if there is an error and its code is 11000 thats a duplicate username if(err.code == 11000) return res.json({success: false, message: 'A user with that username already exists.'}); else return res.send(err) } res.json({message: 'User created'}); }); });
server.js below middleware
api.route('/users') .post(function(req,res) { //create a user }) // get all users in the database GET localhost:3000/api/users .get(function(req,res){ User.find(function(err, users){ if (err) res.send(err) //return users if no errors res.json(users); }); });
server.js
api.route('/users/:id') //get user with specfic id //localhost:3000/api/users/:id .get(function(req,res){ User.findById(req.params.id, function(err,user){ if(err) res.send(err); //return single user_id res.json(user); }); })
server.js
api.route('/users/:id') .get(function(req,res){ //get single user }) //update user .put(function(req,res){ //find user by id User.findById(req.parms.id , function(err, user) { if (err) res.send(err); // update the users info only if its new if (req.body.name) user.name = req.body.name; if (req.body.username) user.username = req.body.username; if (req.body.password) user.password = req.body.password; // save the updates user.save(function(err){ if (err) res.send(err); res.json({message: 'user updated, chyeah'}); }); }); })
server.js
api/route('/users/:id') .delete(function(req,res){ User.remove({ _id: req.params.id }).then(function(err,user){ if (err) return res.send(err) res.json ({message: "user deleted"}); }) })
Their are many ways to implement authentication but one of the most widely used is token based auth. The main benefits to token based authentication is the ability to create stateless and scalable servers, mobile application ready, OAuth and added security.
In traditional server authentication user login information is stored on the server via session, memory or stored disk. The HTTP protocol is stateless meaning if authentication is done based upon that the server would forget who the user is on every new request.
Sessions: storing user information in memory can create overhead when many users are authenticating. Scalability: cloud providers start replicating servers to handle application load, having vital information in session memory will limit ability to scale. CORS: could run into problems with forbidden requests. For example when using multiple mobile devices. CSRF: users could be victim to cross-site forgery.
- token based authentication is stateless.
- no information about the user is stored in server or session
- no session info means app can scale and add more machines as necessary regardless of where user is logged in.
- every request after the first will require the token
- token should be sent in HTTP header
- to accept requests from all domains using Access-Control-Allow-Origin: *
- data access permissions can be set on tokens for third party applications.
- User Requests Access with Username / Password
- Application validates credentials
- Application provides a signed token to the client
- Client stores that token and sends it along with every request
- Server verifies token and responds with data
- tokens are stored on client side, stateless and ready to be scaled.
- can be passed along to any server, useful in load balancing because their is no state or session.
- token holds the user data
- because no cookie is being sent, the feasibility of a CSRF attack drops dramatically.
- token can be stored in a cookie, cookie is not used to authenticate token inside cookie is.
- token can expire after a set amount of time, requiring a user to log in again.
- selective permissions to third party apps.
- used inside HTTP header when authenticating an API.
- works with many programing languages
- carry all the information necessary within itself (self contained)
the header contains two parts the type (jwt) and the hashing algorithm being used.
{ "typ" : "JWT", "aig" : "HS256" }
The payload contains the information that is being transmitted as well as additional token information.
Payload
{ "iss": "issuer of the token", "sub" : "subject of token", "aud" : "audience of token" "exp": "experation time of the token", "nbf": "Defines the time before which the JWT MUST NOT be accepted for processing", "iat": "The time the JWT was issued. Can be used to determine the age of the JWT", "jti": "Unique identifier for the JWT. Can be used to prevent the JWT from being replayed. This
is helpful for a one time use token", "name": "Bobby Mcguee", "admin": true }
a hash made up of the following:
- header
- payload
- secret
- token is sent on every request so there are no CSRF attacks. There is no session based information to manipulate since, there is no session.
var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); HMACSHA256(encodedString, 'secret');
- home page unauthenticated
- API routes are authenticated
- login route used to authenticate a user
- pass in token for auth
npm install jsonwebtoken --save //create and verify tokens
package.json
{ "name": "noderestapi", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" }, "author": "", "license": "ISC", "dependencies": { "bcrypt-nodejs": "0.0.3", "body-parser": "^1.15.0", "express": "^4.13.4", "mongoose": "^4.4.6", "morgan": "^1.7.0" } }
server.js
Was tired of token stuff, gunna start up angular and come back to this
- "What HTML would have been if it had been designed for web-apps"
- MVC architecture
data binding allows for a centralized source of data, this allows a developer to move away from injecting data into views. For example with JQuerys (append,val, html). Angular handles injection for you by binding variables in the view and controller.
Structure
├── js/ │ ├── app.js ├── index.html
app.js
angular.module('angApp',[]); .controller('mainController', function() { // binding this to view model var viewModel = this; // defining variables on this allows them to be available to your apps views viewModel.message = "hello world"; viewModel.list = [ {name:'Bob', sex:'male'}, {name:'Tina', sex:'female'}, {name:'Fiona', sex:'female'} ]; });
<!DOCTYPE html> <html ng-app="angApp"> <head> <meta charset="utf-8"> <title>Angular App Basic</title> </head> <body ng-controller="mainController as main"> <div class=""> <h2>{{main.message}}</h2> <p ng-repeat="peeps in main.list"> {{peeps.name}} {{peeps.sex}} </p> </div> </body> </html>