Create AngularJS RESTful HTTP Heroes API with Node.js and Express.js

Shahriar Shovon

Supported OS

  • All

Categories

Share this post with your friends

In this post, I will create the heroes HTTP API provided by AngularJS tutorial on AngularJS official documentation. They used InMemoryWebApi to simulate these API calls. So I thought of implementing it just for fun. Let's get started with the API.

API Endpoints

In this API project, I will have the following API endpoints. GET /api/heroes Returns all the heroes data GET /api/hero/:id Returns hero that has an id of :id PUT /api/heroes Creates a new hero POST /api/hero/:id Update an existing hero DELETE /api/hero/:id Delete a hero that has an id of :id

Initialization

First create a directory for the API. I am gonna call it heroes Then, we have to create a package.json file. Open your terminal and create one with the following command. npm init It should ask you some questions, Fill in the details any way you like. If you're lazy to do so, you can use mine package.json file. { "name": "angularjs-hero-service", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "node server.js" }, "author": "Shahriar Shovon", "license": "ISC", "dependencies": { "body-parser": "^1.17.2", "express": "^4.15.4" } }

Installing the Dependencies

For this project, we are going to use express and body-parser packages of node.js We can install express and body-parser with the following command. npm install --save express npm install --save body-parser If you're using my package.json file, then you can run npm install to install the requires packages.

Creating the Folder Structure

I want to make my app modular, so I created a routes folder in my project root. I also created a models directory. In this directory, I will store all the files related to handle the data requests. I used an array based implementation in this post. but this app can be easily configured to work with mongodb, mysql, sqlite or any other database. All you have to do is write another model file similar to the one I am creating with same function name. Then only changing the module I loaded with your own will leave the app working as expected. No other changes required. I stored the heroes array in the data directory. So my current folder structure is - heroes/ - data/ - models/ - routes/ - package.json

Adding the Data

As mentioned earlier, in this project, I will use an array to store the heroes data. So create a file heroes.js in data/ directory and add the following code. let HEROES = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, { id: 13, name: 'Bombasto' }, { id: 14, name: 'Celeritas' }, { id: 15, name: 'Magneta' }, { id: 16, name: 'RubberMan' }, { id: 17, name: 'Dynama' }, { id: 18, name: 'Dr IQ' }, { id: 19, name: 'Magma' }, { id: 20, name: 'Tornado' } ]; module.exports = HEROES; These are the hero data directly copied from AngularJS: Master/Detail. This file exports all the hero data as an array of objects. All we have to do is import it in our Hero model.

Creating the Hero Model

Now I am gonna create Hero.js file in models/ directory. Add the following code to this file and I will explain what I did afterward. let heroes = require('../data/heroes'); let Hero = module.exports; Hero.getHeroes = () => { return heroes; }; Hero.getHero = (id) => { let hero = heroes.filter(h => h.id === parseInt(id)); return hero.length === 0 ? {msg: 'hero not found'} : hero[0]; }; Hero.findIndexById = (id) => { let heroIndex = -1; let heroId = parseInt(id); heroes.filter((h, i) => { if(h.id === heroId) { heroIndex = i; return true; } return false; }); return heroIndex; } Hero.create = (name) => { let hero = { id: heroes[heroes.length - 1].id + 1, name: name }; heroes.push(hero); return hero; } Hero.update = (id, name) => { let heroId = parseInt(id); let heroIndex = Hero.findIndexById(heroId); if(heroIndex !== -1) { heroes[heroIndex].name = name; return heroes[heroIndex]; } else { return {msg: 'hero not found'}; } } Hero.delete = (id) => { let heroIndex = Hero.findIndexById(id); if(heroIndex > -1) { heroes.splice(heroIndex, 1); return {msg: 'success'}; } else { return {msg: 'hero not found'}; } }; In this file I have defined a method for each API operation. I added a helper method findIndexById() which takes an id as input and returns the index number of the hero in the heroes Array. getHeroes() simply returns all the heroes in the heroes Array. getHero() returns the hero with specific id. Note that, I used filter method of heroes to find the hero we are looking for. create() uses push() method of Array to push a new hero to the heroes Array. update() uses findIndexById() to find the index of the heroes Array, and updates the array at that index. delete() works the same way as update. Instead of updating the document, delete() uses the splice() Array method to remove the element from the Array.

Creating the App Sever

Now I am gonna create a server.js file on my project root directory heroes/ This file will load everything, define the API routes/endpoints and start the server. let express = require('express'); let bodyParser = require('body-parser'); let app = express(); let heroesRoutes = require('./routes/heroes'); app.use(bodyParser.urlencoded({extended: false})); app.use(heroesRoutes); app.listen(4242, () => console.log('server running on port 4242')); server.js imports express and body-parser and initializes express with body-parser with app.use(bodyParser.urlencoded({extended: false})) body-parser is used so that we can parse the HTTP GET/POST/PUT/DELETE data from express I also did one more thing here. I wrote the API routes in a different file routes/heroes.js, so I can keep everything nice and tidy in a modular way. Then I just started the server on port 4242 with app.listen() method.

Defining the Routes

Everything is complete. Now all that's left is, the API routes definition. It's really self explanatory, so I am not gonna talk about it. The contents of the routes/heroes.js is given bellow. let express = require('express'); let router = express.Router(); let hero = require('../models/Hero'); router.get('/api/heroes', (req, res) => { res.json(hero.getHeroes()); }); router.get('/api/hero/:id', (req, res) => { res.json(hero.getHero(req.params.id)); }); router.put('/api/heroes', (req, res) => { res.json(hero.create(req.body.name)); }); router.post('/api/hero/:id', (req, res) => { res.json(hero.update(req.params.id, req.body.name)); }); router.delete('/api/hero/:id', (req, res) => { res.json(hero.delete(req.params.id)); }); module.exports = router;

Final Project Structure

The final project structure looks like this - heroes/ - data/ - heroes.js - models/ - Hero.js - routes/ - heroes.js - package.json

Starting the API server

Everything is complete at this point. Now we can start our server and start testing the API. To start the server, simply run npm test if you've used my package.json file or run node server.js if you're using your own and haven't configured anything in your package.json file.

Testing the API

You can use curl command line utility or Postman or anything to test these API calls. I am gonna use curl because it's easier to demonstrate without any illustration.

Testing GET /api/heroes

Get all heroes data curl http://localhost:4242/api/heroes [{"id":11,"name":"Mr. Nice"},{"id":12,"name":"Narco"},{"id":13,"name":"Bombasto"},{"id":14,"name":"Celeritas"},{"id":15,"name":"Magneta"},{"id":16,"name":" RubberMan"},{"id":17,"name":"Dynama"},{"id":18,"name":"Dr IQ"},{"id":19,"name":"Magma"},{"id":20,"name":"Tornado"}]

Testing GET /api/hero/:id

Get hero with id 12 curl http://localhost:4242/api/hero/12 {"id":12,"name":"Narco"} Get hero with id 15 curl http://localhost:4242/api/hero/15 {"id":15,"name":"Magneta"}

Testing PUT /api/heroes

Let's create a new hero Pika curl -X PUT -d 'name=Pika' http://localhost:4242/api/heroes {"id":21,"name":"Pika"} curl http://localhost:4242/api/heroes [{"id":11,"name":"Mr. Nice"},{"id":12,"name":"Narco"},{"id":13,"name":"Bombasto"},{"id":14,"name":"Celeritas"},{"id":15,"name":"Magneta"},{"id":16,"name":" RubberMan"},{"id":17,"name":"Dynama"},{"id":18,"name":"Dr IQ"},{"id":19,"name":"Magma"},{"id":20,"name":"Tornado"},{"id":21,"name":"Pika"}] You can see that the new hero Pika is added to the list.

Testing POST /api/hero/:id

Let's update our newly added hero Pika to Pikachu curl -X POST -d 'name=Pikachu' http://localhost:4242/api/hero/21 {"id":21,"name":"Pikachu"} curl http://localhost:4242/api/heroes [{"id":11,"name":"Mr. Nice"},{"id":12,"name":"Narco"},{"id":13,"name":"Bombasto"},{"id":14,"name":"Celeritas"},{"id":15,"name":"Magneta"},{"id":16,"name":" RubberMan"},{"id":17,"name":"Dynama"},{"id":18,"name":"Dr IQ"},{"id":19,"name":"Magma"},{"id":20,"name":"Tornado"},{"id":21,"name":"Pikachu"}] You can see that the hero with id 21 is updated.

Testing DELETE /api/hero/:id

Let's delete our newly created hero Pikachu with id 21 curl -X DELETE http://localhost:4242/api/hero/21 {"msg":"success"} curl http://localhost:4242/api/heroes [{"id":11,"name":"Mr. Nice"},{"id":12,"name":"Narco"},{"id":13,"name":"Bombasto"},{"id":14,"name":"Celeritas"},{"id":15,"name":"Magneta"},{"id":16,"name":" RubberMan"},{"id":17,"name":"Dynama"},{"id":18,"name":"Dr IQ"},{"id":19,"name":"Magma"},{"id":20,"name":"Tornado"}] You can see that Pikachu is not in the list anymore. So that's it for this post. Thank you for visiting.