DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Breaking Up a Monolithic Database with Kong
  • Building REST API Backend Easily With Ballerina Language
  • RESTful Web Services: How To Create a Context Path for Spring Boot Application or Web Service
  • Aggregating REST APIs Calls Using Apache Camel

Trending

  • Java's Quiet Revolution: Thriving in the Serverless Kubernetes Era
  • Building Scalable and Resilient Data Pipelines With Apache Airflow
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  • Docker Model Runner: Streamlining AI Deployment for Developers
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Creating a REST API Part 1: Web Server Basics

Creating a REST API Part 1: Web Server Basics

We'll start our REST API project by creating some initial directories/ files, create a web server module and wire things up so that it starts up and shuts down correctly.

By 
Dan McGhan user avatar
Dan McGhan
·
Apr. 02, 18 · Tutorial
Likes (12)
Comment
Save
Tweet
Share
31.1K Views

Join the DZone community and get the full member experience.

Join For Free

The web server is one of the most important components of a REST API. In this post, you will start your REST API project by creating some initial directories and files. Then you'll create a web server module and wire things up so that the web server starts up and shuts down correctly. Finally, you will add some basic logging capabilities to the web server.

Starting Up and Saying Hello

The code in this project will be organized using a generic directory structure that can be adjusted and built out over time as needed. In the VM, open a new terminal by going to Applications > Favorites > Terminal, then run the following commands.

cd ~
mkdir hr_app
cd hr_app/
touch index.js
mkdir config
touch config/web-server.js
mkdir controllers
mkdir db_apis
mkdir services
touch services/web-server.js

The project will be contained in the hr_app directory. The directories within hr_app should be easy to understand based on their names and the "High-level components" overview in the parent post. The index.js file can be thought of as the "main" file in a Java app - it will be the entry point of the application. We will be adding code to that file and the web-server.js files in the config and services directories in this post.

Go to Applications > Favorites > Files to open the file browser and navigate to Home > hr_app > config. Double-click web-server.js to open it in the gedit text editor. Copy and paste the following code into the file, then save your changes.

config/web-server.js
module.exports = {
  port: process.env.HTTP_PORT || 3000
};

This is a simple JavaScript module that exposes a single property named port. In Node.js, the process object has an env property that contains the user environment. I'm using that to set the value of port to the value of the environment variable HTTP_PORT. If that environment variable isn't defined, the default value will be 3000. It's common to derive ports from environment variables as they may vary in different environments or be randomly assigned at runtime.

With the configuration file ready, you can turn your attention to the web server module. Open the services/web-server.js file in gedit. Copy and paste the following code into the file and save your changes.

services/web-server.js
const http = require('http');
const express = require('express');
const webServerConfig = require('../config/web-server.js');

let httpServer;

function initialize() {
  return new Promise((resolve, reject) => {
    const app = express();
    httpServer = http.createServer(app);

    app.get('/', (req, res) => {
      res.end('Hello World!');
    });

    httpServer.listen(webServerConfig.port, err => {
      if (err) {
        reject(err);
        return;
      }

      console.log(`Web server listening on localhost:${webServerConfig.port}`);

      resolve();
    });
  });
}

module.exports.initialize = initialize;

Here's a breakdown of the web server module so far:

  • Lines 1-3: Several modules are required in. The HTTP module is included with Node.js but the express module will need to be installed via npm.
  • Lines 7-27: A function named initialize is declared. The function immediately returns a promise which is resolved or rejected depending on whether the web server is started successfully.
    • Lines 9-10: A new Express application is created (which is really just a function) and then used to create an HTTP server via the HTTP module.
    • Lines 12-14: The app's get method is used to add a handler for GET requests that come in on the root (/) path. The callback function (also called a middleware function) will be invoked when such a request is received and it will use the "response" parameter (res) to send a "Hello World!" response to the client.
    • Lines 16-25: The server's listen method is used to bind to the specified port and start listening for incoming requests.
  • Line 29: The initialize function is exported from the module so it can be invoked externally.

With the web server module defined, you can put it to use in the main module. Open index.js, copy and paste the following code into it, and save your changes.

const webServer = require('./services/web-server.js');

async function startup() {
  console.log('Starting application');

  try {
    console.log('Initializing web server module');

    await webServer.initialize();
  } catch (err) {
    console.error(err);

    process.exit(1); // Non-zero failure code
  }
}

startup();

The main module brings in the web server module, and then it defines and invokes an async function named startup. Because the web server module's initialize function returns a promise, you can use it with async/await and wrap it in a try-catch block (click here to learn more about async/await). If the initialize function finishes successfully, then the web server will be running; otherwise, any exceptions will be caught and handled.

All you need to do now is initialize npm and install Express - then you can run the app. Run the following commands in the terminal from the hr_app directory.

npm init -y
npm install express -s
node .

The npm init command is used to create the package.json file, which npm uses as a manifest file (the -y flag accepts the default options). The npm install command is used to install express (the -s flag adds Express to the list of dependencies in the package.json). Npm stores the modules you install in the node_modules directory. It also creates a file named package.lock.json to ensure identical module trees across a team of developers.

Do you see a message telling you the web server is listening on localhost:3000? Congratulations, you created an Express based web server! Open Firefox and navigate to http://localhost:3000.

And there it is, yet another "Hello World". While not particularly exciting, it's an important first step for your API. When you're ready, you can shut down the server by returning to the terminal and pressing Ctrl+C.

Controlling the Shutdown

While shutting down by pressing Ctrl+C works, you didn't have much control over how it happened. To control the shutdown process, you will need to explicitly close the web server and exit the Node.js process.

Append the following code to the bottom of the web server module.

services/web-server.js
// *** previous code above this line ***

function close() {
  return new Promise((resolve, reject) => {
    httpServer.close((err) => {
      if (err) {
        reject(err);
        return;
      }

      resolve();
    });
  });
}

module.exports.close = close;

The close function returns a promise that is resolved when the web server is successfully closed. The httpServer.close method stops new connections from being established, but it will not force already opened connections closed. Depending on how many connections are open and what they are doing, you might have to wait a bit for the callback to fire. Though you will not do it in this module, it is possible to use custom code or npm modules, such as http-shutdown, to force open connections closed.

With the close function in place, the main module can be updated to invoke it at the right times. Append the following code to the end of index.js.

// *** previous code above this line ***

async function shutdown(e) {
  let err = e;

  console.log('Shutting down');

  try {
    console.log('Closing web server module');

    await webServer.close();
  } catch (e) {
    console.log('Encountered error', e);

    err = err || e;
  }

  console.log('Exiting process');

  if (err) {
    process.exit(1); // Non-zero failure code
  } else {
    process.exit(0);
  }
}

process.on('SIGTERM', () => {
  console.log('Received SIGTERM');

  shutdown();
});

process.on('SIGINT', () => {
  console.log('Received SIGINT');

  shutdown();
});

process.on('uncaughtException', err => {
  console.log('Uncaught exception');
  console.error(err);

  shutdown(err);
});

SIGINT and SIGTERM events are related to signals that can be sent to the process to shut it down, such as when Ctrl+C is pressed. The uncaughtException event will occur when a JavaScript error is thrown but not caught and handled with a try-catch statement.

Try running and shutting down the application again. You'll know everything is working correctly when you see the "shutting down" messages in the terminal.

Adding Web Server Logging

There's just one last thing to round out our web server module: HTTP logging. There are various modules you could use for this type of logging, but morgan is known to be one of the best (and simplest). Let's install morgan with npm.

npm install morgan -s

Next, add the following line to services/web-server.js under the line that requires express (line 2).

const morgan = require('morgan');

Now you can incorporate morgan as a middleware function that all requests will go through with app.use. Add this line before the app.get call that produces the "hello world" message.

  // Combines logging info from request and response
    app.use(morgan('combined'));

    // *** app.get call below this line ***

Note that app.use is creating a pipeline of middleware functions that can interact with HTTP requests and responses. The middleware will execute in the order that they are included.

Restart the app and position the terminal so you can see it and Firefox at the same time. Each time you reload the page in Firefox you should see a new log entry appear in the terminal. By default, morgan streams log info to STDOUT (which is displayed in the terminal).

This is as far as I'll go with logging in this series. However, in a production application, you might want to stream morgan's output to an HTTP access log file. You might also consider a logging utility, such as Winston, for debugging and tracing as an alternative to the console object.

The next post in this series focuses on database basics-including connection pooling-that will help you understand and build REST APIs for Node.js.

Web server Web Service REST API Web Protocols Manifest file Express application

Published at DZone with permission of Dan McGhan, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Breaking Up a Monolithic Database with Kong
  • Building REST API Backend Easily With Ballerina Language
  • RESTful Web Services: How To Create a Context Path for Spring Boot Application or Web Service
  • Aggregating REST APIs Calls Using Apache Camel

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!