Upcoming Course: Code Your Own Business w/ React + GraphQL!
We're live-coding on Twitch! Join us!
Learn to build a MEAN stack app that displays a list of Top Nigerian Developers

Learn to build a MEAN stack app that displays a list of Top Nigerian Developers

Code Demo

MEAN is a collection of JavaScript-based technologies — MongoDB, Express.js, AngularJS, and Node.js —  used to develop web applications. From the client and server sides to databases, MEAN is a full-stack development toolkit.

Let’s explain the different parts:

  • MongoDB usually acts as the database for your application, in case you need to persist data. It’s where we store records (won’t be used in this app anyway).
  • ExpressJS is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
  • Angular is usually the client side MVC web framework. In this case, we will be using Angular 2.
  • NodeJS powers express, and will be the layer our server run on.

In this article, we will develop an Angular app that shows a list of top Nigerian developers, using the Github API. Preview available at top-nigerian-devs.herokuapp.com.

Prerequisites

We need to install Angular CLI.

$ npm install -g angular-cli

This installs angular cli globally.

Create an Angular app

Next is to create an Angular app using the Angular CLI.

$ ng new top-nigerian-devs

This creates a new directory and an Angular app with all the necessary dependencies installed.

To view/serve the app, simply run

$ ng serve

and open https://locahost:4200 in your browser.

You should see this in your browser:

Adding Express

Remember we said (above) that express provides a robust set of features for web and mobile applications, one of them is Routing. Angular CLI comes with a command ng build, which bundles your angular app into a dist folder, or a folder that you may specify in the angular-cli.json file. This is what our express app will point to.

Install express and body-parser as dependecies.

$ npm install --save express body-parser

Then create a file server.js and a folder server in the root of our angular project. The server.js file will have the server code, that will point to the server folder, where the rest of the server implementation is.

server.js

// Get dependencies
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');

// Get our API routes
const api = require('./server/routes/api');

const app = express();

// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

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

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

// Catch all other routes and return the index file
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist/index.html'));
});

/**
 * Get port from environment and store in Express.
 */
const port = process.env.PORT || '3000';
app.set('port', port);

/**
 * Create HTTP server.
 */
const server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */
server.listen(port, () => console.log(`API running on localhost:${port}`));

The above code sets up a simple express app, with an /api route and all other routes are directed towards the dist/index.html page. This catch all route, denoted with *, MUST come last after all other API routes have been defined.

The /api route points to a file ./server/routes/api.js. Let's create this file.

server/routes/api.js

const express = require('express');
const router = express.Router();

/* GET api listing. */
router.get('/', (req, res) => {
  res.send('api works');
});

module.exports = router;

One last thing before we run this. Since the catch all route is pointing to dist/index.html, we need to do a build of the angular app.

$ ng build

This creates the dist folder with the angular 2 app built files. Now we can serve the app with express.

$ node server.js

Going to http://localhost:3000 should load the app, and http://localhost:3000/api should load the api as shown below. angular app(http://localhost:3000) express app(http://localhost:3000/api)

Server call

Now that we have the api set up. We can define routes to make calls to Github API. First add axios for making http requests.

$ npm install --save axios

Then, update the api.js file to have the following content.

server/routes/api.js

const express = require('express');
const router = express.Router();

// declare axios for making http requests
const axios = require('axios');
const API = 'https://api.github.com';


/* GET api listing. */
router.get('/', (req, res) => {
    res.send('api works');
});

// Get all devs
router.get('/devs/:language', (req, res) => {
    // Get developers in Lagos, Nigeria using their programming language
    const lang=req.params.language;

    axios.get(`${API}/search/users?q=type:user+location:lagos+language:${lang}`)
        .then(devs => {
            res.status(200).json(devs.data.items);
        })
        .catch(error => {
            res.status(500).send(error)
        });
});

module.exports = router;

If we now stop the server and run it again, node server.js, we should see json data when we go to http://localhost:3000/api/devs/java I use a json-viewer chrome plugin. You may see a not so pretty json response.

Angular route, component and service

We'll add an angular component, then add a route that display this component's template.

Add an angular component with the Angular CLI

$ ng generate component intro

The above command adds a new folder in the src/app directory, called intro and also imports the generated IntroComponent in the src/app/app.module.ts file, and adds it to the declarations property of the @NgModule decorator.

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { IntroComponent } from './intro/intro.component';

@NgModule({
  declarations: [
    AppComponent,
    IntroComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Next we'll add an intro route.

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { IntroComponent } from './intro/intro.component';

// Define the routes
const ROUTES = [
  {
    path: '',
    component: IntroComponent
  }
];

@NgModule({
  declarations: [
    AppComponent,
    IntroComponent
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot(ROUTES)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

We are simply telling our router that whenever the root route / is visited, the IntroComponent is used which displays an Intro screen.

One final thing to complete our routing is to first make sure that we have a <base href="/"> in the src/index.html head tag. Then add the following links to src/index.html and scripts to enable BootStrap 4 usage:

<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">
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>

And then add a router-outlet where the route should be rendered. We'll add this in the src/app/app.component.html and replace the rest of the code with the code below.

<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse sticky-top">
    <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <a class="navbar-brand" href="/">Top Nigerian Developers</a>
    <div class="collapse navbar-collapse" id="navbarNavDropdown">
        <ul class="navbar-nav">
            <li class="nav-item active">
                <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item dropdown">
                <a class="nav-link dropdown-toggle" href="http://example.com" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                    Language
                </a>
                <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
                    <a class="dropdown-item" [routerLink]="['/devs', 'java']">Java</a>
                    <a class="dropdown-item" [routerLink]="['/devs', 'python']">Python</a>
                    <a class="dropdown-item" [routerLink]="['/devs', 'javascript']">JavaScript</a>
                    <a class="dropdown-item" [routerLink]="['/devs', 'php']">PHP</a>
                    <a class="dropdown-item" [routerLink]="['/devs', 'go']">GO</a>
                    <a class="dropdown-item" [routerLink]="['/devs', 'C++']">C++</a>
                </div>
            </li>
        </ul>
    </div>
</nav>
<router-outlet></router-outlet>

We need to do a build and serve the app, we could do

$ ng build && node server.js

Or just create an npm script within the package.json.

package.json

{
  "name": "top-nigerian-devs",
  // meta data
  "scripts": {
    // Other scripts
    "build": "ng build && node server.js"
  },
  "private": true,
  "dependencies": {
    ...
  },
  "devDependencies": {
    ...
  }
}

Then simply run.

$ npm run build

Going to http://localhost:3000, you should see this.

Next we add the content of the intro page as in the src/app/intro/intro.component.html below.

src/app/intro/intro.component.html

<div class="container" style="margin-top: 30px">
  <div class="jumbotron">
    <h1 class="display-3">Top Nigerian Developers</h1>
    <p class="lead">This app shows a list of Top Nigerian Developers based on various Programming languages.</p>
    <hr class="my-4">
    <p>It uses Github API for its references.</p>
    <p class="lead">
      <a class="btn btn-primary btn-lg" href="https://developer.github.com/v3/" target="_blank" role="button">Learn more</a>
    </p>
  </div>
</div>

Stop the server and run it again.

$ npm run build

You should see this.

We've just created the intro route, now let's create the devs component and define routes for it.

Creating the Devs Component

We need to create a new component and define route for it, just as we did for the intro component above.

$ ng generate component devs

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { IntroComponent } from './intro/intro.component';
import { DevsComponent } from './devs/devs.component';

// Define the routes
const ROUTES = [
  {
    path: '',
    component: IntroComponent
  }, 
    {
    path: 'devs/:lang', 
    component: DevsComponent
    }
];

@NgModule({
  declarations: [
    AppComponent,
    IntroComponent,
    DevsComponent
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot(ROUTES)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Here, we are defining a new route /devs/:lang which takes in lang as param and uses the DevsComponent we just created.

Connecting Component to Express API

Angular 2's best practices recommend defining a provider or service to handle the http calls. So, we'll generate one with the Angular CLI.

$ ng generate service devs

This creates a devs.service.ts in the src/app directory. We then need to add it in the providers section of our module declaration. Also add HttpModule to the list of imports in the same src/app/app.module.ts file

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { IntroComponent } from './intro/intro.component';
import { DevsComponent } from './devs/devs.component';
import {HttpModule} from "@angular/http";
import {DevsService} from "./devs.service";

// Define the routes
const ROUTES = [
  {
    path: '',
    component: IntroComponent
  },
    {
    path: 'devs/:lang',
    component: DevsComponent
    }
];

@NgModule({
  declarations: [
    AppComponent,
    IntroComponent,
    DevsComponent
  ],
  imports: [
    BrowserModule,
    HttpModule,
    RouterModule.forRoot(ROUTES)
  ],
  providers: [DevsService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Then make the http call within the service to our express server.

src/app/devs.service.ts

import { Injectable } from '@angular/core';
import {Http} from "@angular/http";
import 'rxjs/add/operator/map';

@Injectable()
export class DevsService {

  constructor(private http: Http) { }

  getDevs(lang: string) {
    return this.http.get('/api/devs/'+lang)
        .map(res => res.json());
  }
}

Then import our service in the devs component.

src/app/devs/devs.component.ts

import {Component, OnInit, OnDestroy} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {Subscription} from "rxjs";
import {DevsService} from "../devs.service";

@Component({
  selector: 'app-devs',
  templateUrl: './devs.component.html',
  styleUrls: ['./devs.component.css']
})
export class DevsComponent implements OnInit, OnDestroy {
  private sub : Subscription;
  private lang : string;
  devs : any = [];

  constructor(private route: ActivatedRoute, private devsService: DevsService) {}

  ngOnInit() {
    this.sub = this.route.params.subscribe(params => {
      this.lang = params['lang']; 

      this.devsService.getDevs(this.lang).subscribe(devs => {
        this.devs = devs;
      });

    });
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }

}

And finally, we'll just display the list of developers in the view.

src/app/devs/devs.component.html

<div class="container">
    <div class="row">
        <div class="card col-md-3 devs" style="margin: 10px 0" *ngFor="let dev of devs">
            <img class="card-img-top" style="height: 280px" src="{{dev.avatar_url}}" alt="Card image cap">
            <div class="card-block">
                <h4 class="card-title">{{dev.login}}</h4>
                <a href="{{dev.html_url}}" target="_blank" class="btn btn-primary">Github Repo</a>
            </div>
        </div>
    </div>
</div>

Here, we are looping through devs coming from the api and binding the avatar_url,login and html_url properties.

Run the app.

$ npm run build

Going to localhost:3000/devs/java, should produce this. You can then toggle between different programming languages to see changes, by selecting from Language dropdown on the navbar.

Conclusion

You've just created an angular 2 app using angular cli, with nodejs express server. You can now continuously build angular 2 apps running on express server.

Checkout/Contribute to the complete code for the app from https://github.com/emmanuelkehinde/top-nigerian-devs.

Cheers!

Like this article? Follow @emmakoko96 on Twitter