PERN Stack Series 3: Building The Server

PERN Stack Series 3: Building The Server

ยท

7 min read

In the last part of this series, we outline steps to set up the project structure. If you have not read the article, click the link: PERN stack Series 2: Project Setup - Todo application to read.

Today, we will be setting up the server side of our application. However, before we do so, we will touch on RESTful API as it is relevant to our project.

RESTful API

This stands for Representational State Transfer and is an architectural style for designing networked applications. It is an approach to building web services that are scalable, stateless, and follow a set of constraints and principles.

Here are some key concepts associated with RESTful APIs:

RESTful API, which stands for Representational State Transfer, is an architectural style for designing networked applications. It is an approach to building web services that are scalable, stateless, and follow a set of constraints and principles. Here are some key concepts associated with RESTful APIs:

  1. Resources: In a RESTful API, everything is considered a resource. Resources can be entities like objects, services, or concepts. Each resource is identified by a unique URI (Uniform Resource Identifier).

  2. HTTP Methods (CRUD): RESTful APIs use standard HTTP methods for performing operations on resources. The four main HTTP methods mapped to CRUD operations are:

    • GET: Retrieve a representation of a resource.

    • POST: Create a new resource.

    • PUT: Update a resource or create a new resource if it doesn't exist.

    • DELETE: Remove a resource.

  3. Statelessness: Each request from a client to a server must contain all the information needed to understand and fulfil the request. The server should not store any information about the client's state between requests.

  4. Representation: Resources can have multiple representations, such as JSON, XML, or HTML. The client and server can negotiate the representation during communication.

Now we have that out of the way, let's continue building! ๐Ÿ™‚

Create connection to Postgres database

Create a file called "db.js" , copy and paste the code below in the file

const Pool = require("pg").Pool;

const pool = new Pool({
    user: "postgres",
    password: "admin123",
    host: "localhost",
    port: "5432",
    database: "mytodo_db"
});

module.exports = pool;

This code exports a PostgreSQL connection pool instance using the pg library. Here's a breakdown of the code:

Importpg.Pool:

  1. The Pool class from the pg library is imported. This class is used to create a pool of PostgreSQL client connections.

  2. Create a PostgreSQL Connection Pool:

    • The pool constant is assigned a new instance of the Pool class, which is configured with the connection details for the PostgreSQL database.

    • user: The username for connecting to the database.

    • password: The password for connecting to the database.

    • host: The hostname of the PostgreSQL server.

    • port: The port on which the PostgreSQL server is running.

    • database: The name of the database.

  3. Export the Pool Instance:

    • The pool instance is exported as a module, making it available for use in other parts of your application.

Create Entry Point

we will create a file called index.js which will serve as our entry point. In the context of a system or application, an "entry point" refers to the point at which execution begins.

make sure you are in the server folder

cd server
touch index.js

inside the index.js, copy and paste the code below.


const express = require("express");
const app = express();
const cors = require("cors");
const pool = require("./db");
const { Query } = require("pg");

//middleware
app.use(cors());
app.use(express.json());

//Routes

//create a todo
app.post("/todos", async(req,res) => {
    try {   
        const query = "INSERT INTO todo(description) VALUES ($1) RETURNING *";
        const { description } = req.body
        const newTodo = await pool.query(query,[description]);

        res.json(newTodo.rows[0]);

    } catch (err) {
      console.log(err.message);
    }
});


//get all todos
app.get("/todos", async(req,res) => {
    try{
        const query = "SELECT * FROM todo"
        const allTodos = await pool.query(query,);

        res.json(allTodos.rows);

    } catch (err) {
        console.log(err.message);
    }
});


//get a todo
app.get("/todos/:id", async(req,res) => {
    try {
        const query = "SELECT * FROM todo WHERE todo_id = $1";
        const { id } = req.params; 
        const todo = await  pool.query(query, [id]);

        res.json(todo.rows[0]);

    } catch (err) {
        console.log(err.message);    
    }
});


//update a todo
app.put("/todos/:id", async(req,res) => {
    try {
        const query = "UPDATE todo SET description = $1 WHERE todo_id = $2 RETURNING *";
        const { id } = req.params;
        const { description } = req.body;
        const updateTodo = await pool.query(query, 
        [description, id]
        );

        res.json(updateTodo.rows[0]);

    } catch (err) {
        console.log(err.message);
    }
});

//delete a todo
app.delete("/todos/:id", async(req,res) => {
    try {
        const query = "DELETE FROM todo WHERE todo_id = $1";
        const { id } = req.params;
        const deleteTodo = await pool.query(query, 
        [ id ]
        );

        res.json("To-do was deleted");

    } catch (err) {
        console.log(err.message);
    }
});


app.listen(4000, () => {
    console.log("server has started on port 4000");
});

Here's a breakdown of the code:

  1. Imports:

    • express: The Express.js framework for handling HTTP requests and routes.

    • cors: Middleware to enable Cross-Origin Resource Sharing.

    • pool: Connection pool to interact with the PostgreSQL database.

    • Query from pg: A utility for executing SQL queries.

  2. Middleware:

    • cors: Configures Cross-Origin Resource Sharing to allow the API to be accessed from different domains.

    • express.json(): Middleware to parse incoming JSON requests.

  3. Routes:

    • Create a Todo (POST /todos):

      • Inserts a new todo into the database and returns the created todo.
    • Get All Todos (GET /todos):

      • Retrieves all todos from the database.
    • Get a Todo (GET /todos/:id):

      • Retrieves a specific todo based on the provided id parameter.
    • Update a Todo (PUT /todos/:id):

      • Updates the description of a specific todo based on the provided id parameter.
    • Delete a Todo (DELETE /todos/:id):

      • Deletes a specific todo based on the provided id parameter.
  4. Server Setup:

    • The server listens on port 4000, and a message is logged to the console when the server starts.

Current project structure:

Testing our APIs

Prerequisite:

Start server: run the command below to start the server.

nodemon index.js

Testing endpoints with Postman

  • Update: To edit a todo, we perform an HTTP PUT request to the endpoint, "http://localhost:4000/todos/2", we specify an id parameter, "2", and specify "description" as JSON in the body of the request.

  • DELETE: To access a todo, we perform a HTTP DELETE request to the endpoint, "http://localhost:4000/todod/2", and we specify an id parameter, "2".

Yes, you did it, well done! - our endpoints are functioning properly!

Conclusion

The article delves into the fundamental principles of RESTful API design, emphasizing scalability, statelessness, and adherence to a set of core principles. Concepts like resources, HTTP methods (GET, POST, PUT, DELETE), statelessness, and representation are explored to provide a comprehensive understanding.

The practical implementation includes the creation of a connection to a PostgreSQL database through the "db.js" file, which exports a well-configured PostgreSQL connection pool instance. The code intricately details the import of the pg.Pool class, the configuration of the connection pool with specific database details, and the subsequent export of the pool instance for broader application use.

The article proceeds to establish an entry point for the server in the "index.js" file, leveraging the Express.js framework. This setup incorporates middleware for CORS and JSON parsing, defines routes for CRUD operations on a "todo" resource, and seamlessly interacts with the PostgreSQL database using the exported connection pool instance.

The provided code not only demonstrates the implementation of a robust RESTful API for todo management but also elucidates endpoints for creating, retrieving, updating, and deleting todo items.

Continue to the next part of the series

ย