How to Create a NodeJS API Without Using a Framework

Node.js is an open-source JavaScript runtime built on chrome’s v8 engine that allows you to run JavaScript code outside a browser.

Its event model, ecosystem, and speed have made Node.js one of the most wanted and used runtimes for server-side applications.

Most Node.js API servers use Express or another framework. However, you can also create a simple Node.js API without a framework in just a few steps.

MAKEUSEOF VIDEO OF THE DAY

SCROLL TO CONTINUE WITH CONTENT

Step 1: Setting Up Your Development Environment

Create a project directory and cd into it by running:

 mkdir nodejs-api

cd

nodejs-api

Next, initialize npm in your project by running:

 npm init -y

This CRUD API will feature the use of MongoDB, a NoSQL database, and its popular ODM, mongoose.

Run the following command to install mongoose:

 npm 

install

mongoose

Next, create a server.js file in your project’s root directory and add the code block below to create a server:

 

const

http =

require

(

"http"

);

const

server = http.createServer(

(

req, res

) => {});

server.listen(

3000

, () => {
  

console

.log(

`Server is running`

);
});

This code block imports the http module, a core Node.js module. The http module allows Node.js to transfer data over HTTP. This module contains the methods required to create a server.

Next, it calls the http module’s createServer method which creates and returns an instance of a server. The createServer method takes a callback function with a request and response object as parameters.

Next, the code calls the listen method on the returned server instance. This allows the server to start listening for traffic on the given port. The listen method fires a callback—the second argument—when it succeeds.

Finally, create two directories named routes and models in your project’s root directory. The routes folder will contain the routing logic for your API, while model will contain everything related to the database.

Step 2: Connecting Your Application to a Database

In server.js, import mongoose:

 

const

mongoose =

require

(

"mongoose"

);

Call the connect method on mongoose and pass your MongoDB URI as an argument:

 mongoose.connect(

"MongoDB_URI"

)

Step 3: Creating an API Model

Create a CRUD API for a simple blog application. In your models folder, create a blogModel.js file and add the following code to your file:

 

const

mongoose =

require

(

"mongoose"

);

const

blogSchema = mongoose.Schema({
  title: {
    type:

String

,
    required: [

true

,

"Blog must have a title"

],
  },

  body: {
    type:

String

,
    required: [

true

,

"Blog must have a body"

],
  },
});

module

.

exports

= mongoose.model(

"Blog"

, blogSchema);

The code block above creates a mongoose model with two properties and maps them to a MongoDB database.

Both properties in this model have a String type with required set to true. The accompanying error messages will display if a request body does not contain either of the properties.

The final line creates and exports a mongoose model by calling the model method on mongoose. Pass the model name (Blog) as the first argument and a schema (blogSchema) as the second argument.

Step 4: Implementing Routing in Your Application

Without the aid of frameworks like Express, you’ll have to manually create the logic to handle each request made to your API.

First, create a blogRoutes.js file in your routes folder, then import the blog model:

 

const

Blog =

require

(

"../models/blogModel"

);

Next, create an asynchronous router function, pass req and res as parameters, and export the function:

 

const

router =

async

function

(

req, res

) {};

module

.

exports

= router;

This function will contain all your routing logic.

Next, you’ll implement the routing logic route by route.

GET Routes

Add the code block below to your router function to implement the GET route handler for requests made to /api/blogs:

 

if

(req.url ===

"/api/blogs"

&& req.method ===

"GET"

) {
    
    

const

blogs =

await

Blog.find();

    //

set

the

status

code

and

content

-

type


    res.writeHead(

200

, {

"Content-Type"

:

"application/json"

});

    
    

res

.end

(

JSON

.stringify

(

blogs

));
}

The code block above checks the url and method properties of the request object. It then fetches all blogs from the database via the find method on the mongoose model (Blog).

Next, it calls the writeHead method on res, the response object. This method sends a response header given three arguments: a status code, an optional status message, and headers. The 200 status code represents a successful response and the content-type for this API call is set to application/json.

Finally, close the request to ensure the server doesn’t hang by calling the end method on res. The call to JSON.stringify converts the blogs object to a JSON string and passing that to the end method returns it as the response body.

Add the code block below to your router function to implement the GET route handler for a single resource:

 

if

(req.url.match(

/\\/

api\\/blogs\\/([

0

-9

]+)/) && req.method ===

"GET"

) {
    

try

{
        
        

const

id = req.url.split(

"/"

)[

3

];

        
        

const

blog =

await

Blog.findById(id);

        if (blog) {
            res.writeHead(

200

, {

"Content-Type"

:

"application/json"

});
            

res

.end

(

JSON

.stringify

(

blog

));
        }

else

{
            

throw

new

Error

(

"Blog does not exist"

);
        }
    }

catch

(error) {
        res.writeHead(

404

, {

"Content-Type"

:

"application/json"

});
        

res

.end

(

JSON

.stringify

({

message

: error }));
    }
}

This code uses the match method, which takes in a regex expression as an argument, to check if the url matches the format: /api/blogs/<number>.

Next, extract the id property from the url string by calling its split method. This method takes a pattern as an argument (/), splits the string based on the pattern, and returns an array. The third element of that array is the id.

Finally, retrieve the document with the matching id from your database. If it exists, send a response code of 200, close the request, and send the retrieved blog. If it doesn’t exist, throw an error and send it as a response in the catch block.

POST Route

Add the code block below to your router function to implement the POST route handler:

 

if

(req.url ===

"/api/blogs"

&& req.method ===

"POST"

) {
    

try

{
        

let

body =

""

;

        
        req.on(

"data"

, (chunk) => {
            body += chunk.toString();
        });

        // Listen for

end

event


        req.on(

"end"

,

async

() => {
            
            

let

blog =

new

Blog(

JSON

.parse(body));

            
            

await

blog.save();
            res.writeHead(

200

, {

"Content-Type"

:

"application/json"

});
            

res

.end

(

JSON

.stringify

(

blog

));
        });
    }

catch

(error) {
        

console

.log(error);
    }
}

The request object implements the Node.js ReadableStream interface. This stream emits a data and an end event which give you access to data from the request body.

This code listens for the data event and handles it by converting it to a string and concatenating it to the body variable. In the end event handler, it creates a Blog instance with the parsed body string. It then saves the new blog, sends the status code and content header, and closes the request.

PUT Route

Add the code block below to your router function to implement the PUT route handler:

 

if

(req.url.match(

/\\/

api\\/blogs\\/([

0

-9

]+)/) && req.method ===

"PUT"

) {
    

try

{
        
        

const

id = req.url.split(

"/"

)[

3

];
        

let

body =

""

;

        req.on(

"data"

, (chunk) => {
            body += chunk.toString();
        });

        req.on(

"end"

,

async

() => {
            // Find and

update

document


            

let

updatedBlog =

await

Blog.findByIdAndUpdate(id,

JSON

.parse(body), {
                

new

:

true

,
            });

            res.writeHead(

200

, {

"Content-Type"

:

"application/json"

});
            

res

.end

(

JSON

.stringify

(

updatedBlog

));
        });
    }

catch

(error) {
        

console

.log(error);
    }
}

The PUT request handler is almost identical to the POST request handler, except that it extracts the id property from the url to update the relevant blog.

DELETE Route

Add the code block below to your router function to implement your DELETE route handler:

 // 

DELETE

: /api/blogs/:

id


if

(req.url.match(

/\\/

api\\/blogs\\/([

0

-9

]+)/) && req.method ===

"DELETE"

) {
    

try

{
        

const

id = req.url.split(

"/"

)[

3

];
        
       //

Delete

blog

from

DB
        

await

Blog.findByIdAndDelete(id);
        res.writeHead(

200

, {

"Content-Type"

:

"application/json"

});
        

res

.end

(

JSON

.stringify

({

message

:

"Blog deleted successfully"

}));
    }

catch

(error) {
        res.writeHead(

404

, {

"Content-Type"

:

"application/json"

});
        

res

.end

(

JSON

.stringify

({

message

: error }));
    }
}

This code block extracts the id from the url, deletes the document with the matching id, sends the status code and headers, and closes the request.

Finally, import router in your server.js file and call your router function, passing req and res as arguments:

 

const

router =

require

(

"./routes/blogRoutes"

);

const

server = http.createServer(

(

req, res

) => {
    router(req, res);
});

This allows your server to intercept and handle requests appropriately.

You can find the completed project in this GitHub repository.

Using a Node.js Framework

Even though it’s possible to create a web API by hand, it can be a difficult task. You’ll need to make sure you’ve covered lots of edge cases and your code had better be bug-free.

Over the years, developers have built frameworks like ExpressJS, NestJS, Fastify, etc., to make it much easier.