Monday, July 29, 2024

PostgreSQL

I suggest PostgreSQL when someone asks for the recommendation on what database one should use. I use PostgreSQL when I am learning or building something in a new web framework or library. I use PostgreSQL when I want to experiment with databases. PostgreSQL is the default choice for me in most of the cases.

Every few months, I change the operating system from one GNU/Linux distro to another mostly staying with the land of Debian (e.g. apt). Most of the time, I just need to run the following command to install PostgreSQL.

apt install postgresql

This command install and activates the PostgreSQL server locally on my computer. I use the psql command tool to work with this database server.

Following are the commands I use to work with PostgreSQL.

sudo -i -u postgres
psql

Although technically it is not correct, I read the first command as sudo imagine user as postgres to provide access. After entering the password, the user changes from the current logged-in user to postgres and then I can directly work with PostgreSQL using the second psql command.

We can combine the above two commands into a single one as follows.

sudo -iu postgres psql

Again, technically it is not correct, but I read it as sudo imagine users as postgres and then launch psql directly.

The first thing I normally do after connecting with PostgreSQL is to change the password. This is trivial as running the following command.

password \postgres

We are changing the password for the postgres user. After running this command, PostgreSQL asks you to enter and re-enter a new password for the postgres user. And then you are ready to go!

---

If somehow, you are facing difficulties installing PostgreSQL on your computer, installing via Docker is yet another option. At some convenient place, save the docker-compose.yml file with following content.

version: '3'

services:
  db:
    image: postgres
    restart: always
    ports:
      - '5432:5432'
    environment:
      POSTGRES_PASSWORD: pq2q4a

You can choose any other password you like instead of pq2q4a!

Once done, run the following command in the terminal where you have this docker-compose.yml file.

docker-compose up -d

This will run the docker-compose.yml file and up the PostgreSQL service in detached mode. Meaning that it'll run the PostgreSQL service in the background once the docker is running. When you start the computer, if Docker is not running, you just need to run the docker and it'll auto run the PostgreSQL service for you.

Thursday, July 25, 2024

કરસનદાસ મૂળજીનું પ્રવાસ પુસ્તક

 

ઈંગલેન્ડમાં પ્રવાસ પુસ્તકનું પૂઠું

આમ જોવા જઈએ તો "લેખક" આવે પણ અહીં "કરનાર" લખ્યું છે. પણ, એ વધુ બંધ બેસે છે કે, પ્રવાસ કરનાર કરસનદાસ મુલજી.

એક મિત્ર એ કીધું એ પણ ખરું કે પહેલાના લોકો પ્રેક્ટિકલ હતા.

Friday, June 21, 2024

Sails Tutorial — Chapter 3

Note: To follow this article, you need to install an API client to test the routes that we are going to create in this article. Try Postman or Insomnia if you haven’t one.

In this article, we are going to create the APIs for the Contact resource. If you are building an MVC web app, then you need to create seven routes whereas if you are creating an API, you need to create five routes.

To start with, let’s add a route that returns all the contacts as GET /contacts. When this route is requested, the server should run the index action of ContactsController in the config/routes.js file.

module.exports.routes = {
  "GET /": "HomeController.index",

  // Contacts
  "GET /contacts": "ContactsController.index",
};

We don’t have this ContactsController.js file yet! Let’s create one in the api/controllers folder.

touch api/controllers/ContactsController.js

Open this ContactsController.js file and write the following code.

module.exports = {
  index: (req, res) => {
    res.json({ message: "All contacts" });
  },
};

Open the http://localhost:1337/contacts in the browser and you should see this JSON response.

Now, we need to add a route for a specific contact as GET /contacts/:id. When this route is requested, the server should run the show action of ContactsController in routes.js file.

module.exports.routes = {
  "GET /": "HomeController.index",

  // Contacts
  "GET /contacts": "ContactsController.index",
  "GET /contacts/:id": "ContactsController.show",
};

Let’s define this show action within ContactsController.js file and also print the passed id value using req.params.id.

module.exports = {
  index: (req, res) => {
    res.json({ message: "All contacts" });
  },

  show: (req, res) => {
    console.log(`contact id is ${req.params.id}`);
    res.json({ message: "Single contact" });
  },
};

Open the http://localhost:1337/contacts/1 in the browser and you should see this JSON response. You should also see the console message as contact id is 1 in the terminal.

Next, we need to add a route that creates a new contact by passing first name and last name as firstName and lastName respectively. We can define this route as POST /contacts and map against the create action of ContactsController.

module.exports.routes = {
  "GET /": "HomeController.index",

  // Contacts
  "GET /contacts": "ContactsController.index",
  "GET /contacts/:id": "ContactsController.show",
  "POST /contacts": "ContactsController.create",
};

Let’s define this create action within ContactsController.js file and also print the passed firstName and lastName values using req.body.firstName and req.body.lastName (you can destructure in single line, if you want).

module.exports = {
  index: (req, res) => {
    res.json({ message: "All contacts" });
  },

  show: (req, res) => {
    console.log(`contact id is ${req.params.id}`);
    res.json({ message: "Single contact" });
  },

  create: (req, res) => {
    console.log(`first name is ${req.body.firstName}`);
    console.log(`last name is ${req.body.lastName}`);
    res.json({ message: "Creating contact" });
  },
};

To test this route, we need to do a POST api call in Insomnia (or any other API client you are using). You should get the above JSON response when calling this route and first name & last name should be consoled in the terminal.

Before we go further and add route for updating a resource, let’s do a status code modification in create route as 201 should be the status code when creating a resource. We know how to do it as we’re going to use Express’s .status() method.

module.exports = {
  index: (req, res) => {
    res.json({ message: "All contacts" });
  },

  show: (req, res) => {
    console.log(`contact id is ${req.params.id}`);
    res.json({ message: "Single contact" });
  },

  create: (req, res) => {
    console.log(`first name is ${req.body.firstName}`);
    console.log(`last name is ${req.body.lastName}`);
    res.status(201);
    res.json({ message: "Creating contact" });
  },
};

Much better! Just confirm in Insomnia that you should get 201 status code in response (You can chain .status() and .json() methods in single line, if you want).

Let’s define a route to update a specific contact as PUT /contacts/:id. When this route is requested, the server should run the update action of ContactsController.

module.exports.routes = {
  "GET /": "HomeController.index",

  // Contacts
  "GET /contacts": "ContactsController.index",
  "GET /contacts/:id": "ContactsController.show",
  "POST /contacts": "ContactsController.create",
  "PUT /contacts/:id": "ContactsController.update",
};

Let’s define this update action within the ContactsController.js file. Let’s also print the id as req.params.id and passed firstName & lastName values as req.body.firstName and req.body.lastName (you can also destructure, if you want).

module.exports = {
  index: (req, res) => {
    res.json({ message: "All contacts" });
  },

  show: (req, res) => {
    console.log(`contact id is ${req.params.id}`);
    res.json({ message: "Single contact" });
  },

  create: (req, res) => {
    console.log(`first name is ${req.body.firstName}`);
    console.log(`last name is ${req.body.lastName}`);
    res.status(201);
    res.json({ message: "Creating contact" });
  },

  update: (req, res) => {
    console.log(`contact id is ${req.params.id}`);
    console.log(`first name is ${req.body.firstName}`);
    console.log(`last name is ${req.body.lastName}`);
    res.json({ message: "Updating contact" });
  },
};

We need to test this route in Insomnia. You should get the above JSON response when calling this route and id, firstName & lastName should be consoled in the terminal.

Finally, we need to add a route that deletes a specific contact as DELETE /contacts/:id and should run the destroy action of ContactsController. delete is a reserved keyword in JavaScript. Due to this, we choose destroy as function name.

module.exports.routes = {
  "GET /": "HomeController.index",

  // Contacts
  "GET /contacts": "ContactsController.index",
  "GET /contacts/:id": "ContactsController.show",
  "POST /contacts": "ContactsController.create",
  "PUT /contacts/:id": "ContactsController.update",
  "DELETE /contacts/:id": "ContactsController.destroy",
};

Let’s define this destroy action within ContactsController.js file and also print the id as req.params.id.

module.exports = {
  index: (req, res) => {
    res.json({ message: "All contacts" });
  },

  show: (req, res) => {
    console.log(`contact id is ${req.params.id}`);
    res.json({ message: "Single contact" });
  },

  create: (req, res) => {
    console.log(`first name is ${req.body.firstName}`);
    console.log(`last name is ${req.body.lastName}`);
    res.status(201);
    res.json({ message: "Creating contact" });
  },

  update: (req, res) => {
    console.log(`contact id is ${req.params.id}`);
    console.log(`first name is ${req.body.firstName}`);
    console.log(`last name is ${req.body.lastName}`);
    res.json({ message: "Updating contact" });
  },

  destroy: (req, res) => {
    console.log(`contact id is ${req.params.id}`);
    res.json({ message: "Deleting contact" });
  },
};

And now we have the stub end-points for the given resource e.g. Contact with best practice to create REST APIs.


Thursday, March 7, 2024

Go: Get Started Pt. 1

 Create a folder with name of the project e.g. project-name.

$ mkdir project-name

Go inside the created project-name folder.

$ cd project-name

Create a module or mod file using following command.

$ go mod init project-name

This command will create go.mod file. This file is the manifest file of the project that contains info of your project such as project name, Go version, dependencies of the project and so on. Following is the content as of now we’ve in this go.mod file.

module go-example 

go 1.21.5

We don’t have any dependency related information in this file yet. Because, our project yet not consuming any third-party package.

Create a file with name main.go and write following code in it.


package main


import "net/http"

import "github.com/gin-gonic/gin"


func main() {

  r := gin.Default()

  r.GET("/", func (c *gin.Context) {

    c.JSON(http.StatusOK, gin.H{

      "message": "hello, world",

    })

  })

  r.Run()

}


This is the simple hello, world program in Gin framework. Notice that we’re importing Gin framework from github.com/gin-gonic/gin. But, we haven’t installed this package yet. In order to install the package, we need to run following command.

$ go mod tidy

This command will look for the dependencies used in this project and download it for us. After successful completion of the command, if you look over the go.mod file again, it looks like this.

module project-name

go 1.21.5

require github.com/gin-gonic/gin v1.9.1

require (
  github.com/bytedance/sonic v1.9.1 // indirect
  github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
  github.com/gabriel-vasile/mimetype v1.4.2 // indirect
  github.com/gin-contrib/sse v0.1.0 // indirect
  github.com/go-playground/locales v0.14.1 // indirect
  github.com/go-playground/universal-translator v0.18.1 // indirect
  github.com/go-playground/validator/v10 v10.14.0 // indirect
  github.com/goccy/go-json v0.10.2 // indirect
  github.com/json-iterator/go v1.1.12 // indirect
  github.com/klauspost/cpuid/v2 v2.2.4 // indirect
  github.com/leodido/go-urn v1.2.4 // indirect
  github.com/mattn/go-isatty v0.0.19 // indirect
  github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
  github.com/modern-go/reflect2 v1.0.2 // indirect
  github.com/pelletier/go-toml/v2 v2.0.8 // indirect
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
  github.com/ugorji/go/codec v1.2.11 // indirect
  golang.org/x/arch v0.3.0 // indirect
  golang.org/x/crypto v0.9.0 // indirect
  golang.org/x/net v0.10.0 // indirect
  golang.org/x/sys v0.8.0 // indirect
  golang.org/x/text v0.9.0 // indirect
  google.golang.org/protobuf v1.30.0 // indirect
  gopkg.in/yaml.v3 v3.0.1 // indirect
)

On line number 5, there we’ve the dependency. Remaining are the indirect dependency as they end with comment // indirect meaning that these dependencies are not directly consume by the project. But, project’s dependency are depends on it.

Finally, you should see a file with name go.sum. This file is auto generated by Go for the dependencies with checksum to figure out whether to download the dependencies again or not. Let’s not worry about this file as it’ll be created, updated, and managed by Go for us.

Tuesday, February 6, 2024

સારા ભાષાંતરના ગુણ

સારા ભાષાંતરમાં નીચેના ગુણ હોવા જોઈએ:

  1. એ જાણે સ્વભાષામાં જ વિચારાયું અને લખાયું છે તેવું સહજ અને સરળ હોવુ જોઈએ. જે ભાષામાંથી ઉતારાયું હોયતે ભાષાના રૂઢીપ્રયોગો અને શબ્દોના વિશેષ અર્થો ન જાણનાર એને સમજી ન શકે એવું તે ન હોવું જોઈએ.
  2. ભાષાંતરકારે જાણે મૂળ પુસ્તકને પી જઈને તથા પચાવીને એને ફરીથી સ્વભાષામાં ઉપજાવ્યું હોય તેવી કૃતિ લાગવી જોઇએ.
  3. આથી સ્વતંત્ર પુસ્તક કરતા ભાષાંતર કરવાનું કામ હંમેશા સહેલુ નથી હોતું. મૂળ લેખક સાથે જે પૂરેપૂરો સમભાવી અને એકરસ થઈ શકે નહીં અને તેના મનોગતને પકડી લે નહીં, તેણે તેનું ભાષાંતર ન કરવું જોઈએ.
  4. ભાષાંતર કરવામાં જુદી જુદી જાતનો વિવેક રાખવો જોઈએ. કેટલાંક પુસ્તકોનું અક્ષરશઃ ભાષાંતર કરવું આવશ્યક ગણાય, કેટલાંકનો માત્ર સાર આપી દેવો બસ ગણાય તો કેટલાંક પુસ્તકોનાં ભાષાંતર સ્વ સમાજને સમજાય એ રીતે વેશાંતર કરીને જ આપવાં જોઈએ. કેટલાંક પુસ્તકો તે ભાષાના ઉત્કૃષ્ટ હોવા છતાં પોતાનો સમાજ અતિશય જુદા પ્રકારનો હોવાથી તેના ભાષાંતરની સ્વભાષામાં જરૂર જ ન હોય; અને કેટલાંક પુસ્તકોના અક્ષરશઃ ભાષાંતર ઉપરાંત સારરૂપ ભાષાંતરની પણ જરૂર ગણાય.

~ મોહનદાસ કરમચંદ ગાંધી 

Monday, February 5, 2024

The Law Of Complexity

Complexity never eliminate,
it just move from one place to another.


When you're not writing middleware to log requests, someone else wrote or is writing middleware for you. If you think that you can easily write a single page application in a given framework or library then understand that it depends on 1000+ modules to make it easy and the complexity is divided between these 1000+ modules but not eliminated.
 

Sunday, February 4, 2024

Day 2 - Lisp Learning

Going forward with learning, defvar is used to define variables. Continue with last program written in hello.lisp. Here is how I've defined the hello, world program again with variable.

(defvar greetings "hello, world")


(format t greetings)

I read many places that variables are defined with star(*) around it to mark as global variable and * is valid character to define a variable.

(defvar *greetings* "hello, world")

(format t *greetings*)


Not just * but hyphen(-) can be used to define multi-words variables and this is recommended instead of camelCase or snake_case.

(defvar hello-world "hello, world")

(format t hello-world)

Frankly speaking I haven't seen a program like above anywhere else (defining a variable and then using it in next line without context). May be in functional programming, most of the variables are bound to the scope and only defined where needed.

But, defvar is used to define the variable in Common Lisp.

Saturday, February 3, 2024

Day 1 - Lisp Learning

In my free time (which I have very less), I'm learning Lisp programming language. After doing some search, I decided to go with Common Lisp and installed sbcl.

Unless you're using Emacs, do not write multiple lines of code in REPL (in Terminal). Most of the terminals don't support paren auto-closing and highlight. Due to this, you might frustrate.

I'm using GNU/Linux machine. Following command help me to install the sbcl on my computer.

sudo apt install sbcl

After installation, check for the version and you can confirm the installation.

sbcl --version

Write only sbcl and it'll open the true REPL for you. In order quit or exit the REPL you need to write (quit).

You can directly write 10 or "hello, world" (use double quotes) as these are the literal values.

* 10
10
* "hello, world"
"hello, world"


If you try to write 10 as (10) or "hello, world" as ("hello, world") you'll get errors. Because, in simple terms, after open paren, Lisp expect to have something that can execute. For example, (+ 3 4) works as + will execute on 3 and 4 values.

Here is the hello, world in Common Lisp.

* (format t "hello, world")

Here, format print on terminal (actually it is standard output) "hello, world" string. You can write code within file that can have .lisp extension. Then you can run it as,

sbcl --script hello.lisp

or Open sbcl REPL and load the file.

sbcl
* (load "hello.lisp")
 

Friday, January 19, 2024

Sails Tutorial — Chapter 2

Application is up and running at http://localhost:1337 but it throws a 404 error. Let’s fix it by adding a root route. To do so, first create a config folder and then routes.js file within config folder.

mkdir config
touch config/routes.js

Open the config/routes.js file and write the following code.

module.exports.routes = {};

It exports the routes object that will have all the routes of the application.

Go ahead define a root route or GET / as follows.

module.exports.routes = {
  'GET /': 'HomeController.index',
};

Meaning that when the root route (/) is requested, server run the index action (or method) from the HomeController. But, we don’t have controller and action.

Go ahead and create a folder with the name api. Within this api folder, create a folder with name controllers. In this folder, we’ll put all the application controllers.

mkdir api
mkdir api/controllers

Finally, within this controllers folder, create a file with the name HelloController.js. The name of the file should be the same as the controller name defined in routes.js file.

touch api/controllers/HomeController.js

Open the api/controllers/HomeController.js file and write the following code.

module.exports = {};

It exports the object that will have all the actions we are interested in calling from this controller.

Sails internally uses Express framework. So, the actions within the controller are essentially the callback function we usually write in Express for a given route with req and res (and next) parameters.

Let’s define an index as a named function that returns a sample JSON response.

module.exports = {
  index: (req, res) => {
    res.json({ message: 'Home page' });
  },
};

That’s it! Reload (or open) the http://localhost:1337 in the browser. You should now see a JSON response with the message { "message": "Home page" } instead of a 404 error page.


Tuesday, October 24, 2023

Express.js Code Reading

Following are the list of the points that I find interesting while reading Express code (I'm still reading the source code).

  • Express 4.x support Node v0.10. Due to this, code is not written in ES6.
  • 'use strict' increase the performance.
  • When you write app.set('view engine', 'something'), Express will check something module in node_modules and load it. EJS use this trick and due to this, you just have write this line only. Even you don't have to import ejs (or something in example).
  • All the repos under Express org on GitHub has either index.js if the package is consist of only one file or the package code is within lib folder.

In progress. I'll add more points as I learn more.

Sunday, October 22, 2023

PostgreSQL Tutorial Pt. 1

Run the following command in the terminal.

$ sudo -i -u postgres

This command is used to open a shell session as the user "postgres" with an environment set up as if "postgres" had logged in directly. This is often useful when you need to perform administrative tasks or run commands specific to the PostgreSQL database system, which typically runs with a dedicated user account for security reasons.

Now, write psql command in terminal to open the command-line interface to PostgreSQL.

$ psql

We now have the PostgreSQL open in our terminal.

To fetch the all database, following command is used.

$ \l
                                    List of databases
          Name           |  Owner   | Encoding | Collate | Ctype |   Access privileges   
-------------------------+----------+----------+---------+-------+-----------------------
 abc_development         | postgres | UTF8     | en_IN   | en_IN | 
 exp_development         | postgres | UTF8     | en_IN   | en_IN | 
 postgres                | postgres | UTF8     | en_IN   | en_IN | 
 template0               | postgres | UTF8     | en_IN   | en_IN | =c/postgres          +
                         |          |          |         |       | postgres=CTc/postgres
 template1               | postgres | UTF8     | en_IN   | en_IN | =c/postgres          +
                         |          |          |         |       | postgres=CTc/postgres
(7 rows)

To connect with specific database, use \c followed by the database name.

$ \c abc_development
You are now connected to database "abc_development" as user "postgres".

We're now connected with the abc_development database.

To list all the tables within given database, following command is used.

$ \dt
Did not find any relations.

We get the message "Did not find any relations." meaning that no table exists as of now in this database.

We can create the table using following command (do not run this command yet).

create table products (
  id int primary key,
  name varchar(256) not null 
);

We're trying to add id as primary key and name as the another string column. But, this query has one problem - id is not auto increment for us by default. To have it that feature, we need to defined it as serial than int like this (now you can run this command).

create table products (
  id serial primary key,
  name varchar(256) not null 
);

With this the products table is created.

Let's write a simple insert query to confirm the usage of the table.

insert into products (name) values ('Neem plant');

Finally, let's confirm that the product is added successfully in table by writing the select query.

select * from products;
 id |     name     
----+--------------
  1 | Neem plant
(1 row)

Friday, August 18, 2023

Sails Tutorial — Chapter 1

We are going to build the sample Sails.js or Sails application from the scratch with some unusual steps (for learning). We will end up with a small, functional CRM application.. Without further ado, let’s get started.

Create a folder with name crm.

mkdir crm

Go inside the created crm folder.

cd crm

Create a package.json file with defaults (-y).

npm init -y

Install the Sails framework (sails) as the dependency in this project.

npm i -E sails

Also install the Nodemon (nodemon) to restart the server on changes as development dependency (-D).

npm i  -E -D nodemon

Go ahead and create a new file with name app.js.

touch app.js

Open this created app.js file and write the following code.

const sails = require("sails");

sails.lift();

We first required the sails package and then called the .lift method. That’s it (in terms of code setup to use the Sails)!

Open package.json file and add two scripts — one to run app.js with node and second to run app.js with nodemon (I will add dev first and then start to write the scripts in alphabetic order).

{
  "name": "crm",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "sails": "1.5.7"
  },
  "devDependencies": {
    "nodemon": "3.0.1"
  }
}

Also, change the main file from index.js to app.js.

{
  "name": "crm",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "sails": "1.5.7"
  },
  "devDependencies": {
    "nodemon": "3.0.1"
  }
}

In the terminal, run the dev script.

npm run dev

The application is running at http://localhost:1337. Open this URL in the browser and you should see ‘Not Found’ with 404 status code. This is obvious as we haven’t write any code for the root route. But, the important thing is you have the Sails application up and running with just two lines of code in app.js file. Rest of the things were setup!


Tuesday, July 11, 2023

Object Based Routing in Express.js

On the other day, I was looking over the source code of the Sails.js framework. Sails.js framework has a nice routing configuration based on the routes.js file. In this file, you can have the exported object in the following way.

module.exports = {
  'get /': 'HomeController.index',
  'get /pages/contact': 'PagesController.contact',
  'post /users': 'UsersController.create',
};

When the user visit GET /, the framework maps the HomeController.js file from the controllers folder and executes the index method or an action (as in terms of MVC). The key in the above object is HTTP (GET) verb, followed by a space, and then route(/) and the value (of the object key) is the file name without extension(HomeController) followed by dot(.) and then action name(index). Nice and clean mapping!

Sails.js framework is based on Express. In other words, the above code at the end should be converted into standard app.get(), app.post(), etc. methods. So, I thought about the possibility of adding this object-based routing in a simple Express application. Rest of the article is walk-through on how one can add. I do not yet recommend trying this in a production application.


Create a folder with the name routing-object. Go inside, and then create a package.json file to mark this folder as a package/module.

$ mkdir routing-object
$ cd routing-object
$ npm init -y

Install the Express(express) as the production dependency and Nodemon(nodemon) as the development dependency(-D).

$ npm i express
$ npm i -D nodemon

Nodemon is used in development mode to restart the server when we do the changes. Let's add the dev and start scripts in package.json file to run the application in development and production mode respectively.

{
  "name": "routing-object",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "4.18.2"
  },
  "devDependencies": {
    "nodemon": "3.0.0"
  }
}

Finally, create an app.js file and write the following skeleton Express code.

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

We can easily run this application in development mode using dev script.

$ npm run dev
[nodemon] starting `node app.js`
Application is up and running on port 3000

With these changes, the base Express application is up and running.


Let's add three routes GET /, GET /pages/contact, and POST /users as mentioned in the object at starting of this article. I'll just return simple messages for all these routes.

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.json({ message: "home page" });
});

app.get("/pages/contact", (req, res) => {
  res.json({ message: "contact us" });
});

app.post("/users", (req, res) => {
  res.json({ message: "user created" });
});

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

Test these routes in the browser and/or Insomnia.

With these changes, our application is now supporting the above-mentioned routes.

Now, we're going to do the changes in the application in such a way that it should not affect the output but the structure of the application.


Go ahead and create a routes.js file in the route folder.

$ touch routes.js

Open this file and copy the object mentioned in this article at the beginning.

module.exports = {
  "get /": "HomeController.index",
  "get /pages/contact": "PagesController.contact",
  "post /users": "UsersController.create",
};

The plan is when the user called POST /users, we should look for the file name UserController.js. Even further we'll look for this file in the controllers folder. So, let's create the controllers folder first in the root folder.

$ mkdir controllers

In this folder, based on the above routes object, we need to create three files - HomeController.js, PagesController.js, and UserController.js.

$ touch controllers/HomeController.js
$ touch controllers/PagesController.js
$ touch controllers/UsersController.js

HomeController.js should have the index method.

module.exports = {
  index: () => {

  },
};

When the user visit GET /, this index method ultimately runs. In other words, if we map the following code with the above code,

app.get("/", (req, res) => {
  res.json({ message: "home page" });
});

Then

(req, res) => {
  res.json({ message: "home page" });
}

should be within the index method in HomeController.js file as follow.

module.exports = {
  index: (req, res) => {
    res.json({ message: "home page" });
  }
}

Similarly, PagesController.js should have the contact method.

module.exports = {
  contact: (req, res) => {
    res.json({ message: "contact us" });
  }
}

And finally, the UsersController.js file should have the create method.

module.exports = {
  create: (req, res) => {
    res.json({ message: "user created" });
  },
};

With these changes, the routes are ready and the associated code is implemented. Next, we need to map these two.


In the app.js file, go ahead and remove three routes code as we have that code in other places.

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

To call the method from the files or controller's files, we need to first import these files. For this, we can use require-all npm package. This package requires all the files and makes them available for use to directly call. Go ahead stop the server and install this package.

$ npm i require-all

As mentioned in the documentation of this package, we can write the following code.

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

This controllers object now has reference to all the modules or files within controllers folder. To call index method from HomeController we can simply write

controllers.HomeController.index()

Let's use the require-all package in the app.js file and require all the controllers.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

With these changes, we just figure out what to do with the values of the routes object. Next, we need to fetch the routes from routes.js file and attach the respected method to each of the routes by loop through.


Let's simply require the routes.js file to fetch all the routes.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

We're going to use for...in loop to loop through the object.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

The route is the key and it contains two things - HTTP verb and route itself. We need to fetch both the details from route. To fetch the HTTP verb, we can simply use the RegEx as follow.

const verbExpr = /^(get|post|put|delete)\s+/;
const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

The first line is the RegEx for four HTTP verbs and the second line check for the verbs and return and saved them into the verb variable.

Let's add these lines to our app.js file.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

Next, we need to fetch the route itself. Fetching the route is as simple as replacing the verb with an empty string as whatever remains must be the route.

let path = route;
path = path.replace(verbExpr, "");

Instead of directly changing the route iterator, I used the intermediate path variable and saved the route in it.

Let's add these lines to our app.js file.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

  let path = route;
  path = path.replace(verbExpr, "");
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

We now have all the needed code but in de-composed mode.


Even though all the methods within controllers are available in the controllers object, we can not dynamically call them. We need to save the controllers and methods separately to call.

To save the controllers and method from object value, we can simply split by dot(.).

let location = routes[key];
location = location.split('.');
let controllerLocation = location[0];
let methodLocation = location[1];

The above code saves the value of the route object in location and we split it by dot(.). With this, we can have the controller in controllerLocation and the method in methodLocation variables in callable form.

Let's add these lines to our app.js file.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

  let path = route;
  path = path.replace(verbExpr, "");

  let location = routes[key];
  location = location.split('.');
  let controllerLocation = location[0];
  let methodLocation = location[1];
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

The exact callable form is within controllers object. So, let's fetch specific controllers and methods from the controller per route.

let controller = controllers[controllerLocation];
let method = controller[methodLocation];

Let's add these lines to our app.js file.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

  let path = route;
  path = path.replace(verbExpr, "");

  let location = routes[key];
  location = location.split('.');
  let controllerLocation = location[0];
  let methodLocation = location[1];

  let controller = controllers[controllerLocation];
  let method = controller[methodLocation];
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

With these changes, we now have the verb, route, and method to call.


The only code we now need to write is,

app[verb](path, method);

The above code attaches .get() or .post() method to app and the path and method as the argument to it.

Let's add these lines in our app.js file to complete the working Express application.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

  let path = route;
  path = path.replace(verbExpr, "");

  let location = routes[key];
  location = location.split('.');
  let controllerLocation = location[0];
  let methodLocation = location[1];

  let controller = controllers[controllerLocation];
  let method = controller[methodLocation];

  app[verb](path, method);
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

Go ahead and re-run all three routes in the browser and/or Insomnia. You should see the identical output.

The above code is not complete. I intentionally left many scenarios and improvements. For example,

  1. You can add all the HTTP verbs in verbExpr and even do case-insensitive matching.
  2. You can place this code into a separate file.
  3. You can have better names for the variables.
  4. You can even implement your require-all package as this package is quite small as one file only.
  5. You can add error checking as what if someone forgot to add. between controller and method name of added tab between HTTP verb and route instead of single space, etc.