Build a Node.js API with TypeScript

Build a Node.js API with TypeScript

In this article, I’ll show you how to write a simple Node.js API for a real-time chat app using TypeScript.

JavaScript has been turning into more and more of a robust language and is no longer just seen in browsers. Server-side JavaScript has become quite relevant. However, one major thing that JavaScript is not, is a strongly typed language. Being explicit with what types to expect from function parameters or object and class properties can help prevent bugs from creeping into the code. It can also help make the code easier to understand if you know exactly what types of values you need to pass into an API call, for example.

If you’re both a fan of JavaScript and strongly typed languages, then you’re in luck. TypeScript is a superset of JavaScript that provides some extra syntax for defining types. Today I’ll show you how to write a simple API for a real-time chat app using TypeScript. The catch will be messages will expire after a short amount of time and get deleted from both the server and the client, so you have to pay attention if you want to keep up on the conversation. I’ll also show you how to create a simple frontend using React to interface with the API. You’ll also be using Okta to add user authentication so that you don’t just have a bunch of anonymous messages.

Set Up Your TypeScript + Node.js Server

If you don’t have Node installed yet, you’ll need to do that first. That’s the only real prerequisite for this tutorial. To see if you have it installed, try typing node --version in a terminal. You’ll want to get a response back with something like v8 or higher. If you get an error, you may need to install it. I recommend installing via nvm. The install should look something like this:

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash

But if that doesn’t work, head to the docs to see how to get it up and running for your system.

Once you have Node running, create a new directory for your chat server, then use npm to create a package.json file for you:

mkdir real-time-chat
cd real-time-chat
npm init -y

Edit the package.json file so the "main" entry says "dist/index.js". Then edit the "scripts" entry to have the following scripts:

"scripts": {
  "prebuild": "tslint -c tslint.json -p tsconfig.json --fix",
  "build": "tsc",
  "prestart": "npm run build",
  "start": "node .",
  "dev": "ts-node-dev src/index.ts",
  "test": "echo \"Error: no test specified\" && exit 1"
}

You’ll also need to install some dependencies:

npm install [email protected]
npm install --save-dev [email protected] [email protected] [email protected] @types/[email protected] @types/[email protected]

You’ll need to create some configuration files for typescript. Create a tslint.json file:

{
  "defaultSeverity": "error",
  "extends": [
    "tslint:recommended"
  ]
}

and a tsconfig.json file:

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "target": "es6",
    "noImplicitAny": true,
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "dist",
    "baseUrl": ".",
    "paths": {
      "*": [
        "node_modules/*"
      ]
    }
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "src/client"
  ]
}

Now that the TypeScript configuration is ready, create a new folder src and create a new file src/index.ts:

import express from "express";

const app = express();
const port = 8080 || process.env.PORT;

app.get("/", (req, res) => {
  res.send("Hi!");
});

app.listen(port, () => {
  // tslint:disable-next-line:no-console
  console.log(`server started at http://localhost:${port}`);
});

If you run the code now with npm run dev you should get a website up and running at <a href="http://localhost:8080" target="_blank">http://localhost:8080</a> that just says Hi!. Nothing fancy yet.

Set Up a React Frontend for Your Node.js + TypeScript API

Another neat developer tool is Parcel. It provides a really simple way to bundle JavaScript or TypeScript code for the frontend, so you can use the familiar import syntax and install packages using npm, but at the end of the day your client just gets a single bundle pared down to the code they need.

To set this up with React, add the following dependencies:

npm install [email protected] [email protected]
npm install --save-dev [email protected] @types/[email protected] @types/[email protected] @types/[email protected] @babel/[email protected] @babel/[email protected]

Create a new folder for your client-side code in src/client:

mkdir src/client

You’ll need a second configuration file for TypeScript to know that the client should be packaged for the web rather than for Node. Create a new file src/client/tsconfig.json

{
  "compilerOptions": {
    "lib": [
      "es6",
      "dom"
    ],
    "allowSyntheticDefaultImports": true,
    "jsx": "react",
    "module": "es6",
    "target": "es6",
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "../../dist",
    "baseUrl": ".",
    "paths": {
      "*": [
        "../../node_modules/*"
      ]
    }
  },
  "include": [
    "./**/*"
  ]
}

Then create a new file src/client/index.html:

<!doctype html>
<html>
  <head>
    <title>Real-Time Chat</title>
  </head>
  <body>
    <main id="root"></main>
    <script src="./index.tsx"></script>
  </body>
</html>

You’ll then need to create the src/client/index.tsx file mentioned.

import React from "react";
import ReactDOM from "react-dom";

import App from "./App";

ReactDOM.render(
  <App />,
  document.getElementById("root"),
);

As well as src/client/App.tsx:

import React from "react";

export default () => <div>Hello world</div>;

Now that you have your client set up, you need to tell Express how to serve the content using Parcel. Edit your src/index.ts file:

// These should be at the top of the file
import Bundler from "parcel-bundler";
import path from "path";

// replace the call to app.get with:
const bundler = new Bundler(path.join(__dirname, "../src/client/index.html"));
app.use(bundler.middleware());

With those changes, your src/index.ts should now look like this:

import express from "express";
import Bundler from "parcel-bundler";
import path from "path";

const app = express();
const port = 8080 || process.env.PORT;

const bundler = new Bundler(path.join(__dirname, "../src/client/index.html"));
app.use(bundler.middleware());

app.listen(port, () => {
  // tslint:disable-next-line:no-console
  console.log(`server started at http://localhost:${port}`);
});

Send and Receive Messages with Socket.IO

For real-time applications, Socket.IO makes it easy to send messages back and forth between the server and any connected clients. It uses WebSockets to keep a connection open so messages are instant, but it does fall back to polling in cases where WebSockets aren’t available. You’ll need to add a few new dependencies for this:

npm install [email protected] [email protected] [email protected] [email protected]
npm install --save-dev @types/[email protected] @types/[email protected] [email protected]

Set Up the Backend

To set up the backend, create a new file src/socket.ts:

import { Server, Socket } from "socket.io";
import uuid from "uuid/v4";

const messageExpirationTimeMS = 10 * 1000;

export interface IUser {
  id: string;
  name: string;
}

const defaultUser: IUser = {
  id: "anon",
  name: "Anonymous",
};

export interface IMessage {
  user: IUser;
  id: string;
  time: Date;
  value: string;
}

const sendMessage = (socket: Socket | Server) =>
  (message: IMessage) => socket.emit("message", message);

export default (io: Server) => {
  const messages: Set<IMessage> = new Set();

  io.on("connection", (socket) => {
    socket.on("getMessages", () => {
      messages.forEach(sendMessage(socket));
    });

    socket.on("message", (value: string) => {
      const message: IMessage = {
        id: uuid(),
        time: new Date(),
        user: defaultUser,
        value,
      };

      messages.add(message);

      sendMessage(io)(message);

      setTimeout(
        () => {
          messages.delete(message);
          io.emit("deleteMessage", message.id);
        },
        messageExpirationTimeMS,
      );
    });
  });
};

That might be a lot to grok, so I’ll break down the individual pieces.

Here you’re defining types, known as interfaces in TypeScript. You’re also creating a default user for when you don’t have user information (you’ll be adding that later with authentication).

export interface IUser {
  id: string;
  name: string;
}

const defaultUser: IUser = {
  id: "anon",
  name: "Anonymous",
};

export interface IMessage {
  user: IUser;
  id: string;
  time: Date;
  value: string;
}

This next function actually returns another function. This pattern is common in functional programming. It’s not strictly necessary to do it this way, but makes some things a little (subjectively) cleaner later on.

const sendMessage = (socket: Socket | Server) =>
  (message: IMessage) => socket.emit("message", message);

Here you’re exporting a function that takes in a Socket.IO server and adds some listeners. This uses an es6 Set to keep track of the messages. This will make it easy to remove them later without having to search through an array.

When a client connects to the server, it triggers a callback anywhere you’ve added a listener, like in the following with io.on("connection", listener). When the server receives a getMessages signal, it will send all of the current messages to the client. The client will need to send that message after it finishes connecting.

When the client sends a message, that message gets added to the Set of messages with a unique ID, the current timestamp, and for now just the default anonymous user. It then sends that message to all connected clients.

In order to delete the message after a set period of time, a timeout set to 10 seconds (you can tweak this number by editing messageExpirationTimeMS) will remove the message from the Set of messages, and sends a message to all clients to delete the message by ID. It will be up to the client to actually remove the message, but since it’s removed from the Set no new clients will get that message.

export default (io: Server) => {
  const messages: Set<IMessage> = new Set();

  io.on("connection", (socket) => {
    socket.on("getMessages", () => {
      messages.forEach(sendMessage(socket));
    });

    socket.on("message", (value: string) => {
      const message: IMessage = {
        id: uuid(),
        time: new Date(),
        user: defaultUser,
        value,
      };

      messages.add(message);

      sendMessage(io)(message);

      setTimeout(
        () => {
          messages.delete(message);
          io.emit("deleteMessage", message.id);
        },
        messageExpirationTimeMS,
      );
    });
  });
};

You’ll now need to initialize the socket in src/index.ts. Add the following imports:

import http from "http";
import SocketIOServer from "socket.io";

import initializeSocketIO from "./socket";

After creating the app variable, add the following:

const server = new http.Server(app);
const io = SocketIOServer(server);

initializeSocketIO(io);

Then change app.listen to server.listen.

// this was `app.listen`
server.listen(port, () => {
  // tslint:disable-next-line:no-console
  console.log(`server started at http://localhost:${port}`);
});

Your full src/index.ts file should now look something like this:

import express from "express";
import http from "http";
import Bundler from "parcel-bundler";
import path from "path";
import SocketIOServer from "socket.io";

import initializeSocketIO from "./socket";

const app = express();
const server = new http.Server(app);
const io = SocketIOServer(server);
const port = 8080 || process.env.PORT;

const bundler = new Bundler(path.join(__dirname, "../src/client/index.html"));

initializeSocketIO(io);
app.use(bundler.middleware());

server.listen(port, () => {
  // tslint:disable-next-line:no-console
  console.log(`server started at http://localhost:${port}`);
});

Set Up the Frontend

Your server is all ready to go now. Next, you’ll need to create a couple of new components for the client to interface with the socket.

Create a src/client/NewMessage.tsx component that will allow you to send a new message to the server:

import React, { SyntheticEvent, useState } from "react";
import { Socket } from "socket.io";

interface IProps {
  socket: Socket;
}

const NewMessage = ({ socket }: IProps) => {
  const [value, setValue] = useState("");
  const submitForm = (e: SyntheticEvent) => {
    e.preventDefault();
    setValue("");

    socket.emit("message", value);
  };

  return (
    <form onSubmit={submitForm}>
      <input
        autoFocus
        value={value}
        onChange={(e: SyntheticEvent<HTMLInputElement>) => {
          setValue(e.currentTarget.value);
        }}
      />
    </form>
  );
};

export default NewMessage;

This sets up a simple form so that when you hit Enter that will trigger the form submission. You can then hijack that, preventDefault to keep the form from refreshing the page, send the value to the server, and reset the input to an empty string. Some TypeScript types are peppered throughout to make sure you’re getting what you expect.

You’ll also need a way to display the messages. Create a new src/client/MessageList.tsx component:

import { Map } from "immutable";
import React, { SyntheticEvent, useEffect, useState } from "react";

import "./MessageList.scss";

import { IMessage } from "../socket";

const MessageList = ({ socket }) => {
  const [messages, setMessages] = useState(Map());

  useEffect(() => {
    const messageListener = (message: IMessage) => {
      setMessages((prevMessages) => prevMessages.set(message.id, message));
    };

    const deleteMessageListener = (messageID: string) => {
      setMessages((prevMessages) => prevMessages.delete(messageID));
    };

    socket.on("message", messageListener);
    socket.on("deleteMessage", deleteMessageListener);
    socket.emit("getMessages");

    return () => {
      socket.off("message", messageListener);
      socket.off("deleteMessage", deleteMessageListener);
    };
  }, [socket]);

  return (
    <div className="message-list">
      {messages
        .toSet()
        .sortBy((message: IMessage) => message.time)
        .map((message: IMessage) => (
          <div
            key={message.id}
            className="message-list--message-container"
            title={`Sent at ${new Date(message.time).toLocaleTimeString()}`}
          >
            <span className="message-list--message">{message.value}</span>
            <span className="message-list--user">{message.user.name}</span>
          </div>
        )).toArray()
      }
    </div>
  );
};

export default MessageList;

The above component is using the Map from Immutable to make sure that a new Map is returned when setting a value on it. This will make React detect that there was a change in the state so that it renders the new data. It’s important to use the functional version of setMessages in order to get prevMessages. Otherwise, you’re using messages as a closure and it will keep trying to set the value to the original Map, which means you’ll only ever see a single message.

When using useEffect, you can return a function that will get run during cleanup. This removes the listeners from the socket, so those functions don’t get called even after the component is unmounted. It’s also important to notice the [socket] passed in as the second param to useEffect (useEffect(addListeners, [socket])). This tells it to only update when one of the values in the array changes, meaning it will only run when a new socket is passed in. If you didn’t have that, you would be removing the listeners and reading them on every render, then send a message to get new messages, and you’d get stuck in a never-ending loop.

I’ve also added some basic styles to make it looks a little easier to read. You’ll need to create a new file src/client/MessageList.scss with the following (but feel free to tweak it to your heart’s desire…it’s nothing fancy at the moment):

.message-list {
  max-width: 500px;
  width: 100%;

  .message-list--message-container {
    display: flex;
    font-size: 14px;
    justify-content: space-between;
    align-items: center;

    .message-list--user {
      font-size: 1.1em;
      font-weight: bold;
      justify-content: flex-end;
    }

    .message-list--message {
      flex: 1;
      display: flex;
      justify-content: flex-start;
      padding-right: 10px;
    }
  }
}

Now to add those to your app, edit src/client/App.tsx to look like this:

import React from "react";
import io from "socket.io-client";

import MessageList from "./MessageList";
import NewMessage from "./NewMessage";

const socket = io(location.origin);

export default () => (
  <div>
    <MessageList socket={socket} />
    <NewMessage socket={socket} />
  </div>
);

Add User Authentication to Your Node.js + TypeScript API

You should now be able to connect to your server and send messages that get posted in real time. You can have multiple people connected and they should all get all the messages as they come, and they should get removed after 10 seconds for all clients.

The big missing piece though is all users are just “Anonymous”. You could let users simply add their name, but that wouldn’t guarantee they are who they say they are. Instead, you can add user authentication with Okta to make it easy to manage users and verify them.

Okta is a cloud service that allows developers to create, edit, and securely store user accounts and user account data, and connect them with one or multiple applications.

If you don’t already have one, sign up for a forever-free developer account. Log in to your developer console, navigate to Applications, then click Add Application. Select Single-Page App, then click Next. You can leave all the settings the same, just change the name of your app to something meaningful. The settings should look something like this:

Click Done to save your app, then copy your Client ID and paste it as a variable into a file called .env in the root of your project. This will allow you to access the file in your code without needing to store credentials in source control. You’ll also need to add your organization URL (without the -admin suffix). The file should end up looking like this:

OKTA_ORG_URL=https://{yourOktaDomain}
OKTA_CLIENT_ID={yourClientId}

Okta provides an API that allows you to look up user information, among many other things. Okta also has a Node library to make it really simple. For your app to authenticate with Okta so you can look up user information, such as their name, you need an API token. From your dashboard, select Tokens from the API dropdown in the header. Click Create Token and give it a meaningful name. It will then give you a token this one time - if you lose it you’ll need to create another one.

Go ahead and add this to your .env file as OKTA_TOKEN, so that it now looks like this:

OKTA_ORG_URL=https://{yourOktaOrgUrl}
OKTA_CLIENT_ID={yourClientId}
OKTA_TOKEN={yourToken}

Add Server-Side Authentication

In order for your server to read the environment variables, you’ll need to use dotenv. You’ll also need to install the Okta SDK and a JWT Verifier to ensure that the tokens users are sending are valid. Install these dependencies:

npm install [email protected] @okta/[email protected] @okta/[email protected]
npm install --save-dev @types/[email protected]

You’ll first need to set up dotenv. At the very top of your src/index.ts file, add the following. It should be the first thing in your code to make sure the rest of your code has access to your environment variables from .env:

import dotenv from "dotenv";
dotenv.config();

At the time of this writing, Okta doesn’t provide any types for their modules, so TypeScript will complain when you try to use them. You’ll need to simply create a new file and declare the modules. Create a new file src/global.d.ts with the following:

declare module "@okta/jwt-verifier";
declare module "@okta/okta-sdk-nodejs";

In src/socket.ts you’ll need to import a couple new packages:

import OktaJwtVerifier from "@okta/jwt-verifier";
import okta from "@okta/okta-sdk-nodejs";

Then you’ll need to configure those as well:

const jwtVerifier = new OktaJwtVerifier({
  clientId: process.env.OKTA_CLIENT_ID,
  issuer: `${process.env.OKTA_ORG_URL}/oauth2/default`,
});

const oktaClient = new okta.Client({
  orgUrl: process.env.OKTA_ORG_URL,
  token: process.env.OKTA_TOKEN,
});

Now inside your export default function, before the call to io.on("connection", connectionHandler), add the following middleware:

  const users: Map<Socket, IUser> = new Map();

  io.use(async (socket, next) => {
    const { token = null } = socket.handshake.query || {};
    if (token) {
      try {
        const [authType, tokenValue] = token.trim().split(" ");
        if (authType !== "Bearer") {
          throw new Error("Expected a Bearer token");
        }

        const { claims: { sub } } = await jwtVerifier.verifyAccessToken(tokenValue);
        const user = await oktaClient.getUser(sub);

        users.set(socket, {
          id: user.id,
          name: [user.profile.firstName, user.profile.lastName].filter(Boolean).join(" "),
        });
      } catch (error) {
        // tslint:disable-next-line:no-console
        console.log(error);
      }
    }

    next();
  });

This will check the socket handshake to see if there’s a token attached or not. If there is, it’ll use the jwtVerifier to make sure it’s a valid token. If it is, then it’ll fetch information about the user so that it can get their name. Finally, it adds the user to an es6 Map so that it can look up the user by the socket later.

Now, where we were setting user: defaultUser, you can enter the actual user:

const message: IMessage = {
  // ...
  user: users.get(socket) || defaultUser,
};

You’ll also want to add a listener when the socket disconnects to clean up the users Map. At the end of your connection handler, add the following:

socket.on("disconnect", () => {
  users.delete(socket);
});

Add Client-Side Authentication

You’ll need some more packages in order to set up authentication on the client. Add the following:

npm install @okta/[email protected] [email protected] [email protected]

Okta uses React Router to handle routes and keep things secure. You’ll need to wrap the App component in Router and Security wrappers, then render it as a Route. You’ll need to add an ImplicitCallback route as well so that Okta knows what to do after you’ve authenticated in the browser. Edit your src/client/index.tsx file to look like this:

import { ImplicitCallback, Security } from "@okta/okta-react";
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";

import App from "./App";

ReactDOM.render(
  <Router>
    <Security
      issuer={`${process.env.OKTA_ORG_URL}/oauth2/default`}
      client_id={process.env.OKTA_CLIENT_ID}
      redirect_uri={`${window.location.origin}/implicit/callback`}
    >
      <Route path="/" exact component={App} />
      <Route path="/implicit/callback" component={ImplicitCallback} />
    </Security>
  </Router>,
  document.getElementById("root"),
);

You can create a new React hook to help with authentication as well. This will require you to pass in an auth variable, which will then be used to determine whether or not a user is authenticated, find out info about the user, and get the access token. These are then passed back into your React component for user later. Create a new file src/client/auth.ts:

import { useEffect, useState } from "react";

export const useAuth = (auth) => {
  const [authenticated, setAuthenticated] = useState(null);
  const [user, setUser] = useState(null);
  const [token, setToken] = useState(null);

  useEffect(() => {
    auth.isAuthenticated().then((isAuthenticated) => {
      if (isAuthenticated !== authenticated) {
        setAuthenticated(isAuthenticated);
      }
    });
  });

  useEffect(() => {
    if (authenticated) {
      auth.getUser().then(setUser);
      auth.getAccessToken().then((accessToken) => {
        setToken(accessToken ? `Bearer ${accessToken}` : null);
      });
    } else {
      setUser(null);
      setToken(null);
    }
  }, [authenticated]);

  return [authenticated, user, token];
};

In your src/client/App.tsx file, you’ll need to use the useAuth hook to get info about the user, including the token. Then whenever the token changes, you’ll need to reconnect to the backend with a new socket. You’ll also need to wrap the App with Okta’s withAuth higher order component in order to get access to auth as a prop. This will allow you to create buttons to sign the user in or out. Edit your src/client/App.tsx file to look like this:

import { withAuth } from "@okta/okta-react";
import { Map } from "immutable";
import React, { SyntheticEvent, useEffect, useState } from "react";
import io from "socket.io-client";

import { useAuth } from "./auth";
import MessageList from "./MessageList";
import NewMessage from "./NewMessage";

export default withAuth(({ auth }) => {
  const [authenticated, user, token] = useAuth(auth);
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    const newSocket = io(location.origin, token && { query: { token } });
    setSocket(newSocket);
    return () => newSocket.close();
  }, [token]);

  return socket && (
    <div>
      {user ? (
        <div>
          Signed in as {user.name}
          <button onClick={() => auth.logout()}>Sign out</button>
        </div>
      ) : (
        <div>
          Not signed in
          <button onClick={() => auth.login()}>Sign in</button>
        </div>
      )}
      <MessageList socket={socket} />
      <NewMessage socket={socket} />
    </div>
  );
});

You should now be able to run npm run dev again and send messages in real-time and see the user who sent the message!

Hire Node JS Developer from Expert Node JS Development Company

Hire Node JS Developer from Expert Node JS Development Company

NodeJS Development Company-Hire Node JS developer from the most prominent NodeJS development company, Mobiweb and get remarkable Node.js app development services.

Are you looking to hire the most talented and expert Node JS developers for your valuable web application projects and mobile app development projects or you want to migrate application on Node JS framework? Then you have to hire Node JS developer from leading offshore Node JS development company Mobiweb Technologies. We have a team of developers with extensive experience in developing Node JS based applications whether it is web based or mobile app based.

Creating a RESTful Web API with Node.js and Express.js from scratch

Creating a RESTful Web API with Node.js and Express.js from scratch

In this article, I’ll show you step by step how to create a RESTful Web API with Node.js and Express.js by building a simple and useful Todo API. This article assumes you have basic javascript knowledge and terminal using capabilities.

In this article, I’ll show you step by step how to create a RESTful Web API with Node.js and Express.js by building a simple and useful Todo API. This article assumes you have basic javascript knowledge and terminal using capabilities.

You can also build a Web API in Node.js by using another framework except Express.js but Express.js is one of the most popular web framework for Node.js.

You can found the final source code of this Web API in this github repository.

Let’s start to create our mentioned Web API.

Before start

If you have never used Node.js or npm package manager you should install them.

To check whether the Node.js is already installed on your computer, open your terminal and run node -v command. If you see your Node.js version it's installed. Otherwise go to below link.

Click here to download and install Node.js (You can choose LTS version)

And if you don’t have any IDE or text editor for writing javascript I advice you Visual Studio Code.

Click here to download VS Code (Optional)

About express-generator

In fact we could use <a href="https://expressjs.com/en/starter/generator.html" target="_blank">express-generator</a> tool which designed to creating an Express Web API quickly but I want to create this API from scratch because of that tool puts some extra files and folder structures that we don't need them now. But you can use this useful tool next time on creating new Web API. I won't use it now due to keep article simple.

Creating Project

Go to your workspace root folder and create a new folder there named "todo-api".

Then create "package.json" and "server.js" files into "todo-api" folder like below.

package.json

{
    "name": "todo-api",
    "version": "1.0.0",
    "scripts": {
        "start": "node server.js"
    },
    "dependencies": {
        "express": "^4.16.4"
    }
}

server.js

const http = require('http');
const express = require('express');
const app = express();
app.use(express.json());
app.use('/', function(req, res) {
    res.send('todo api works');
});
const server = http.createServer(app);
const port = 3000;
server.listen(port);
console.debug('Server listening on port ' + port);

After creating above files open your terminal in the "todo-api" folder and run npm installcommand.

This command will be install your project dependencies which pointed at the "package.json" file.

After finished package download process, downloaded dependency files will be installed into"node_modules" folder at the root of the "todo-api" folder.

After finished package installing then run npm start to start our Web API.

Now our Web API listening. To see result open your web browser then write localhost:3000 to address bar and press enter.

As result you’ll see our request handler response in your browser: “todo api works”.

This is a dead simple Express.js Web API. And it needs the some development. For example we need to an api endpoint to get todo items. So let’s add a new API endpoint for this.

Create a new folder named "routes" in the root of the "todo-api" folder.

Then create a "items.js" file inside of "routes" folder and put following codes inside it.

Your final folder structure should be like below;

/todo-api
/node_modules
/routes
    items.js
package.json
server.js

items.js

const express = require('express');
const router = express.Router();
const data = [
    {id: 1, title: 'Finalize project', order: 1, completed: false, createdOn: new Date()},
    {id: 2, title: 'Book ticket to London', order: 2, completed: false, createdOn: new Date()},
    {id: 3, title: 'Finish last article', order: 3, completed: false, createdOn: new Date()},
    {id: 4, title: 'Get a new t-shirt', order: 4, completed: false, createdOn: new Date()},
    {id: 5, title: 'Create dinner reservation', order: 5, completed: false, createdOn: new Date()},
];
router.get('/', function (req, res) {
    res.status(200).json(data);
});
router.get('/:id', function (req, res) {
    let found = data.find(function (item) {
        return item.id === parseInt(req.params.id);
    });
    if (found) {
        res.status(200).json(found);
    } else {
        res.sendStatus(404);
    }
});
module.exports = router;

Initial code of "items.js" file contains two endpoints. First one gets all todo items and second one gets one item which matches given id parameter.

Before testing items routes we should register it in the "server.js" file.

Modify "server.js" file like below to register new item routes.

server.js

const http = require('http');
const express = require('express');
const itemsRouter = require('./routes/items');
const app = express();
app.use(express.json());
app.use('/items', itemsRouter);
app.use('/', function(req, res) {
    res.send('todo api works');
});
const server = http.createServer(app);
const port = 3000;
server.listen(port);
console.debug('Server listening on port ' + port);

Now run npm start to start our Web API.

Then open your web browser and write localhost:3000/items to address bar and press enter.

You’ll see todo items json array in the response body.

And write localhost:3000/items/3 to address bar and press enter.

You’ll see the todo item which has id 3 in the response body.

But not finished up yet.

CRUD Operations and HTTP methods

I think we’ll need CRUD operations to Create, Read, Update and Delete todo items.

We have already two endpoints for getting items. So we need Create, Update and Delete endpoints.

Let’s add also these endpoints into the items.js file.

Our final "items.js" file and endpoints should be like below.

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

const data = [
  {id: 1, title: 'Finalize project',          order: 1, completed: false, createdOn: new Date()},
  {id: 2, title: 'Book ticket to London',     order: 2, completed: false, createdOn: new Date()},
  {id: 3, title: 'Finish last article',       order: 3, completed: false, createdOn: new Date()},
  {id: 4, title: 'Get a new t-shirt',         order: 4, completed: false, createdOn: new Date()},
  {id: 5, title: 'Create dinner reservation', order: 5, completed: false, createdOn: new Date()},
];

router.get('/', function (req, res) {
  res.status(200).json(data);
});

router.get('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    res.status(200).json(found);
  } else {
    res.sendStatus(404);
  }
});

router.post('/', function (req, res) {
  let itemIds = data.map(item => item.id);
  let orderNums = data.map(item => item.order);

  let newId = itemIds.length > 0 ? Math.max.apply(Math, itemIds) + 1 : 1;
  let newOrderNum = orderNums.length > 0 ? Math.max.apply(Math, orderNums) + 1 : 1;

  let newItem = {
    id: newId,
    title: req.body.title,
    order: newOrderNum,
    completed: false,
    createdOn: new Date()
  };

  data.push(newItem);

  res.status(201).json(newItem);
});

router.put('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    let updated = {
      id: found.id,
      title: req.body.title,
      order: req.body.order,
      completed: req.body.completed
    };

    let targetIndex = data.indexOf(found);

    data.splice(targetIndex, 1, updated);

    res.sendStatus(204);
  } else {
    res.sendStatus(404);
  }
});

router.delete('/:id', function (req, res) {
  let found = data.find(function (item) {
    return item.id === parseInt(req.params.id);
  });

  if (found) {
    let targetIndex = data.indexOf(found);

    data.splice(targetIndex, 1);
  }

  res.sendStatus(204);
});

module.exports = router;

Short Explanation

I wanna explain shortly some points of our last codes.

First of all you must have noticed that our api works on a static data and keeps it on memory. All of our GET, POST, PUT and DELETE http methods just manipulate a json array. The purpose of this is to keep article simple and draw attention to the Web API structure.

Due to this situation our POST method has some extra logic such as calculating next item ids and order numbers.

So you can modify logic and data structures in these http methods to use a database or whatever you want.

Testing API with Postman

We have tested the GET methods of our Web API in our web browser and seen responses. But we can’t test directly POST, PUT and DELETE http methods in web browser.

If you want to test also other http methods you should use Postman or another http utility.

Now I’ll show you how to test the Web API with Postman

Before we start click here and install Postman.

When you first launch Postman after installing you’ll see start window. Close this start window by clicking close button on top right corner. Then you must see following screen.

An empty Postman request

Sending GET Request

Before sending a request to API we should start it by running npm startcommand as we do before.

After start the Web API and seeing “Server listening on…” message write localhost:3000/itemsto address bar as seen below and click Send button. You'll see todo items array as API response like below.

Sending a GET request with Postman

You can try similarly by giving an item id in request url like this localhost:3000/items/3

Sending POST Request

To sending a POST request and create a new todo item write localhost:3000/items to address bar and change HTTP verb to POST by clicking arrow at front of the address bar as seen below.

Sending a POST request with Postman

Before sending the POST request you should add request data to body of the request by clicking body tab and selecting raw and JSON as seen below.

Attaching a JSON body to POST request in Postman

Now click Send button to send POST request to the Web API. Then you must get “201 Created” http response code and seeing created item in the response body.

To see the last status of todo items send a get request to localhost:3000/itemsaddress. You must see newly created item at the end of the list.

Sending PUT Request

Sending PUT request is very similar to sending POST request.

The most obvious difference is request url should be pointed specific item like this localhost:3000/items/3

And you should choose PUT as http verb instead of POST and send all of the required data in the request body unlike POST.

For example you could send a JSON body in the PUT request as below.

An example JSON body for PUT request

{
    "title": "New title of todo item",
    "order": 3,
    "completed": false
}

When you click Send button you must get “204 No Content” http response code. You can check item you updated by sending a get request.

Sending DELETE Request

To send a DELETE request, change the request url to address a specific item id like this localhost:3000/items/3

And select DELETE as http verb and click Send button.

You must get “204 No Content” http response code as result of the DELETE operation.

Send a get request and see the last status of list.

About the DELETE Http Request

I want to say a few words about DELETE http request. You must have noticed something in our delete code. DELETE request returns “204 No Content” every situation.

Http DELETE requests are idempotent. So what that mean? If you delete a resource on server by sending DELETE request, it’s removed from the collection. And every next DELETE request on the same resource won’t change outcome. So you won’t get “404 Not Found” in the second request. Each request returns same response whether succeed or not. That’s mean idempotent operation.

Conclusion

Finally we’ve tested all http methods of our Web API.

As you can see, it works just fine.

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Main Reasons of Using Node JS for Your Web Application Development

Main Reasons of Using Node JS for Your Web Application Development

You have to hire Node JS developer from prestigious and expert Node JS development company Mobiweb Technologies. They are tech enthusiasts with new and latest programming ideas, web development technologies and industry trends.

Node JS is the best JavaScript for utilizing in real-time applications. If you are stressed of using low level web sockets or protocols then with the incredible speed of Node JS you can easily develop real-time applications. According to the business perspective, Node JS is highly advantageous for any online business or business website, so it is very difficult for companies or business owners to avoid Node JS for their web application projects. For the best results in your Node JS development project you must have to hire Node JS developer from the prestigious web development company- Mobiweb Technologies.