Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Real-World Angular Series, Part 1a: MEAN Setup and Angular Architecture

DZone's Guide to

Real-World Angular Series, Part 1a: MEAN Setup and Angular Architecture

In the first part of our series, we'll show how to get your environment up and running, using front-end technologies such as Node.js.

· Web Dev Zone ·
Free Resource

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

Introduction: What We'll Build

This tutorial series will teach you how to build a real-world MEAN stack application, covering everything from ideation and data modeling to production deployment.

We will not be building your run-of-the-mill to-do app. In order to learn the ins and outs of production-level JavaScript web application development, we'll build an app to create and RSVP to events. With our RSVP app, an administrator will be able to create, update, and delete events. Other users will then be able to RSVP to events. Our RSVP app's features will include the following (and more):

  • Authentication and Role Authorization (client and server).
  • CRUD operations with an API.
  • Searching and filtering.
  • Template-driven forms.
  • Reactive forms with custom validation.
  • Simple UI animation.
  • Lazy loading.
  • Production deployment on VPS with NGINX and SSL.

We'll do all of the above (including SSL and production deployment) at minimal cost to the developer. There's no reason you shouldn't be able to get your own apps launched to production at low-to-no cost!

To explore the final product, check out the deployed, completed app here. In your own app, you'll be the administrator and will be able to create and modify events yourself (not just RSVP to existing events).

Let's begin!

Angular App Setup

We'll use the Angular CLI to create and build out our Angular application. Make sure you have the CLI installed globally:

$ npm install -g @angular/cli

The other thing you'll likely want is an Angular Language Service extension for your editor or IDE of choice. The Angular Language Service provides Angular intellisense and autocompletion. To learn more about the Language Service, check out this summary of Day 3 of ng-conf 2017. Check your editor's extension database for "Angular Language Service" and install if found (instructions for VS Code, WebStorm, and Sublime Text are available here).

Create an Angular Project

Once the CLI and Language Service are installed, open a terminal or command prompt at the location you'd like your project folder created and run the following command:

$ ng new mean-rsvp --routing --style scss

This creates and installs a new Angular project with a routing module and SCSS support.

Once the Angular CLI has finished creating the app and installing its dependencies, we're ready to get going on the basic customization necessary for our RSVP app.

Add Title Service

In order to change the page titles for our routes, we'll need to use Angular's Title service. This is because the <title> tag lives outside our Angular application, so we can't simply access it and add a property to change it with data binding.

Open the app.module.ts file and add the Title service to imports and to the providers array like so:

// src/app/app.module.ts
import { BrowserModule, Title } from '@angular/platform-browser';
...
@NgModule({
  ...,
  providers: [
    Title
  ],
  ...
})

Add Bootstrap

Now open the src/index.html file and add a link to the Bootstrap CSS on CDN like so:

<!-- src/index.html -->
...
<head>
  ...
  <title>RSVP</title>
  ...
  <link
    rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
    integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"
    crossorigin="anonymous">
</head>
...

Note: At the time of writing, we're using the v4 alpha version of Bootstrap to take advantage of newer features such as the flex grid. As with any pre-release tool, there may be bugs. If a later release is available, please upgrade. If you choose to downgrade to v3, be aware that many of the classes in the provided HTML templates will not work and will need to be replaced.

Global SCSS

We'll now add some SCSS to manage global styling for our application. This will support basic layout and media queries.

First, open your src/assets folder and an scss folder. Now find the src/styles.scss file and move it into src/assets/scss.

Next, open the .angular-cli.json file. Find the styles property and change it to the following:

...
      "styles": [
        "assets/scss/styles.scss"
      ],
...

Now our project will look for this file in the assets folder rather than in the root.

Note: If you have the Angular CLI server running, you'll need to stop and restart it after making changes to the .angular-cli.jsonconfiguration file.

Next, let's create a few SCSS files.

Base Styles

First, we'll create our _base.scss styles. Create this file and add the following SCSS to it:

/* src/assets/scss/_base.scss */
/*--------------------
       BASICS
--------------------*/

body {
  min-width: 320px;
}

/*-- Cursor --*/

a,
input[type=button],
input[type=submit],
button {
  cursor: pointer;
}

/*-- Link Buttons --*/

.btn-link {
  color: #0275d8;

  &:hover {
    text-decoration: underline !important;
  }
  &:focus {
    box-shadow: none !important;
    color: #0275d8;
    text-decoration: none;
  }
}

/*-- Forms --*/

input[type="text"],
input[type="number"],
input[type="password"],
input[type="date"],
select option,
textarea {
  font-size: 16px; /* for iOS to prevent autozoom */
}
.formErrors {
  padding-top: 6px;
}
.ng-invalid.ng-dirty,
.ng-invalid.ng-dirty:focus {
  border-color: #D9534E !important;
}
input::-webkit-input-placeholder { /* Chrome/Opera/Safari */
  color: rgba(0,0,0,.25) !important;
  opacity: 1 !important;
}
input::-moz-placeholder { /* Firefox 19+ */
  color: rgba(0,0,0,.25) !important;
  opacity: 1 !important;
}
input:-moz-placeholder { /* Firefox 18- */
  color: rgba(0,0,0,.25) !important;
  opacity: 1 !important;
}
input:-ms-input-placeholder { /* IE 10+ */
  color: rgba(0,0,0,.25) !important;
  opacity: 1 !important;
}

/*-- Helpers --*/

.opacity-half {
  opacity: .5;
}

Bootstrap provides a CSS reset and plenty of styling. Our local _base.scssprovides some basic helpers and improvements.

Variables and Partials

Now we'll add a few files for variables and partials. Create a new folder in your src/assets/scss directory called partials.

In the partials folder, add a new file called _layout.vars.scss and add the following:

/* src/assets/scss/partials/_layout.vars.scss */
/*--------------------
   LAYOUT VARIABLES
--------------------*/

$padding-screen-small: 3%;
$padding-screen-large: 1.5% 3%;

In the same partials folder, create a file called _responsive.partial.scss:

/* src/assets/scss/partials/_responsive.partial.scss */
/*--------------------
      RESPONSIVE
--------------------*/

/*-- Variables --*/

$large: 'screen and (min-width: 768px)';

/*-- Mixins --*/

@mixin mq($mqString) {
  @media #{$mqString} {
    @content;
  }
}

This file contains a $large variable with a media query for large screen sizes and an mq() mixin for easily targeting media queries in our SCSS. If necessary, we can add more variables to this file as our app grows.

Import Global SCSS

Finally, we need to import all the files we just created. Open the styles.scssfile and add:

/* src/assets/scss/styles.scss */
// partials
@import 'partials/layout.vars';
@import 'partials/responsive.partial';

// global styles
@import 'base';

Now our Angular app has everything we need to get started developing features. However, before we move forward with any more client side development, let's set up MongoDB and our Node.js API!

Hosted MongoDB Setup

MongoDB is an open-source document database. For speed and ease, we'll use mLab's free, cloud-hosted MongoDB deployment for our application's database. We'll also set up a handy tool called MongoBooster for simple MongoDB access and management.

Create m Account and Database

Let's sign up and create our new database.

  1. Go to mLab and sign up for an account.
  2. Confirm your email address. Doing so will take you to your account dashboard.
  3. Under MongoDB Deployments, click the "Create new" button.
  4. Select your desired Cloud Provider and Region.
  5. Change the Plan to Single-node and select the free "Sandbox" option.
  6. Scroll down and give your database a name, like mean.
  7. Click the "Create new MongoDB deployment" button.

Image title


The new database can now be selected from our deployments. It should look something like this:

mLab MongoDB database

We now need to add a user in order to connect to our database. Click on the database to edit it.

  1. Select the Users tab and click the "Add database user" button.
  2. Enter a database username and password in the modal. These credentials will be needed to read and write to the database with Node.
  3. Make a note of the database's MongoDB URI. This should be in the format: mongodb://<dbuser>:<dbpassword>@<ds111111>.mlab.com:<port>/<dbname>.

mLab MongoDB URI

Now we're ready to use our MongoDB database.

Note: If you prefer, you can host MongoDB locally. Follow these instructions to install MongoDB on your operating system.

Set Up MongoBooster

For easy database management, we can use an app called MongoBooster. The free version will serve our purposes just fine. Download MongoBooster for your OS and open it.

  1. In the upper left of the Connections prompt, click "Create" to set up a new connection.
  2. A Connection Editor dialog will appear with the Basic tab open.
  3. Enter the server address. This will be your MongoDB URI that we made note of earlier, something like: <ds111111>.mlab.com.
  4. Enter the port number in the field after the :.
  5. Enter a name for the connection, such as mLab - mean.

Set up MongoBooster with MongoDB connection from mLab

Next, switch to the Authentication tab.

  1. For Mode, select "Basic(Username/Password)" from the dropdown.
  2. Enter your mLab database name, like mean.
  3. Enter the username for the user you created in mLab.
  4. Enter the password you created for the user in mLab.

Set up MongoBooster with MongoDB authentication

We should now be able to click the "Test Connection" button to confirm our configuration. If everything was entered correctly, we should receive a Test Connection dialog that confirms we were able to connect and authorize with a status of OK.

Note: If you encounter a failure, double-check the information you entered.

Close the test dialog and click the "Save" button. We can now select and connect to our mLab mean database with MongoBooster.

Auth0 Account and Setup

Our Angular application and Node API will use the IDaaS (Identity-as-a-Service) platform Auth0 for authentication and route authorization.

Auth0 hosted login screen

Sign Up for a Free Auth0 Account

You'll need an Auth0 account to manage authentication. You can sign up for free. Next, set up an Auth0 client app and API so Auth0 can interface with an Angular app and Node API.

Set Up a Client App

  1. Go to your Auth0 Dashboard and click the "create a new client" button.
  2. Give your new app a name (for example: RSVP MEAN App) and select "Single Page Web Applications."
  3. In the Settings for your new Auth0 client app, add http://localhost:8083/callback and http://localhost:4200/callback to the Allowed Callback URLs.
  4. In Allowed Origins (CORS), add http://localhost:8083 and http://localhost:4200.
  5. Scroll down to the bottom of the Settings section and click "Show Advanced Settings." Choose the OAuth tab and change the JSON WebToken Signature Algorithm to "RS256."
  6. If you'd like, you can set up some social connections. You can then enable them for your app in the Client options under the Connections tab. The example shown in the screenshot above utilizes username/password database, Facebook, Google, and Twitter.

We added two ports to the callback URLs and allowed origins because we'll be running and testing the app from both during development. Port 4200 is the port the Angular CLI serves the Angular app from. Port 8083 is the port our Node API and server use: this will be necessary in order to test the production build. When we launch to a production URL, we can either create a new production Auth0 Client or add our production URL to the client as well.

Note: If you set up social connections, enter App/Client IDs as per the instructions for each connection instead of leaving those fields blank and using Auth0 dev keys. This will be important for token renewal and deployment later.

Set Up an API

  1. Go to APIs in your dashboard and click the "Create API" button.
  2. Enter a name for the API (for example: RSVP MEAN API).
  3. Set the Identifier to your API endpoint URL. This identifier is the audience parameter on authorization calls. In our app, this is http://localhost:8083/api/.
  4. The Signing Algorithm should be "RS256."

Node.js Server Setup

Our next order of business is the Node server and API.

Install Dependencies

In the root folder of our new Angular app, install the following dependencies with npm, saving them to the package.json like so:

$ npm install --save express body-parser express-jwt jwks-rsa method-override mongoose cors --save

File Structure

Create a new folder at the root of the project called server. Inside this folder, add two new files: server/api.js and server/config.js. At the root of the project, create a server.js file. The file structure should now look like this:

...
server/
 |- api.js
 |- config.js
src/
...
server.js
...

Configuration

Open the server/config.js file and add the following to it:

// server/config.js
module.exports = {
  AUTH0_DOMAIN: '[YOUR_AUTH0_DOMAIN].auth0.com',
  AUTH0_API_AUDIENCE: '[YOUR_AUTH0_API_NAME]', /* likely 'http://localhost:8083/api/' */
  MONGO_URI: process.env.MONGO_URI || 'mongodb://[USER]:[PASSWORD]@[DS######].mlab.com:[PORT]/[DB_NAME]'
};

Replace the Auth0 domain, API audience, and MongoDB URI with the appropriate data from your Auth0 account and mLab database.

Server

Now let's develop our server.js. Open the empty file we created and add the following to it:

// server.js
/*
 |--------------------------------------
 | Dependencies
 |--------------------------------------
 */

// Modules
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const methodOverride = require('method-override');
const cors = require('cors');
// Config
const config = require('./server/config');

/*
 |--------------------------------------
 | MongoDB
 |--------------------------------------
 */

mongoose.connect(config.MONGO_URI);
const monDb = mongoose.connection;

monDb.on('error', function() {
  console.error('MongoDB Connection Error. Please make sure that', config.MONGO_URI, 'is running.');
});

monDb.once('open', function callback() {
  console.info('Connected to MongoDB:', config.MONGO_URI);
});

/*
 |--------------------------------------
 | App
 |--------------------------------------
 */

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(methodOverride('X-HTTP-Method-Override'));
app.use(cors());

// Set port
const port = process.env.PORT || '8083';
app.set('port', port);

// Set static path to Angular app in dist
// Don't run in dev
if (process.env.NODE_ENV !== 'dev') {
  app.use('/', express.static(path.join(__dirname, './dist')));
}

/*
 |--------------------------------------
 | Routes
 |--------------------------------------
 */

require('./server/api')(app, config);

// Pass routing to Angular app
// Don't run in dev
if (process.env.NODE_ENV !== 'dev') {
  app.get('*', function(req, res) {
    res.sendFile(path.join(__dirname, '/dist/index.html'));
  });
}

/*
 |--------------------------------------
 | Server
 |--------------------------------------
 */

app.listen(port, () => console.log(`Server running on localhost:${port}`));

Notice that there are a few sections that are environment-dependent. For development, we want to be able to take advantage of the Angular CLI's ability to serve and watch files with JIT without requiring an entire project build each time we want to check our work. In order to facilitate this, we'll start by separating our Node.js server from our Angular front-end in development.

This way, we can run the Node API on localhost:8083 while the Angular app runs on localhost:4200. For production, we want the Node server to run the API and use a static path to serve the front-end. Our MEAN stack should pass routing to the compiled Angular app for deployment.

API Routes

Open the api.js file and add the following:

// server/api.js
/*
 |--------------------------------------
 | Dependencies
 |--------------------------------------
 */

const jwt = require('express-jwt');
const jwks = require('jwks-rsa');

/*
 |--------------------------------------
 | Authentication Middleware
 |--------------------------------------
 */

module.exports = function(app, config) {
  // Authentication middleware
  const jwtCheck = jwt({
    secret: jwks.expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksRequestsPerMinute: 5,
      jwksUri: `https://${config.AUTH0_DOMAIN}/.well-known/jwks.json`
    }),
    aud: config.AUTH0_API_AUDIENCE,
    issuer: `https://${config.AUTH0_DOMAIN}/`,
    algorithm: 'RS256'
  });

/*
 |--------------------------------------
 | API Routes
 |--------------------------------------
 */

  // GET API root
  app.get('/api/', (req, res) => {
    res.send('API works');
  });

};

Using express-jwt and jwks-rsa with our Auth0 API, we can implement protection for selected API routes when necessary. We'll do this by adding a jwtCheck middleware function to routes we want to secure. We'll discuss this in futher detail later.

Serve and Watch the App and API

Let's install nodemon globally to watch the Node server for changes without needing to restart after making updates:

$ npm install nodemon -g

Note: If you encounter EACCESS errors when using npm, you may need to execute some commands either with the command prompt running as administrator (Windows) or with sudo (Mac/Linux).

We should now be able to access both the Angular application and the API in the browser on localhost. We can do so by running each of the following commands from the root of our project in separate terminal windows (VS Code is great at this).

Note: We'll be using separate terminal windows a lot so we can keep watching the app and API while adding components with the Angular CLI.

  • Serve Angular app: ng serve
  • Serve Node API: NODE_ENV=dev nodemon server

If we've done everything correctly, the Angular app will compile and show a success message in its terminal. We should also see a message in the Node server terminal confirming that the server is running and that we've successfully connected to MongoDB.

We can then navigate to http://localhost:4200 to see our Angular app running in the browser:

Angular app works running on localhost on Node backend

We can also navigate to http://localhost:8083/api to see our API running:

Node API works running on localhost

Note: Eventually we'll be serving everything from the Node server, but since this requires the Angular app to be built and no longer watched, we won't do this until later. However, if you'd like to see what this looks like now, you can run ng build followed by node server. This will build and serve the app and API on http://localhost:8083.

Our Node API and Angular app are now up and running in a development environment!

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

Topics:
web dev ,angular ,web application development ,nodejs

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}