Understanding a RESTful API using Node.js, TypeScript and Pokemon

August 22, 2018 0 Comments

Understanding a RESTful API using Node.js, TypeScript and Pokemon

 

 

In this post we will learn to make a simple REST API with a .json file as database. Most of the tutorials out there teach you to build a TODO app using Node.js, Express.js with JavaScript. To learn something new, we will use TypeScript and make a pokemon API to read data from json file.

Instead of just saying: do this, do that and voila it’s done, let’s take it this way: why do this? what is that for? So hopefully this should be a beginner friendly tutorial. After reading this post you should be able to understand and make a simple API.

Before starting with the tutorial, let’s get some concepts right.

If you are new to Node.js, I would recommend that you read my previous blog (24k claps!) on What exactly is Node.js?

TypeScript is a super-set of JavaScript. Most valid JavaScript code can be written in TypeScript file(.ts), because .ts compiles to .js in the end. This explanation is good for a top level view of TS, but understanding all the nuances will require a separate post. This stack-overflow question has some great answers for TS.

Express.js is a framework built on top of Node.js to help ease our task. You can setup the routes in Node.js without express.js or use any other framework like Hapi.js. Not using a framework is not a good idea because the code we write may not be optimal, may contain errors, have security issues or simply become unreadable as the application scales. Express has a lot of features, utility methods and middleware to help us create scalable and robust APIs quickly.

TypeScript compiler(tsc) helps to compile .ts to .js file. We need to setup two configuration files: tsconfig.json and package.json.

Make a new directory and a tsconfig.json file.

mkdir pokemon-api
cd pokemon-api/
touch tsconfig.json

This is a minimal setup, more details of all the available options can be found here.

The JavaScript language didn’t have a native way of organizing code before the ES2015(ES6) standard. So Node.js picked up a module system or community driven convention called CommonJs for organizing code that’s split into different files/modules.
npm init -y
npm install typescript ts-node nodemon --save-dev
  • Just like runningnpm install looks for details in package.json, running tsc looks in tsconfig.json.
  • After compiling the .ts files, all the transpiled JavaScript files are placed in the same directory as their corresponding .ts file. To change this behaviour we have included the option of “outDir” in our tsconfig.json file.
  • ts-node also does the work of tsc. But instead of loading from tsconfig.json file, it starts from the input file and discovers the rest of the project tree through imports and references.
  • Similarly, you can run your .js file(after compiling with tsc or ts-node) with node foo.js command but with every change you need to restart the server. nodemon helps in this process by detecting file changes and restarting server.
"scripts": {
"build": "tsc",
"dev": "ts-node ./src/server.ts",
"start": "nodemon ./dist/server.js",
"prod": "npm run build && npm run start"
}

To justify using TypeScript and get auto-completion we need to have the types definition of the 3rd party modules we use. This is the file/package from which TS understands the type of var/const and your editor suggests intelligent snippets.

npm install express body-parser --save
npm install @types/node @types/express @types/body-parser --save-dev
mkdir src
touch src/app.ts src/server.ts
import app from "./app";
const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
console.log('listening on port ' + PORT);
})

Command npm run dev will help you check if everything is working.

Although this is a small project but the aim is to prepare for the bigger one. So we will structure properly. All routes should go in one folder and all routes related to each other should go in one file. The general structure should look like this:

src/
|--dist/
|--server.ts
|--app.ts
|--routes/
| |--trainers.ts
| |--pokemons.ts
|--models/
|--tests/

Routes for performing CRUD operations on trainers should be inside the trainers.ts file and similarly for pokemons.

In larger applications, you may define a model/schema for your database which should go into the models folder.

Currently we require mkdir src/routes and touch src/routes/pokemons.ts

We could have used any SQL or NoSQL database with this but currently our main aim is to understand the node.js part. Considering the fact that NoSQL like MongoDB stores data as documents. We will use a db.json file as our database. Download this file and place in src/db.json

We need to import this route in our app.ts file

// /src/app.ts
import * as express from "express";
import * as bodyParser from "body-parser";
import { Pokemons } from "./routes/pokemons";

class App {

public app: express.Application;
public pokeRoutes: Pokemons = new Pokemons();

constructor() {
this.app = express();
this.config();
this.pokeRoutes.routes(this.app);
}
private config(): void{
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: false }));
}
}

Now you need to configure the endpoints based on your requirements. Look at the following part in pokemons.ts file. Based on this route’s understanding we will configure other RESTful routes of our API.

//to pokemons/ route. this is your endpoint
app.route('/pokemons')
.get((req: Request, res: Response) => {   //get(verb) request  
         //send all pokemons with a 200 success status code.
res.status(200).send(pokemons);
 })
Representational State Transfer (REST) is an architectural style that defines a set of constraints to be used for creating web services. -Wikipedia

Simply put, it is a standard to name your API endpoints. Look at the Path column in the table below and analyse the pattern.

7 RESTful routes

When you want a pokemon with a particular id, your endpoint would look like http://localhost:3000/pokemons/2 . The last part ‘2’ is the id which is variable hence we need to extract this id using request parameters.

app.route('/pokemons/:id')
.get((req:Request, res: Response) => {
let id = req.params.id;
res.status(200).send(pokemons[id]);
})

Create, Update and Delete routes will receive a payload(data) to process and would make more sense with a database connectivity. But you can still do it with the db.json file with Node.js file system(fs) module.

app.route('/pokemons')
.post((req: Request, res: Response) => {
let name = req.body.name; //this requires body-parser package
let attack = req.body.attack;
//logic to store in database
})

Finally, run your server using npm run dev or npm run prod

Download Postman from here. This a very useful tool to test your API before you make a frontend for your app.

The post request requires you to send your data in the body as json data. Select the right verb(GET, POST, UPDATE, etc) from the dropdown.

POST: select raw and JSON(application/json)
GET: Type the URL and click send

This sums up the tutorial. Hence we have learnt how to make a simple RESTful API using TypeScript, Node.js and Express.js.

You can find the complete code repository here.

Next Steps: Learn MongoDB concepts and its connectivity with Node.js. Stay tuned.


Tag cloud