A picture of Konrad

How to Create MEAN Stack Applications Smoothly (Part 1)

Introduction to the MEAN stack, setup and posts API

Parts of the series:

  1. Introduction to the MEAN stack (you are here)
  2. Building a frontend in Angular
  3. Adding an Admin Panel

Have you ever had a dream about a world in which you could use the same programming language on both the frontend and the backend? Or maybe you are a frontend developer who have always wanted to work on the backend side, but knows only JavaScript? Well, guess what - you are not alone. Using only JavaScript with MEAN stack for developing fully-working projects is easier than you think and in this tutorial I will show you how to do this.

The end goal for the tutorial will be to have a functional blog CMS written in JavaScript. In the first part we will set the project and develop a posts API endpoint. In the following parts we are going to create the frontend side, an admin panel for posts management and user authentication.

The MEAN stack

The MEAN stack is a set of technologies which allows us to be writing applications in one programming language (JavaScript) both on the client & server sides. It’s free and open-source. Its parts work together very well, making the MEAN stack great for building robust and dynamic web apps.

The “MEAN” acronym is a combination of the first letters of its four components. So let’s look into them right now:

  • MongoDB - NoSQL database which stores data in JSON-like documents
  • Express - lightweight web framework running on Node.js - the heart of our API
  • Angular - modern frontend framework written in TypeScript (superset of JavaScript)
  • Node.js - JavaScript runtime helping us build a server in JS

Like it or not, JavaScript is one of the most popular programming languages in the world. You can’t really develop dynamic and engaging UI without JavaScript. It’s implemented on the client-side and is essential for current web development. Most likely you already know and program in JS. So why not use it on the server-side as well?

JavaScript is in every component of the MEAN stack. You don’t need to hire different developers for each part of the application - everything could be done by one JavaScript programmer. All of the used technologies are battle-tested and have huge and strong communities behind them. The amount of ready-made components and libraries in JavaScript is enormous and drastically improves the development speed. Writing applications based on MEAN stack is very simple and quick.

Prerequisites

Before we start developing our project we will have to install two things:

If everything went well and we have database up and running, we can proceed to the next step.

Project setup

As a starting point for our project we will use Angular. Angular CLI is very helpful for creating new applications, so let’s go ahead and install it:

npm install -g @angular/cli

We now have access to everything that this great tool has to offer. Generating new project is very simple. It’s just one command:

ng new mean-blog

And that’s it! We can go to our newly created folder and serve the app now:

cd mean-blog && ng serve

Go to http://localhost:4200. If everything went fine, you should see a nice welcome message from Angular. We have our frontend part working now! How fast was that?

“Well, that’s great and all, but where is my API? Where is my database connection? Where is all that stuff I’ve come here for?” - you may say. I can assure you - we will get there. But first let’s look at the project's structure:

.
├── e2e
│   ├── src
│   │   ├── app.e2e-spec.ts
│   │   └── app.po.ts
│   ├── protractor.conf.js
│   └── tsconfig.e2e.json
├── node_modules
├── src
│   ├── app
│   │   ├── app.component.css
│   │   ├── app.component.html
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   └── app.module.ts
│   ├── assets
│   ├── environments
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── browserslist
│   ├── favicon.ico
│   ├── index.html
│   ├── karma.conf.js
│   ├── main.ts
│   ├── polyfills.ts
│   ├── styles.css
│   ├── test.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.spec.json
│   └── tslint.json
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── tsconfig.json
└── tslint.json

Quick explanation of some of the most important folders and files:

e2e/ - the home of the end-to-end tests. We’re not going to test our app, because we like to live "dangerously", so we’ll pretend that this folder doesn’t exist.

node_modules/ - the directory where all modules defined in package.json are installed. We don’t have to worry about it - Node.js handles this for us.

src/ - the heart of the frontend part of our project. It contains components, configurations, and assets.

src/app/ - the directory of the Angular components, styles, templates and unit tests.

src/app/app.module.ts - a very important file. It’s the root module of our app. It declares all used components and modules.

src/assets/ - we can keep our fonts, images etc. here. It will be copied on build.

src/environments/ - environment-specific configuration files.

src/index.html - that’s the file which users get when visiting our app.

src/main.ts - the entry point of our app. It bootstraps the AppModule.

package.json - contains our project’s dependencies which are installed in node_modules and custom scripts used for building, testing, serving and anything else we want.

I think that’s enough to start the work.

The server

It’s time to create the backend for our blog! First we have to install a few more dependencies:

npm install express body-parser mongoose nodemon concurrently --save

As mentioned previously, Express is our backend framework. "body-parser" is a middleware that parses request body and makes our life a little easier. Mongoose is an object modelling tool for MongoDB, it will also help us interact with the database. Nodemon will watch for the changes in our app and restart it automatically. With "concurrently" we will be able to run multiple commands in the same time.

We can now configure our server. Let’s create a new folder in the root directory of the app called server. It will contain all controllers, models and routes. Inside the server directory create another one - routes. Add an index.js file inside it:

const express = require('express');

const router = express.Router();

router.get('/', function (req, res) {
  res.send('API works!');
});

module.exports = router;

Not much happens here for now. We just have one route which returns an "API works!" message. But we can’t access it yet, because we still have to start our server. So let’s go ahead and create a server.js file in the root folder of the project:

const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const http = require('http');
const mongoose = require('mongoose');
const routes = require('./server/routes');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.use(express.static(path.join(__dirname, 'dist')));

app.use('/api', routes);

app.get('*', function (req, res) {
  res.sendFile(path.join(__dirname, 'dist/index.html'));
});

mongoose.connect('mongodb://127.0.0.1:27017/mean_tutorial');
mongoose.connection.on('error', console.error.bind(console, 'Database connection error:'));
mongoose.connection.once('open', function () {
  console.info('Successfully connected to the database');
});

const port = process.env.PORT || '3000';
app.set('port', port);

const server = http.createServer(app);

server.listen(port, function () {
  console.info(<code>Server started on http://localhost:${port}</code>)
});

First we are mounting our body parser middleware mentioned earlier. We want our frontend part to work on the root path, so we need to serve Angular’s dist folder with express.static(). Our API will be under the /api route. Any other url will lead to the index page. Next we are connecting to the database. By default it’s on mongodb://127.0.0.1:27017 (if you have different settings for MongoDB, you should change this line). mean_tutorial our database name and it will be created automatically by Mongoose. Finally we’re creating our server.

To start our server we just need to run node server.js, but remember we also have Angular app which needs to be built and it would be nice if we wouldn’t have to restart the server manually after each change. That’s when concurrently and nodemon come in handy.

Let’s go to the package.json file and change the start script to this:

"concurrently \"ng build --watch\" \"nodemon server.js\""

Now after running npm start our frontend will build, the server will start, it will watch for any changes in the code and restart automatically. Very convenient!

If you go to http://localhost:3000 you should still see the default page, but on http://localhost:3000/api we’ll have a short message from the API. There is still no use of the API, so we have to take care of it now.

Posts API

The previous two parts were just a preparation for our project. We have the frontend part working and the server running. It’s time for real work. It’s time for the posts API!

First we need to define the post model, so let’s create a new folder in the server directory called models and a new file in it -post.model.js:

const mongoose = require('mongoose');

const PostSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true
  },
  body: {
    type: String,
    required: true
  },
  author: {
    type: String,
    required: true
  },
  date: {
    type: Date,
    default: Date.now()
  },
  isPublished: {
    type: Boolean,
    default: false
  }
});

const Post = mongoose.model('Post', PostSchema);

module.exports = Post;

Schema is a definition of documents in MongoDB collection. We define our post’s properties here. Schema is compiled into model. Models are responsible for creating and reading documents from the database.

We are going to create the posts controller now. In the server directory create controllers/post.controller.js. The controller will perform actions on our post model. On the very top of the new file we have to import the model:

const Post = require('../models/post.model');

Let’s write our first action now. It will create a new post based on a data passed in a request.

exports.create = function (req, res) {
  const post = new Post({
    title: req.body.data.title,
    body: req.body.data.body,
    author: req.body.data.author,
    isPublished: req.body.data.isPublished
  });

  post.save()
    .then(function (createdPost) {
      return res.status(200).json({
        status: 200,
        data: createdPost,
        message: 'Success'
      });
    })
    .catch(function (err) {
      return res.status(400).json({
        status: 400,
        message: err.message
      });
    });
}

We are creating a new instance of the post and calling the save method on it (Mongoose handles it), then (if it was successful) we send a response with the created post's data (if it was successful) or with error message (if it wasn’t successful). And that’s it!

Now we need to create a route for this action. In the server/routes folder create a new file post.route.js:

const express = require('express');
const PostCtrl = require('../controllers/post.controller');

const router = express.Router();

router.post('/', PostCtrl.create);

module.exports = router;

Import it and use it in the index file of our routes. It should look like this now:

const express = require('express');
const postRoutes = require('./post.route');

const router = express.Router();

router.get('/', function (req, res) {
  res.send('API works!');
});

router.use('/posts', postRoutes);

module.exports = router;

We only have the POST route for the posts API for now, but we can test if everything works as expected.

For convenience I use Insomnia as REST Client and Robo 3T as GUI for MongoDB, but you can use whatever you want.

To check our posts endpoint we have to create a new request and pass data in its body. When we send it, we should get a nice response from the server. In Insomnia it looks like this:

Creating the post in Insomnia. JSON with body reading Other programmers hate him! Learn this one simple trick

If we haven’t encountered any errors, the new post should be added to the database. So let’s check it now:

Posts table in Robo 3T. Post object consists of _id, date, isPublished, title, body, author and __v fields.

It’s there indeed! It seems like everything works, so we can create all the missing actions in the post controller.

The whole controller should look like this:

const Post = require('../models/post.model');

exports.create = function (req, res) {
  const post = new Post({
    title: req.body.data.title,
    body: req.body.data.body,
    author: req.body.data.author,
    isPublished: req.body.data.isPublished
  });

  post.save()
    .then(function (createdPost) {
      return res.status(200).json({
        status: 200,
        data: createdPost,
        message: 'Success'
      });
    })
    .catch(function (err) {
      return res.status(400).json({
        status: 400,
        message: err.message
      });
    });
}

exports.get = function (req, res) {
  Post.findById(req.params.id)
    .then(function (post) {
      return res.status(200).json({
        status: 200,
        data: post,
        message: "Success"
      });
    })
    .catch(function (err) {
      return res.status(400).json({
        status: 400,
        message: err.message
      });
    });
}

exports.list = function (req, res) {
  Post.find()
    .then(function (posts) {
      return res.status(200).json({
        status: 200,
        data: posts,
        message: 'Success'
      })
    })
    .catch(function (err) {
      return res.status(400).json({
        status: 400,
        message: err.message
      });
    });
}



exports.update = function (req, res) {
  Post.findById(req.params.id)
    .then(function (post) {
      post.title = req.body.data.title || post.title;
      post.body = req.body.data.body || post.body;
      post.author = req.body.data.author || post.author;
      post.date = req.body.data.date || post.date;
      post.isPublished = req.body.data.isPublished || post.isPublished;

      post.save()
        .then(function (updatedPost) {
          return res.status(200).json({
            status: 200,
            data: updatedPost,
            message: 'Success'
          });
        })
        .catch(function (err) {
          return res.status(400).json({
            status: 400,
            message: err.message
          });
        });
    })
    .catch(function (err) {
      return res.status(400).json({
        status: 400,
        message: err.message
      });
    });
}

exports.delete = function (req, res) {
  Post.findById(req.params.id)
    .then(function (post) {
      post.remove()
        .then(function (deletedPost) {
          return res.status(200).json({
            status: 200,
            data: deletedPost,
            message: 'Success'
          });
        })
        .catch(function (err) {
          return res.status(400).json({
            status: 400,
            message: err.message
          });
        });
    })
    .catch(function (err) {
      return res.status(400).json({
        status: 400,
        message: err.message
      });
    });
}

A quick explanation of controller’s actions:

  • get finds one post by the ID and returns it
  • list returns all the posts which are in the table
  • update finds the posts by the ID and updates its properties
  • delete deletes the post

We have to update our route now:

const express = require('express');
const PostCtrl = require('../controllers/post.controller');

const router = express.Router();

router.get('/', PostCtrl.list);
router.get('/:id', PostCtrl.get);
router.post('/', PostCtrl.create);
router.put('/:id', PostCtrl.update);
router.delete('/:id', PostCtrl.delete);

module.exports = router;

At this point we have completed our posts API. We can see that if we add the ID to the route, we can get a single post:

Get single post in Insomnia. JSON with a response with body reading Other programmers hate him! Learn this one simple trick

Go ahead and check if all other requests work as well. It they do - congratulations! If they don’t - you can check the repo here.

That’s it for the first part of the tutorial! As you can see, setting up a project and developing an API on MEAN stack is very straightforward and quick. You have probably noticed that we still have to show our posts in the frontend part of the app. We will take care of it in the next part of the tutorial. So stay tuned!

FacebookTwitterPinterest

Konrad Jaguszewski

Front-end Developer

I'm a self-taught web developer. Since childhood I was amazed by computers. I love creating and learning new stuff, be that programming, cooking, playing guitar or doing crafts.