Node, Express, and MongoDB - a Primer

Welcome. Using Node, Express, and Mongoose, let’s create an interactive form.

Before you start, make sure you have Node installed for your specific operating system. This tutorial also uses Express v4.9.0 and Mongoose v3.8.21.

Project Setup

Start by installing the Express generator, which will be used to create a basic project for us:

1
$ npm install -g express-generator@4

The -g flag means that we’re installing this on our entire system.

Navigate to a convenient directory, like your “Desktop” or “Documents”, then create your app:

1
$ express node-mongoose-form

Check out the project structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── index.js
│   └── users.js
└── views
    ├── error.jade
    ├── index.jade
    └── layout.jade

Don’t worry about the files and folders for now. Just know that we have created a boilerplate that could be used for a number of Node applications. This took care of the heavy lifting, adding common files and functions associated with all apps.

Notice the package.json file. This stores your project’s dependencies, which we still need to install:

1
2
$ cd node-mongoose-form
$ npm install

Now let’s install one last dependency:

1
$ npm install mongoose --save

The --save flag adds the dependencies and their versions to the package.json file. Take a look.

Sanity check

Let’s test our setup by running the app:

1
$ npm start

Navigate to http://localhost:3000/ in your browser and you should see the “Welcome to Express” text.

Supervisor

I highly recommend setting up Supervisor so that you can run your app and watch for code changes. Check out the above link to learn more.

1
$ npm install supervisor -g

Kill the server by pressing CTRL-C.

Once installed, let’s update the package.json file to utilize Supervisor to run our program.

Simply change this-

1
2
3
"scripts": {
  "start": "node ./bin/www"
},

To this:

1
2
3
"scripts": {
  "start": "supervisor ./bin/www"
},

Let’s test again:

1
$ npm start

In your terminal you should see:

1
Watching directory 'node-mongoose-form' for changes.

If you see that, you know it’s working right. Essentially, Supervisor is watching that directory for code changes, and if they do occur, then it will refresh your app for you so you don’t have to constantly kill the server then start it back up. It saves a lot of time and keystrokes.

Awesome. With the setup out of the way, let’s get our hands dirty and actually build something!

Routes

Grab your favorite text editor, and then open the main file, app.js, which houses all of the business logic. Take a look at the routes:

1
2
app.use('/', routes);
app.use('/users', users);

Understanding how routes work as well as how to trace all the files associated with an individual route is an important skill to learn. You’ll be able to approach most applications and understand how they work just by starting with the routes.

Let’s look at this route:

1
app.use('/users', users)

Here, we know that this route is associated with the /users endpoint. What’s an endpoint? Simply navigate to http://localhost:3000/users.

So the end user navigates to that endpoint and expects something to happen. That could mean some HTML is rendered or perhaps JSON is returned. That’s not important at this point. For now, let’s look at how Node handles that logic for “handling routes”.

Also, within that route, you can see the variable users. Where is that in this file? It’s at the top, and it loads in another file within our app:

1
var users = require('./routes/users');

Open that file:

1
2
3
4
5
6
7
8
9
var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res) {
  res.send('respond with a resource');
});

module.exports = router;

What’s happening here? We won’t touch everything but essentially when that endpoint is hit it responds by sending text in the form of a response to the end user - “respond with a resource”. Now, of course you don’t always have to send text. You could respond with a template or view like a Jade file that gets rendered into HTML. We’ll look at how this works in just a minute when we add our own routes.

Make sure you understand everything in this section before moving on. This is very important.

Add a new route

Let’s now add a new route that renders an HTML form to the end user.

Start by adding the route handler in the app.js file:

1
app.use('/form', form);

Remember this simply means app.use('/ENDPOINT', VARIABLE_NAME);,

Use the form variable to require a JS file within our routes folder.

1
var form = require('./routes/form');

Take a look in the terminal. You should see an error, indicating Node can’t find that ‘./routes/form’ module. We need to create it!

Create that JS file/module by saving an empty file called form.js to the “routes” directory. Add the following code:

1
2
3
4
5
6
7
8
9
var express = require('express');
var router = express.Router();

/* GET form. */
router.get('/', function(req, res) {
  res.send('My funky form');
});

module.exports = router;

Remember what this code res.send('My funky form'); should do? If not, review the previous section.

Navigate to http://localhost:3000/form. You should see the text “‘My funky form” on the page. Sweet.

Jade

Jade is a templating language, which compiles down to HTML. It makes it easy to separate logic from markup.

Take a quick look at the layout.jade and index.jade files with the “views” folder. There’s a relationship between those two files. It’s called inheritance. We define the base structure in the layout file, which contains common structure that can be reused in multiple places.

Do you see the block keyword?

What really happens when the index file is rendered is that it first inherits the base template because of the extends keywords. So, the layout template then gets rendered, which eventually pulls in the child template, overwriting the block keyword with:

1
2
h1= title
  p Welcome to #{title}

Hope that makes sense. If not, check out this resource for more info.

Setup form.jade

Create a new file called “form.jade” in the “views” directory, and then add the following code:

1
2
3
4
5
extends layout

block content
  h1= title
  p Welcome to #{title}

The same thing is happening here with inheritance. If you’re unfamiliar with Jade syntax, title is essentially a variable, which we can pass in from ./routes/form.js.

Update ./routes/form.js by changing-

1
res.send('My funky form');

To:

1
res.render('form', { title: 'My funky form' });

This just says, “When a user hits the /form endpoint, render the form.jade file and pass in My funky form as the title.”

Keep in mind that all Jade files are converted to HTML. Browsers can’t read the Jade syntax, so it must be in HTML by the time the end user sees it.

Ready to test? Simple refresh http://localhost:3000/form.

Did it work? If yes, move on. If not, go back through this section and review. Look in you terminal as well to see the error(s). If you’re having problems, don’t beat yourself up. It’s all part of learning!

Update form.jade

So, let’s update the Jade syntax to load a form.

1
2
3
4
5
6
7
8
9
10
extends layout

block content
  //- passed into layout.jade when form.jade is rendered
  block content
    h1= title
    form(method="post" action="/create")
      label(for="comment") Got something to say:
      input(type="text", name="comment", value=comment)
      input(type="submit", value="Save")

I’m not going to touch on all the Jade syntax, but essentially, we have just a basic HTML form to submit comments.

Refresh your browser. Do you see the form? Try clicking save. What happens? Well, you just tried to send a POST request to the /create endpoint, which does not exist. Let’s set it up.

Add route handler for /create

Open app.js and add a new route:

1
app.use('/create', form);

Notice how we’re using the same form variable. What does this mean?

Open form.js to add the logic for this new route:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var express = require('express');
var router = express.Router();

/* GET form. */
router.get('/', function(req, res) {
  res.render('form', { title: 'My funky form' });
});

/* POST form. */
router.post('/', function(req, res) {
  console.log(req.body.comment);
  res.redirect('form');
});

module.exports = router;

1Test this out again. Now, when you submit the form, we have the /create endpoint setup, which then grabs the text from the input box via req.body.comment. Make sure the text is consoled to your terminal.

Okay. So, we are handling the routes, rendering the right template, let’s now setup Mongoose to save the data from our form.

Setup Mongoose

Mongoose is awesome. Start with defining the Schema, which the maps to a collection in Mongo. It utilizes OOP.

Create a file called database.js in your app’s root directory, then add the following code:

1
2
3
4
5
6
7
8
9
10
var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

var Comment = new Schema({
    title : String,
});

mongoose.model('comments', Comment);

mongoose.connect('mongodb://localhost/node-comment');

Here, we required/included the Mongoose library along with a reference to the Schema() method. As said, you always start with defining the schema, then we linked it to collection called “comments”. Finally, we opened a connection to an instance of our local MongoDB.

If you don’t have the MongoDB server running. Do so now. Open a new terminal window, and run the command sudo mongod.

Next, open app.js and require the Mongoose config at the very top of the file:

1
2
// mongoose config
require('./database');

With Mongoose setup, we need to update form.js to create (via POST) and read (via GET) data from the Mongo collection.

Handling form GET requests

Open form.js. Require Mongoose as well as the comments model, which we already created:

1
2
var mongoose = require('mongoose');
var Comment = mongoose.model('comments');

Now, update the function handling GET requests:

1
2
3
4
5
6
7
8
9
10
/* GET form. */
router.get('/', function(req, res) {
  Comment.find(function(err, comments){
    console.log(comments)
    res.render(
      'form',
      {title : 'My funky form', comments : comments}
    );
  });
});

Comment.find() grabs all comments from the Mongo collection, which we assign to the variable comments. We can now use that variable in our Jade file.

Update form.jade to display comments

Let’s add a loop to iterate through the comments and then display the title key from the collection.

1
2
3
4
5
6
7
8
9
10
11
12
13
extends layout

block content
  //- passed into layout.jade when form.jade is rendered
  block content
    h1= title
    form(method="post" action="/create")
      label(for="comment") Got something to say:
      input(type="text", name="comment", value=comment)
      input(type="submit", value="Save")
    br
    - for comment in comments
      p= comment.title

Do you remember where we set the title key? Check out the database schema in database.js.

Before this will actually work - e.g., display comments - we first need to add the logic to insert data into the Mongo collection.

Handling form POST requests

Back in form.js, update the function handling POST requests:

1
2
3
4
5
6
7
8
/* POST form. */
router.post('/', function(req, res) {
  new Comment({title : req.body.comment})
  .save(function(err, comment) {
    console.log(comment)
    res.redirect('form');
  });
});

The simply saves a new comment, which again is grabbed from the form via req.body.comment.

Sanity Check

Refresh you app. Add some comments. If you’ve done everything correctly, the comments should be displayed beneath the form.

Conclusion

That’s it. Grab the code from the repository. Cheers!

Kickstarting Angular With Gulp and Browserify, Part 2 - Browserify

Hello. Welcome to the second half. Last time, we built a nice Angular starter project, utilizing Gulp and Bower. Let’s take this a step further and add the power of Browserify into the mix. Before you read any further, check out the Introduction to the Browserify Handbook to learn about the problems that Browserify solves.

Just want the code? Get it here.

Install Dependencies

Let’s get Browserify installed…

First, install Browserify globally

1
$ npm install -g browserify

Then install the Gulp dependencies locally

1
$ npm install gulp-browserify gulp-concat --save

The former dependency allows you to run Browserify from Gulp, while the latter concatenates all the Bowerserify dependencies into a single JS file.

Update the Gulpfile

Update the requirements

1
2
var browserify = require('gulp-browserify');
var concat = require('gulp-concat');

Add the following tasks

1
2
3
4
5
6
7
8
9
gulp.task('browserify', function() {
  gulp.src(['app/js/main.js'])
  .pipe(browserify({
    insertGlobals: true,
    debug: true
  }))
  .pipe(concat('bundled.js'))
  .pipe(gulp.dest('./app/js'))
});

Now update the default task

1
2
3
4
// default task
gulp.task('default',
  ['lint', 'browserify', 'connect']
);

Update the HTML

Change the included JS file in index.html.

From:

1
<script src="./js/main.js"></script>

To:

1
<script src="./js/bundled.js"></script>

Test

To recap:

  1. We added Browserify
  2. Updated the build process so that a single JS file named bundled.js is created
  3. Updated index.html to include that new JS file
1
$ gulp

Navigate to http://localhost:8888/ and you should still see:

1
2
3
Angular-Gulp-Browserify-Starter

Testing...

Notice the bundled.js file. Again, this is generated by concat('bundled.js'). If you kill the server, then try to run it again, you’ll get an error. Essentially, the bundled.js file needs to be removed before each run. So update the clean task:

1
2
3
4
5
6
gulp.task('clean', function() {
    gulp.src('./dist/*')
      .pipe(clean({force: true}));
    gulp.src('./app/js/bundled.js')
      .pipe(clean({force: true}));
});

Browserify

Remember all those Bower components in the index.js file? Let’s clean up that mess by requiring our app’s dependencies with Browserify.

Update the HTML (again)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html ng-app="SampleApp">
  <head lang="en">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Angular-Gulp-Browserify-Starter</title>
    <!-- styles -->
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
    <link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.css"/>
    <link rel="stylesheet" href="bower_components/animate.css/animate.css"/>
    <link rel="stylesheet" href="css/main.css"/>
  </head>
  <body>
    <div class="container">
      <h1>Angular-Gulp-Browserify-Starter</h1>
      <!-- views -->
      <div ng-view></div>
    </div>
    <!-- scripts -->
    <script src="bower_components/jquery/dist/jquery.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="./js/bundled.js"></script>
  </body>
</html>

Now, we need to use Browserify to require the following depencies in our app:

  1. angular.js
  2. angular-route.js
  3. angular-animate.js

Why don’t we replace all of our Bower components? It’s good to use both Bower and Browserify in case NPM does not have a certain dependency that Bower may have. The point of this example is to show you how to use both.

Install Requirements

Go ahead and install the requirements we need via NPM:

1
$ npm install angular angular-route angular-animate --save

Update JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
(function () {

  'use strict';

  require('angular');
  require('angular-route');
  require('angular-animate');

  angular.module('SampleApp', ['ngRoute', 'ngAnimate'])

  .config([
    '$locationProvider',
    '$routeProvider',
    function($locationProvider, $routeProvider) {
      $locationProvider.hashPrefix('!');
      // routes
      $routeProvider
        .when("/", {
          templateUrl: "./partials/partial1.html",
          controller: "MainController"
        })
        .otherwise({
           redirectTo: '/'
        });
    }
  ]);

  //Load controller
  angular.module('SampleApp')

  .controller('MainController', [
    '$scope',
    function($scope) {
      $scope.test = "Testing...";
    }
  ]);

}());

Now we can include various modules the “Node-way” using require() calls, giving you access to nearly 90,000 modules.

Controller

Let’s abstract out the controller to a file of its own.

First, update main.js again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(function () {

  'use strict';

  require('angular');
  require('angular-route');
  require('angular-animate');
  var mainCtrl = require('./controllers/mainctrl');

  angular.module('SampleApp', ['ngRoute', 'ngAnimate'])

  .config([
    '$locationProvider',
    '$routeProvider',
    function($locationProvider, $routeProvider) {
      $locationProvider.hashPrefix('!');
      // routes
      $routeProvider
        .when("/", {
          templateUrl: "./partials/partial1.html",
          controller: "MainController"
        })
        .otherwise({
           redirectTo: '/'
        });
    }
  ])

  //Load controller
  .controller('MainController', ['$scope', mainCtrl]);

}());

Now create a new folder called “controllers” within “app/js”. In the new folder add a new file called mainctrl.js:

1
2
3
4
module.exports = function($scope) {
  $scope.test = "Testing...";
  console.log("required!");
};

This syntax should look familiar if you’ve worked with Node before. We use exports to expose the function, which we then have access to in main.js since it’s part of the requirements.

Test Again

1
2
$ gulp clean
$ gulp

Navigate to http://localhost:8888/ to make sure everything still works.

Update the Build

Now that we have the default task working, let’s update the build process so we can create a deployable build.

Update the Gulpfile

Add the following task to the gulpfile:

1
2
3
4
5
6
7
8
9
gulp.task('browserifyDist', function() {
  gulp.src(['app/js/main.js'])
  .pipe(browserify({
    insertGlobals: true,
    debug: true
  }))
  .pipe(concat('bundled.js'))
  .pipe(gulp.dest('./dist/js'))
});

This task simply updates where the bundled.js is stored after creation.

Finally, update the build task itself adding in the above task:

1
2
3
4
// build task
gulp.task('build',
  ['lint', 'minify-css', 'browserifyDist', 'copy-html-files', 'copy-bower-components', 'connectDist']
);

Create a Build

1
$ gulp build

Check out the live app at http://localhost:9999/. Deploy your app, if you’d like.

Conclusion

Let’s recap. Over the past two posts, we’ve created a sample app that can be used as a seed for all of your Angular projects. Want to use this in your own projects?

  1. Clone the repo
  2. Install the global requirements: npm install -g gulp bower browserify
  3. Install the local requirements: npm install
  4. Install the Bower components: bower install
  5. Run locally: gulp
  6. Create a build: gulp build

I encourage you to add your favorite libraries and modules, which is easy to do. Looking for a client side dependency? Be sure to check NPM first before relying on Bower so you can take advantage of the simple require calls, via Browserify, which reduces code clutter and enables you to write modular, re-usable code.

As always, I’d love to hear your feedback. How are you using Browserify in your projects? Comment below.

Thanks for reading.

Kickstarting Angular With Gulp and Browserify, Part 1 - Gulp and Bower

Let’s develop an Angular boilerplate. Why? Despite the plethora of Angular seeds/generators/templates/boilerplates/starters/etc. on Github, none of them will ever do exactly what you want unless you build your own, piece by piece. By designing your own, you will better understand each component as well as how each fits into the greater project. Stop fighting against a boilerplate that just doesn’t fit your needs and start from scratch. Keep it simple, as you learn the process.

In this first part, we’ll start with Angular and Gulp, getting a working project setup. Next time we’ll add Browserify into the mix.

This tutorial assumes you have Node.js installed and have working knowledge of NPM and Angular. Just want the code? Get it here.

Project Setup

Install Dependencies

Setup a project folder and create a package.json file:

1
2
$ mkdir project_name && cd project_name
$ npm init

The npm init command helps you create your project’s base configuration through an interactive prompt. Be sure to update the ‘entry point’ to ‘gulpfile.js’. You can just accept the defaults on the remaining prompts.

Do the same for Bower:

1
$ bower init

Accept all the defaults. After the file is created update the ignore list:

1
2
3
4
5
6
7
"ignore": [
  "**/.*",
  "node_modules",
  "app/bower_components",
  "test",
  "tests"
],

Install global dependencies:

1
$ npm install -g gulp bower

Bower install directory

You can specify where you want the dependencies (commonly known as bower components) installed to by adding a .bowerrc file and adding the following code:

1
2
3
{
  "directory": "/app/bower_components"
}

Install local dependencies:

NPM

1
$ npm install gulp bower gulp-clean gulp-jshint gulp-uglify gulp-minify-css gulp-connect --save

Bower

1
$bower install angular angular-animate angular-route jquery animate.css bootstrap fontawesome --save

The --save flag adds the dependencies to the package.json and bower.json files, respectively.

We’ll address each of these dependencies shortly. For now, be sure you understand the project’s core dependencies:

  • Gulp is a Javascript task runner, used to automate repetitive tasks (i.e., minifying, linting, testing, building, compiling) to simplify the build process.
  • Bower manages front-end dependencies.

Folder Structure

Let’s setup a base folder structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.
├── app
│   ├── bower_components
│   ├── css
│   │    └── main.css
│   ├── img
│   ├── index.html
│   ├── partials
│   │    ├── partial1.html
│   │    └── partial2.html
│   └── js
│   │    └── main.js
├── .bowerrc
├── .gitignore
├── bower.json
├── gulpfile.js
├── node_modules
└── package.json

Add the files and folders not already included. This structure is based on the popular Angular Seed boilerplate, developed by the Angular team.

Gulp

To start, we just need the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
// gulp
var gulp = require('gulp');

// plugins
var connect = require('gulp-connect');


gulp.task('connect', function () {
  connect.server({
    root: 'app/',
    port: 8888
  });
});

This allows us to serve our future Angular app on a development server running on port 8888.

Test

Let’s test it out. Add the word ‘hi’ to the index.html file, then run the following command:

1
$ gulp connect

Navigate to http://localhost:8888/ and you should see ‘hi’ staring back at you. Let’s build a quick sample app. Keep the server running…

Develop a Sample App

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html ng-app="SampleApp">
  <head lang="en">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Angular-Gulp-Browserify-Starter</title>
    <!-- styles -->
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
    <link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.css"/>
    <link rel="stylesheet" href="bower_components/animate.css/animate.css"/>
    <link rel="stylesheet" href="css/main.css"/>
  </head>
  <body>
    <div class="container">
      <h1>Angular-Gulp-Browserify-Starter</h1>
      <!-- views -->
      <div ng-view></div>
    </div>
    <!-- scripts -->
    <script src="bower_components/jquery/dist/jquery.js"></script>
    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/angular-route/angular-route.js"></script>
    <script src="bower_components/angular-animate/angular-animate.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="js/main.js"></script>
  </body>
</html>

This should look familiar. The ng-app directive initiates an Angular app while ng-view sets the stage for routing.

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(function () {

'use strict';


  angular.module('SampleApp', ['ngRoute', 'ngAnimate'])

  .config([
    '$locationProvider',
    '$routeProvider',
    function($locationProvider, $routeProvider) {
      $locationProvider.hashPrefix('!');
      // routes
      $routeProvider
        .when("/", {
          templateUrl: "./partials/partial1.html",
          controller: "MainController"
        })
        .otherwise({
           redirectTo: '/'
        });
    }
  ]);

  //Load controller
  angular.module('SampleApp')

  .controller('MainController', [
    '$scope',
    function($scope) {
      $scope.test = "Testing...";
    }
  ]);

}());

Again, this should be relatively straightforward. We setup the basic Angular code to establish a route handler along with a controller that passes the variable test to the template.

partial1.html

Now let’s add the partial template:

1
<p>{{ test }}</p>

Test

Back in your browser, refresh the page. You should see the text:

1
2
3
Angular-Gulp-Browserify-Starter

Testing...

Create the Build

Now that our app is working locally, let’s modify our gulpfile.js to generate a deployable build. Kill the server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// gulp
var gulp = require('gulp');

// plugins
var connect = require('gulp-connect');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var minifyCSS = require('gulp-minify-css');
var clean = require('gulp-clean');

// tasks
gulp.task('lint', function() {
  gulp.src(['./app/**/*.js', '!./app/bower_components/**'])
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
    .pipe(jshint.reporter('fail'));
});
gulp.task('clean', function() {
    gulp.src('./dist/*')
      .pipe(clean({force: true}));
});
gulp.task('minify-css', function() {
  var opts = {comments:true,spare:true};
  gulp.src(['./app/**/*.css', '!./app/bower_components/**'])
    .pipe(minifyCSS(opts))
    .pipe(gulp.dest('./dist/'))
});
gulp.task('minify-js', function() {
  gulp.src(['./app/**/*.js', '!./app/bower_components/**'])
    .pipe(uglify({
      // inSourceMap:
      // outSourceMap: "app.js.map"
    }))
    .pipe(gulp.dest('./dist/'))
});
gulp.task('copy-bower-components', function () {
  gulp.src('./app/bower_components/**')
    .pipe(gulp.dest('dist/bower_components'));
});
gulp.task('copy-html-files', function () {
  gulp.src('./app/**/*.html')
    .pipe(gulp.dest('dist/'));
});
gulp.task('connect', function () {
  connect.server({
    root: 'app/',
    port: 8888
  });
});
gulp.task('connectDist', function () {
  connect.server({
    root: 'dist/',
    port: 9999
  });
});


// default task
gulp.task('default',
  ['lint', 'connect']
);
// build task
gulp.task('build',
  ['lint', 'minify-css', 'minify-js', 'copy-html-files', 'copy-bower-components', 'connectDist']
);

What’s happening here?

  1. gulp-jshint checks for code quality in the JS files. If there are any issues the build fails and all errors output to the console.
  2. gulp-clean removes the entire build folder so that we start fresh every time we generate a new build.
  3. gulp-uglify and gulp-minify-css minify JS and CSS, respectively.

Build commands

Default

The default task, gulp, is a compound task that runs both the lint and connect tasks. Again, this just serves the files in the “app” folder on http://localhost:8888/.

Build

The build task creates a new directory called “dist”, runs the linter, minifies the CSS and JS files, and copies all the HTML files and Bower Components. You can then see what the final build looks like on http://localhost:9999/ before deployment. You should also run the clean task before you generate a build.

Test this out:

1
$ gulp clean build

Conclusion

Well, hopefully you now have a better understanding of how Gulp can greatly simply the build process, handling a number of repetitive tasks. Next time we’ll clean up some of the mess that the Bower components leave behind by adding Browserify into the mix and detail a nice workflow that you can use for all your Angular projects.

Leave questions and comments below. Cheers!