Implementing a Simple REST API in ExpressJS and Consuming it from Client, Server or using CURL/Postman

admin  

In this tutorial, we'll create a simple REST API in Express, that we can invoke from a frontend to get domain suggestions. We'll start setting up a basic Express server that handles only a POST request, and we'll create a small frontend to invoke it. This server will receive a description of what the user is looking for and, for the purpose of this example, will respond with a mock array of domain suggestions.

Implementing the REST API on an Express App

Step 1: Setting up a new Express App

We first create a new directory for the rest api server project, navigate into it, initialize a new Node.js project and Install Express:

mkdir my-domain-search-app
cd my-domain-search-app
npm init -y
npm install express

Step 2: Create Express Server File

Create a file named server.js in the root of your project directory and add the following code to set up a basic Express server:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;

// Middleware to parse JSON bodies
app.use(bodyParser.json());

// Handle CORS - This is required for the frontend to communicate with the backend
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header(
        'Access-Control-Allow-Headers',
        'Origin, X-Requested-With, Content-Type, Accept'
    );
    next();
});

// POST endpoint for domain suggestions
app.post('/api/domain-suggestions', (req, res) => {
    const description = req.body.description;
    console.log(`Received description: ${description}`);

    // Mock response - replace this with actual logic to generate domain suggestions
    const suggestions = [
        `${description}-online.com`,
        `${description}hub.com`,
        `get${description}.com`
    ];

    res.json({success: true, suggestions});
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Step 3: Running the Express Server

Start your server by running:

npm run start

Or, if you set up your package.json's scripts to include a command for starting the server, use that command. By default, node server.js should suffice.

The Express server is now set up to handle POST requests at /api/domain-suggestions and will return a mock list of domain suggestions based on the description sent in the request body. Remember, the actual logic for generating domain suggestions (e.g., calling an external API or algorithm) should replace the mock logic provided in the example.

REST API with a newly generated app using Express generator

In the previous point we created a project using npm init, then we added express as an npm package. However, if we want a more structured project we can use the express generator to generate a project using the default template.

Connect Your Frontend to Your Express Backend

To connect the frontend to the Express backend, we need to modify the JavaScript in the HTML file to make an AJAX call (using fetch, for example) to the new endpoint when the form is submitted. Here is an example snippet of the frontend code:

document.getElementById('domainSearchForm').addEventListener('submit', async function(event) {
    event.preventDefault(); // Prevent the default form submission
    const description = document.getElementById('descriptionInput').value;
    
    // Make the API call to the Express backend
    try {
        const response = await fetch('http://localhost:3000/api/domain-suggestions', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ description }),
        });

        if (response.ok) {
            const data = await response.json();
            console.log('Suggestions:', data.suggestions);
            // Process your suggestions here
        } else {
            console.error('Failed to fetch suggestions');
        }
    } catch (error) {
        console.error('Error:', error);
    }
});

This code sends the description to the Express server and logs the suggested domains to the console. Later on, we can expand this by displaying the suggestions on the webpage.

Best Practices

Following Rest Design Principles

By definition the REST API does not impose a set of strict rules, but it should follow the design principles of the representational state transfer (REST) architectural style:

  • Uniform Interface: API requests for the same resource must be consistent, regardless of the request's origin. Resources should be uniquely identifiable through a URI and designed to be neither too large nor lacking necessary information.

  • Client-Server Decoupling: Client and server applications should operate independently, with the client knowing only the URI of requested resources. Servers should not alter client applications except to deliver requested data.

  • Statelessness: Each request must contain all information required for processing, eliminating the need for server-side sessions or data storage related to client requests.

  • Cacheability: Resources should support caching to enhance performance and scalability, with server responses indicating cacheability.

  • Layered System Architecture: RESTful communication may pass through multiple intermediaries without client or server being able to discern direct or indirect connections, ensuring flexibility in network setups.

  • Code on Demand (Optional): While REST APIs typically deliver static resources, they can also provide executable code on demand to enhance functionality, such as Java applets, but this is optional.

Using HTTP Methods Appropriately

When implementing a REST API in Express.js, there are several best practices and important points that we should consider to ensure the API is robust, secure, efficient, and easy to maintain.

  • GET for retrieving resources.
  • POST for creating new resources.
  • PUT or PATCH for updating resources.
  • DELETE for deleting resources.

Implementing Proper HTTP Status Codes

Use the appropriate HTTP status codes to indicate the success or failure of an API request. For example, 200 (OK), 201 (Created), 400 (Bad Request), 404 (Not Found), 500 (Internal Server Error), etc.

Use Middleware Wisely

This is something that is not specific to REST apis, but to any express application. Middleware functions can execute code, make changes to the request and response objects, end the request-response cycle, and call the next middleware function.

Common uses include request logging, authentication, body parsing, and handling CORS.

Secure The API

  • Use HTTPS for secure communication.
  • Implement authentication and authorization (JWT, OAuth).
  • Sanitize input to prevent injection attacks.
  • Use rate limiting to prevent abuse.
  • Keep dependencies up to date to avoid vulnerabilities.

Version The API

API versioning (e.g., /api/v1/resource) allows to make changes to the API without breaking existing clients.

Error Handling

Implement centralized error handling to catch and respond to errors consistently. Use try-catch for asynchronous code to handle exceptions.

Logging

Log requests, responses, and errors to help with debugging and monitoring. Consider using a logging library that supports different log levels (e.g., winston, morgan).

Documentation

Document your API endpoints, request parameters, and response formats. Tools like Swagger (OpenAPI) can help generate interactive API documentation.

Caching & Performance Optimization

  • Use caching strategies to reduce load times and database queries.
  • Optimize database queries and indexes.
  • Use compression middleware to reduce the size of the response body.

Use Environment Variables

  • Store configuration options and sensitive information in environment variables, not in your codebase.

Serverless Considerations

  • You can deploy APIs in serversless environments, like AWS lambda functions, cloudflare workers or digitalocean functions. Plan accordingly, because express is not an option foor this.

Content Negotiation

  • If you plan to implement an API available to different users, might be usefull to support multiple response formats (e.g., JSON, XML) if necessary, and handle Accept headers appropriately.

Validation

It's a good practice to validate input data both on the client and server side to ensure your API receives the correct data format and values. The server should always implement a validation mechanism.

Cross-Origin Resource Sharing (CORS)

If your API is accessed from different domains, configure CORS correctly to allow or restrict access.

Rate Limiting

Implement rate limiting to protect your API against brute-force attacks or overly frequent requests from a single client. By focusing on these aspects, you can create an Express.js REST API that is not only functional and user-friendly but also secure, reliable, and scalable.

Consume ES Module Libraries from CommonJS Apps By Creating a Simple Node Server

If you want to use ES Module Libraries from CommonJS Apps or you endup in a version or depdency hell, or even want to use nodejs libraries from other languanges like python you can easily expose them as APIs

Understanding Middleware Types and Invocation Order in Express Applications

Middleware in Express sorts out requests and responses, tackling everything from data parsing to security. Though often misunderstood, getting a grip on the different types and their order can seriously level up your web apps, making them slick, secure, and speedy.