|
| 1 | +# MEAN Skeleton |
| 2 | +This repo is a basic structure for a web application that uses the MEAN stack (MongoDB, Express, AngularJS, and NodeJS). It is made for beginners with a basic understanding of these technologies. If there are any questions, feel free to create a New Issue on the repository. |
| 3 | + |
| 4 | +## Setup |
| 5 | + |
| 6 | +#### Node |
| 7 | +To get this running locally, start by installing [**NodeJS**](http://nodejs.org/download/). The Node website is very good at explaining how to do this. Once installed verify that npm (Node Package Manager) came with the installation by running npm in Terminal. |
| 8 | + |
| 9 | +#### Nodemon |
| 10 | +I recommend installing [Nodemon](https://github.com/remy/nodemon) to assist you in development. It will watch for changes in your server files and automatically restart the server for you. That way you can stick to developing rather than constantly restarting the process manually. |
| 11 | + |
| 12 | +#### Mongo |
| 13 | +Next, download and install [**MongoDB**](http://www.mongodb.org/downloads). **Make sure to follow all the directions for installing on your respective operating system.** Verify that this is installed correctly by running the mongo server locally with the command ```mongod```. The mongod service must be running locally to point to local Mongo databases. |
| 14 | + |
| 15 | + |
| 16 | +## Configure |
| 17 | +Clone the repository, and you will have the structure in place to start. Begin by editting the package.json file. |
| 18 | + |
| 19 | +#### Package.json |
| 20 | +```javascript |
| 21 | +// package.json |
| 22 | +{ |
| 23 | + "name" : "mean-skeleton", |
| 24 | + "version" : "0.0.1", |
| 25 | + "description" : "Skeleton for an application using the MEAN stack.", |
| 26 | + "main" : "server.js", |
| 27 | + "author" : "Alfred", |
| 28 | + "dependencies" : { |
| 29 | + "express" : "latest", |
| 30 | + "mongoose" : "latest" |
| 31 | + } |
| 32 | +} |
| 33 | +``` |
| 34 | + |
| 35 | +**Don't forget to change** |
| 36 | +* Name |
| 37 | +* Version, if applicable |
| 38 | +* Description |
| 39 | +* Author |
| 40 | + |
| 41 | +Install all listed dependencies by navigating to the repository in Terminal and running the command |
| 42 | + |
| 43 | +```npm install``` |
| 44 | + |
| 45 | +This will install [**Express**](http://expressjs.com/4x/api.html) along with the other packages in the package.json file. |
| 46 | + |
| 47 | +In the above, "latest" denotes version number. It's a string. I set it to latest to simplify use. I am also assuming that these package owners will continue to document their changes as they update their packages. Luckily [**StrongLoop**](http://strongloop.com/) is pretty good about it. |
| 48 | + |
| 49 | +<Enter> |
| 50 | +<Enter> |
| 51 | +<Enter> |
| 52 | + |
| 53 | +#### Connecting to a Database |
| 54 | +Replace the link below with the url for your hosted DB or keep this url for the default local MongoDB |
| 55 | +```javascript |
| 56 | +// config/db.js |
| 57 | +module.exports = { |
| 58 | + url : 'mongodb://localhost:27017/test' |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +#### Server Settings |
| 63 | +Right now we have a few moving parts, but they aren't connected yet. Let's change that. |
| 64 | + |
| 65 | +In the root of our project you'll find a server.js file. This is the file that we will run to make our server live. |
| 66 | + |
| 67 | +First, we require our dependencies. You'll notice I declare bodyParser, which is an Express middleware for parsing HTTP Responses and Requests. I also declare morgan, another Express middleware, used for logging. |
| 68 | + |
| 69 | +I suggest reading up on them and other Express middlewares, but the settings I have here should work for this example. |
| 70 | + |
| 71 | +```javascript |
| 72 | +// server.js |
| 73 | + |
| 74 | +// Dependencies |
| 75 | +var express = require('express'); |
| 76 | +var mongoose = require('mongoose'); |
| 77 | +var bodyParser = require('body-parser'); |
| 78 | +var morgan = require('morgan'); |
| 79 | +``` |
| 80 | + |
| 81 | +Next, we configure our Express application. I import the database file to this one in an effort to keep the database file nice and lean. |
| 82 | +```javascript |
| 83 | +// server.js |
| 84 | + |
| 85 | +// Configs |
| 86 | +var db = require('./config/db'); |
| 87 | + |
| 88 | +// Connect to the DB |
| 89 | +mongoose.connect(db.url); |
| 90 | + |
| 91 | +// Initialize Express app |
| 92 | +var app = express(); |
| 93 | +// Configure |
| 94 | + |
| 95 | +// To expose public assets to the world |
| 96 | +app.use(express.static(__dirname + '/public')); |
| 97 | + |
| 98 | +// log every request to the console |
| 99 | +app.use(morgan('dev')); |
| 100 | + |
| 101 | +// For parsing HTTP responses |
| 102 | +app.use(bodyParser.json()); |
| 103 | +app.use(bodyParser.urlencoded({ |
| 104 | + extended: true |
| 105 | +})); |
| 106 | +``` |
| 107 | + |
| 108 | +After configuring, we add the routes by sending the application as a parameter to the require statements. |
| 109 | + |
| 110 | +Lastly, start the application with listen by giving it a port number. |
| 111 | +```javascript |
| 112 | +// server.js |
| 113 | + |
| 114 | +// Express Routes |
| 115 | +require('./app/routes/api')(app); |
| 116 | +require('./app/routes/routes')(app); |
| 117 | + |
| 118 | +// Start the app with listen |
| 119 | +app.listen(3000); |
| 120 | + |
| 121 | +``` |
| 122 | + |
| 123 | +#### Run our server |
| 124 | +To run this server, in the root of the project directory run |
| 125 | + |
| 126 | +```node server.js``` or if using nodemon ```nodemon server.js``` |
| 127 | + |
| 128 | +It will start the application and you should be able to navigate to http://localhost:3000 and see our first page. |
| 129 | + |
| 130 | +## Make It Yours |
| 131 | +### Defining a Model |
| 132 | +This application is designed to implement the MV* pattern. Thus, start by creating a Model for our data. |
| 133 | + |
| 134 | +Each model should be defined in its own file in the app/models directory. |
| 135 | + |
| 136 | +```javascript |
| 137 | +// app/models/user.js |
| 138 | + |
| 139 | +var mongoose = require('mongoose'), |
| 140 | +Schema = mongoose.Schema; |
| 141 | + |
| 142 | +var UserSchema = new Schema ({ |
| 143 | + name : { |
| 144 | + type: String |
| 145 | + }, |
| 146 | +}); |
| 147 | + |
| 148 | +module.exports = mongoose.model('User', UserSchema); |
| 149 | +``` |
| 150 | + |
| 151 | +The above is an example of a User model with a name field. We require [mongoose](http://mongoosejs.com/index.html) as our Object Modeler and then require Mongoose Schema as an object to extend and create our own Schema. |
| 152 | + |
| 153 | +Then define the fields of your model. You can set the datatype, mark fields as required and validate the data before saving the document to the collection. Read more about mongoose schemas in their [docs](http://mongoosejs.com/docs/guide.html) |
| 154 | + |
| 155 | +Lastly, you are going to want to export the model with a name and a Schema Object for use in our API. |
| 156 | + |
| 157 | +### Defining Routes |
| 158 | +These are Express-style routes, to see all the fancy things you can do refer to the Express docs. |
| 159 | + |
| 160 | +To define a simple route to serve a static HTML page: |
| 161 | +```javascript |
| 162 | +// app/routes/route.js |
| 163 | +module.exports = function(app) { |
| 164 | + app.get('/path', function(request, response) { |
| 165 | + response.sendfile('path/to.html'); |
| 166 | + }); |
| 167 | +} |
| 168 | +``` |
| 169 | + |
| 170 | +To define a route that writes to the response: |
| 171 | +```javascript |
| 172 | +// Assuming this is wrapped in the export line |
| 173 | + app.get('/path', function(request, response) { |
| 174 | + response.writeHead(200); // 200 Success Status code |
| 175 | + response.write(<h1>Hello World</h1>); // Writes to the body |
| 176 | + response.end(); // Closes the write stream |
| 177 | + }); |
| 178 | +``` |
| 179 | + |
| 180 | +#### Defining an API |
| 181 | +Since making RESTful applications makes everything easy on everyone, let's define a way to interact with our model |
| 182 | + |
| 183 | +To make a call that returns our models: |
| 184 | +```javascript |
| 185 | +/* app/routes/api.js */ |
| 186 | + |
| 187 | +module.exports = function(app) { |
| 188 | + /* Require mongoose, and the Model */ |
| 189 | + var mongoose = require('mongoose'), |
| 190 | + Model = require('app/models/model') |
| 191 | + |
| 192 | + app.get('path', function(request, response) { |
| 193 | + Model.find({conditions : 'in JSON'}, function(err, models) { |
| 194 | + response.send(models); // Sends the data in JSON in the response |
| 195 | + }); |
| 196 | + }); |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +Express also supports the other HTTP verbs like POST. |
| 201 | +To create a model and return them all after one is created. |
| 202 | +```javascript |
| 203 | + // Assuming this is wrapped in the export after the require lines |
| 204 | + |
| 205 | + app.post('path', function (req, res) { |
| 206 | + Model.create({ |
| 207 | + name : req.body.name // Value from form with field "name" |
| 208 | + }, function(err, model) { |
| 209 | + |
| 210 | + Model.find(function(err, models) { |
| 211 | + res.send(models); |
| 212 | + }); |
| 213 | + }); |
| 214 | + }); |
| 215 | +``` |
| 216 | + |
| 217 | +So far we have set up quite the backend, and gone over the M, E, and N in MEAN. Now we can finally start **A**. |
| 218 | + |
| 219 | +## Make It Pretty |
| 220 | +We use [AngularJS](https://angularjs.org/) to receive all of the data sent from Node in the backend to give us a truly dynamic webpage it also offers us many directives to display this data on the frontend. |
| 221 | + |
| 222 | +### Define an Angular Controller |
| 223 | +Angular follows the MVC pattern on the frontend. We won't have to really do anything to the model once it gets to Angular so, next up is the Controller. We define Angular controllers and export them as angular modules. |
| 224 | + |
| 225 | +[Angular Services](https://docs.angularjs.org/api/ng/service) |
| 226 | + |
| 227 | +To add more functionality to our controller, we can throw in more angular services as arguments after ```$scope``` |
| 228 | + |
| 229 | +I recommend adding the ```$http``` service to make frontend wrappers for our API calls. |
| 230 | +```javascript |
| 231 | +var appController = angular.module('appController', []); |
| 232 | + |
| 233 | +function appCtrl($scope, $http) { |
| 234 | + ... |
| 235 | +} |
| 236 | +``` |
| 237 | + |
| 238 | +[Angular Directives](https://docs.angularjs.org/api/ng/directive) |
| 239 | + |
| 240 | +To use what we have defined in our controller, we use the ng-app directive to point to the controller for this page. We must also be sure to load angular and the controller.js file. |
| 241 | + |
| 242 | +Lastly, add the ng-controller directive to the body of the document to make all of the controller methods available to the body. |
| 243 | + |
| 244 | + |
| 245 | +```html |
| 246 | +<!DOCTYPE html> |
| 247 | +<html ng-app="appController"> |
| 248 | + <head> |
| 249 | + <!-- Angular from Google CDN --> |
| 250 | + <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script> |
| 251 | + |
| 252 | + <!-- Load AppController --> |
| 253 | + <script type="text/javascript" src="../controllers/app.js"></script> |
| 254 | + </head> |
| 255 | + <body ng-controller="appCtrl"> |
| 256 | + |
| 257 | + </body> |
| 258 | +</html> |
| 259 | +``` |
| 260 | + |
| 261 | +## Testing Your Application |
| 262 | +Making sure that our application works is an incredibly important part of developing for any platform. The entire software stack in this instance is JavaScript. This means we only need JavaScript testing libraries. |
| 263 | + |
| 264 | +#### Testing Angular |
| 265 | +[Jasmine](http://jasmine.github.io/) is recommended for testing your frontend JavaScripts. If you are unfamiliar with jasmine check out the tutorial [here](http://jasmine.github.io/2.0/introduction.html) |
| 266 | + |
| 267 | +You can find a very basic angular controller spec in the ``test-angular/spec/`` directory. |
| 268 | + |
| 269 | +#### Testing Node |
| 270 | +[jasmine-node](https://github.com/mhevery/jasmine-node) is jasmine for your Node backend. Their documentation is brief because they assume that you are familiar with running jasmine already. |
| 271 | + |
| 272 | +Install the package globally and it comes with a CLI. |
| 273 | + |
| 274 | +To run Node tests: |
| 275 | + |
| 276 | +```jasmine-node test-node/``` |
| 277 | + |
| 278 | +This will recursively run all the files named *spec.js, which means all of your specs must end with spec.js |
| 279 | + |
| 280 | +## Disclaimer |
| 281 | +I do not claim to be an expert at any of the technologies used. I also do not claim to own any of the technologies mentioned in this guide. |
0 commit comments