Friday, March 28, 2025

Catchy Advice, Boring Advice

On the other day, I was reading a post on a social network where a person who went to an interview for the position of (junior) developer asked a question at the end of it: how one can become a senior developer. He was very impressed by the answer of the interviewer. The answer was,

Being a senior developer is about how to handle people, how to teach them, how to guide and mentor them. You’ll have a group of juniors under you with different mindsets. You need to know their mindset and behave accordingly…

…blah blah blah. This answer may look good. You may be impressed by the answer. But wait! Does that mean if you are good at people management and can mentor them, then you’re a senior developer? I mean, where is the point of being very good or an expert at development to become a senior developer? Where is the point where you need to be good at estimation, implementation planning, knowing risks in the technical decisions, business values, etc.? Aren’t those needed to become a senior developer, or is just people management and mentoring enough?

On the pretense of saying something that looks catchy, they’re ignoring the base. Or are they assuming the base when giving a shining opinion to impress people? Like, being good at development is the base to become a senior developer, isn’t it?

I remember watching an interview where the interviewer mentioned that focus was the reason for Bill Gate’s success. Then someone suggested that curiosity was the reason why he was successful. These are absolutely correct reasons to be successful. But, does that means if I sharpen my focus on something for half an hour every day, that I’ll become successful? No. One must need to work hard as the base. Focusing on one thing is very important, but hard work is essential. Why are people not talking about hard work nowadays? Is it because talking about hard work is boring? I don’t know.

Wednesday, March 26, 2025

Welcome to Go

Go or Golang is a static, inferred, structural programming language created by Robert Griesemer, Rob Pike, and Ken Thompson initially at Google. It has built-in support for concurrency, testing, and has rich standard library.

Go is extremely easy to dive into. There are a minimal number of fundamental language concepts and the syntax is clean and designed to be clear and unambiguous. - Dave Astels.

Install Go

Let’s first install the Golang. You can find the release binary from the official download page and instructions in the official installation guide.

As I’m using GNU/Linux system, I need to follow these steps. The steps for the GNU/Linux are written so clearly that I follow these steps even I want to install something else from given .tar.gz file.

# unzip the .tar.gz file at /usr/local
tar -C /usr/local -xzf go1.24.1.linux-amd64.tar.gz
# Add the bin path to access go command
export PATH=$PATH:/usr/local/go/bin
# Test
go version

Let’s write the hello, world program. Create a file with name hello.go and write the following code.

package main

import "fmt"

func main() {
    fmt.Println("hello, world")
}

Run the following command in the terminal to run this program.

go run hello.go
hello, world

Explanation of the hello, world Program

  1. You can package your code by any name you want to arrange your code logically. But, package main is the special package that should contain main() function. We’ll talk about package stuff in future if it looks confusing to you. For now, just add package main at the top of the file. We we write something else as name of the package than main, you’ll know the meaning of it.
  2. import is used to import the library or package. Here, we are importing fmt standard library to access the Println() function.
  3. Execution of the program starts from main() function. func keyword is used to define function.
  4. Println() function is used to print text in the terminal. Println() is part of fmt. Remember that P is capital in Println() function.

Variables

There are multiple ways to define variables and assign values to them. As Go is static programming language, type is bound to the variable than the value. What value a variable can accept is depends on type it bound to.

To define a variable following syntax is used. var keyword, followed by name of the variable, and finally the type of the variable.

var x int

As the type of x is int, it can only accept valid integer values. Assigning floating number of string results in error.

Boring = or assignment operator is used to assign value to the variable.

var x int = 42

If you are assigning the value to the variable at the time of defining it, you can drop the type from the syntax as Go will inter the type based on initial value.

var x = 42

Even further, replace boring = with interesting := operator and you don’t have to write var keyword.

x := 42

And now you know couple of ways to define variable in Go. Use the syntax that make more sense to you in terms of program context and usage.

Go is rich in types. Following are some the basic data types Go has supports for.

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8

rune // alias for int32
     // represents a Unicode code point

float32 float64

complex64 complex128

nil

You don’t have to remember all these types! You’ll get to know and use these types as you get more experience with Go.

Here is an example program that demonstrate the usage of some of the types from above list.

package main

import "fmt"

func main() {
    var name string = "Peter"           // String
    var age int = 32                    // Integer
    var height float64 = 190.47         // Float64
    var adult bool = true               // Boolean
    var address *string = nil           // Nil pointer

    fmt.Println(name, age, height, adult, address)
}

Output is:

Peter 32 190.47 true <nil>

In Go, variables can be reassigned and are mutable by default. Unlike current tend, Go doesn’t enforce immutability by default.

age := 32
age = 42

fmt.Println(age)

Output is:

42

In Go, if you declare a variable but don’t use it, the compiler will give an error. To ignore values and fix the compiler error, use the blank identifier _:

_ = 12
result := 12 + 10
_ = result // If you're not going to use result

String Interpolation and Concatenation

Go uses the fmt package for string formatting and the + operator for concatenation. Go use Printf() function with format specifier to do the string interpolation.

package main

import "fmt"

func main() {
    name := "Bruce"
    age  := 32

    fmt.Printf("I am %s and %d years old\n", name, age)
    fmt.Println("But my real name is " + "Batman ЁЯжЗ")
}

Output is:

I am Bruce and 32 years old
But my real name is Batman ЁЯжЗ

Finally, following are the list of popular projects created in Golang.

  1. Kubernetes
  2. Docker
  3. Prometheus
  4. Micro Editor
  5. Grafana
  6. CockroachDB

Acknowledgement: This article is heavily inspired by content from RunElixir.com.


Monday, January 13, 2025

You Are Using Old Technology

viewpoint may be largely unrelated to its usefulness. Many if not most of the software we use is probably obsolete according to the latest design criteria. Most users could probably care less if the internals of the operating system they use is obsolete. They are rightly more interested in its performance and capabilities at the user level. - Ken Thompson, The Tanenbaum-Torvalds Debate

At the moment when you start using cutting-edge technology, it quickly becomes outdated. At the same time, someone is fixing a bug, making your cutting-edge technology one patch version old. Someone else might be adding a new feature, making your cutting-edge technology one minor version old. Or, someone could have a grand plan to improve it, making your cutting-edge technology one major version old, or even deprecated! In other words, as soon as you use a particular technology, it is already old. What I’m saying is, you’re always using old technology.

Sunday, January 5, 2025

Elixir Pt. 4

We are continuing our journey with composite data types, and in this article, we briefly touch on the next composite data type: Tuples.

Tuples are defined using {} and can hold any type of value.

Go ahead and create a file named tuple.ex. Open this file in your favorite text editor and write the following code in it:

tuple1 = {3, "hi"}
tuple2 = {0, {1, false}, 2.0, "three"}

IO.inspect(tuple1)
IO.inspect(tuple2)

Let’s run this program, and then I’ll explain. Use the following command to run the program:

$ elixir tuple.ex 
{3, "hi"}
{0, {1, false}, 2.0, "three"}

Explanation

  • The first two lines define the tuples tuple1 and tuple2, each containing values of different types. Even further, tuple2 contains a tuple!
  • Finally, as we know, we cannot use the IO.puts function to print the composite data type values. Instead, we have used the same IO.inspect function to inspect and print the values of tuple1 and tuple2.

---

From a user’s (or developer’s) perspective, both lists and tuples store the same types of values (though not internally in the same way). However, if you want to save a small number of values, such as two, three, or five, use tuples. For all other scenarios, use lists.

Saturday, January 4, 2025

Elixir Pt. 3

Now, you know a few primitive data types such as Integer, Floating-point numbers, Boolean, and String. Let’s move on and talk about a few composite data types, starting with Lists.

Lists are defined using [] and can hold any type of value.

Go ahead and create a file named list.ex. Open this file in your favorite text editor and write the following code in it:

list1 = [1, true, 3, "four", 5.0]
list2 = [ [0, 1], 1, 2.0]

IO.inspect(list1)
IO.inspect(list2)

Let’s run this program, and then I’ll explain. Use the following command to run the program:

$ elixir list.ex 
[1, true, 3, "four", 5.0]
[[0, 1], 1, 2.0]

Explanation

  • The first two lines define the lists list1 and list2, each containing values of different types. Even further, list2 contains a list!
  • Here, we also define true and 5.0. These are examples of the Boolean and Floating-point data types in Elixir.
  • Finally, we cannot directly print the List data type values using the IO.puts function. That’s why we use a specialized function IO.inspect to inspect and print the values of list1 and list2.

Friday, January 3, 2025

Elixir Pt. 2

Elixir is a dynamic programming language. Assign the value to a variable, and you’re good to go.

Go ahead and create a new file named var.ex and write the following code in it:

name = "Bruce"
age = 26

IO.puts("Hi " <> name <> ".")
IO.puts("You're " <> Integer.to_string(age) <> " years old.")

Let’s run this program, and then I’ll explain. Use the following command to run the program:

$ elixir var.ex
Hi Bruce.
You're 26 years old.

Explanation

We’ve defined two variables, name and age, with initial values of Bruce and 26, respectively.

  • Bruce is string in Elixir and due to this, all the functions associated with type String are applicable to name variable.
  • 26 is integer in Elixir and due to this, all the the functions associated with type Integer are applicable to age variable.

In the last two lines, we’re printing text to the terminal.

  • In the first printing line, we’re greeting Bruce with “Hi Bruce.”. The <> is a binary concatenation operator used to combine strings.
  • In the second printing line, we’re printing the age message as “You’re 26 years old.”. Since age is an integer, we need to convert the number into a string. To do this, we use the to_string function from the Integer module.

---

Finally, there are two other primitive data types that I haven’t covered in this article which are Boolean and floating point numbers. Boolean has true and false value and floating numbers are numbers with decimal points.

Elixir Pt. 1

Elixir is a dynamic, functional programming language designed for building scalable and maintainable applications. It runs on the Erlang VM (BEAM), known for its low-latency, distributed, and fault-tolerant capabilities.

---

Go ahead and create a file with the name hello_world.ex. Open this file in your favorite text editor and write the following code in it:

IO.puts("hello, world")

Let’s run this program, and then I’ll explain. To run the program, use the following command (elixir followed by the name of the program):

$ elixir hello_world
hello, world

Explanation

IO.puts is a function in the Elixir programming language that prints a string to the terminal with a newline at the end.

  • IO is the module that contains the puts function.
  • puts is the function responsible for outputting the string.

Elixir is a functional programming language that organizes code into modules, and functions are defined inside these modules. In this case, IO is the module, and puts is the function that performs the printing.

Thursday, September 19, 2024

this in JavaScript

JavaScript object is a powerful data structure that contains both data and methods. Consider the following example.

const person = {
  firstName: "Kiran",
  lastName: "Chauhan",
  greet: function () {
    console.log("Hello from Kiran Chauhan");
  },
};

Here, person object contains firstName and lastName as data and greet() as method.

We use dot notation(.) or array notation([]) to access the data or call methods.

// Dot notation
person.firstName; // 'Kiran'
person.lastName;  // 'Chauhan'
person.greet();   // Hello from Kiran Chauhan

// Array notation
person["firstName"]; // 'Kiran'
person["lastName"];  // 'Chauhan'
person["greet"]();   // Hello from Kiran Chauhan

Most of the time, dot notation is enough to access data or call the methods unless we have more specific scenarios to work with.

Now, pay attention to the body of the greet() method.

const person = {
  firstName: "Kiran",
  lastName: "Chauhan",
  greet: function () {
    console.log("Hello from Kiran Chauhan");
  },
};

Console message contains “Kiran” and “Chauhan” as hard-coded values. We can easily replace these hard-coded values with firstName and lastName. But, firstName and lastName are not variables. Instead, they are the properties of the person object. So that, we can’t directly access them like variables. Even if we try to do so, JavaScript will throw errors.

To use the properties of the object within object, we need to use this keyword. this means this object or object at the hand or self or etc. In other words, if we want to use properties of the object within object, then we need to write this.firstName instead of firstName and this.lastName instead of lastName.

const person = {
  firstName: "Kiran",
  lastName: "Chauhan",
  greet: function () {
    console.log(`Hello from ${this.firstName} ${this.lastName}`);
  },
};

The output remains the same. But, we removed the hard-coded values and replaced by the appropriate properties.

person.firstName; // 'Kiran'
person.lastName;  // 'Chauhan'
person.greet();   // Hello from Kiran Chauhan

In conclusion, this in greet() method refers to the current object or the object on the method is called upon(person). In simple words, whatever is on the left-side of the dot operator, this refers to it. In case of greet() method call, it is person object. So, this refers to the person.

---

But, consider the following example.

function magicSum() {
  const sum = this.a + this.b;
  console.log(sum);
}

We can simple call this function without dot as,

magicSum();

In this example, we do not have dot at all. Then the question is what this refers to in the magicSum() function? The answer is the global window object. When you define a normal function outside of anything, the function is attached to the window object and because of that you can call this function as magicSum() or window.magicSum(). Both calls are identical.

magicSum(); // NaN
window.magicSum(); // NaN

As a side note, when you define variables with var keyword, they are also attached to the global window object whereas let and const don’t.

var v1 = "a";
v1; // 'a'
window.v1; // 'a'

let v2 = "a";
v2; // 'a'
window.v2; // undefined

const v3 = "a";
v3; // 'a'
window.v3; // undefined

Continuing with the magicSum() function, this refers to the global window object. But, in the window object, we don’t have a and b. Due to this, we get NaN as a result. But, if we define variables a and b using the var keyword, then this.a and this.b resolve from the window object and we will get the current result as a sum than NaN.

var a = 40;
var b = 2;

Re-define this magicSum() function after these variables.

var a = 40;
var b = 2;

function magicSum() {
  const sum = this.a + this.b;
  console.log(sum);
}

Now, calling this function as magicSum() or this.magicSum() returns the correct result 42. this refers to the window object and resolve the values of this.a to 40 and this.b to 2 resulting 42 in magicSum() as sum.

The conclusion is, the value of this can be different based on how the function or method is called. If the function is called directly, this refers to the window object. But, if the method is called on the object, this refers to that object.

Things are not done here. We are halfway through. Based on the context, this can have three different sets of values and we can also set the value of this based on our requirements. So, this can have four different types of value based on context.


Do you know what a constructor function is? Consider the following example.

function Point() {
  this.x = 2;
  this.y = 4;
}

Note: Don’t worry about this in this example. We are going to cover it soon. That’s the main topic of this ongoing explanation.

This is the constructor function. Ok, this can be a constructor function. Ok, ok, this is not a constructor function. Actually, there is no unique syntax to define the constructor function. Instead, how the function is called, makes it a constructor function. When you use the new keyword to construct the copy of it, then it becomes the constructor function. When you make a copy of it using the new keyword, it will create an object. But, by convention, developers make the first letter capital to differentiate it from the normal function. Going back to the example,

function Point() {
  this.x = 2;
  this.y = 4;
}

We can create as many copies or the objects of this function we want using the new keyword.

const p1 = new Point();
const p2 = new Point();
const p3 = new Point();

Check the values of these p1, p2, and p3.

p1; // Point {x: 2, y: 4}
p2; // Point {x: 2, y: 4}
p3; // Point {x: 2, y: 4}

If you look further at the properties of the Point, you’ll find the Point() as constructor in console.

But, this is a useless function. Each copy has the same values of x and y. We can easily modify this function to accept the values of x and y.

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Now, we can create objects by passing the values of x and y.

const p1 = new Point(2, 2);
const p2 = new Point(3, 4);
const p3 = new Point(7, 3);

Now, these p1, p2, and p3 refer to the different objects with different values.

p1; // Point {x: 2, y: 2}
p2; // Point {x: 3, y: 4}
p3; // Point {x: 7, y: 3}

With this explanation, you know what the constructor function is, right? A function is usually defined with first-letter capital and you call it using the new keyword that creates a new object. But, what does it has to do with this? The answer is this plays an important role when you have the methods in it.

One more time, let’s define the same Point constructor function but with an add method.

function Point(x, y) {
  this.x = x;
  this.y = y;
  this.add = function () {
    console.log(this.x + this.y);
  };
}

Now, go ahead and create a p1 object.

const p1 = new Point(2, 2);

You can access the values x and y and the method add().

p1.x; // 2
p1.y; // 2
p1.add(); // 4

In p1.x, the left-side of the x is p1. So, this refers to the p1 object. That’s why you get 2. In p1.y, the left-side of the y is p1. So, this refers to the p1 object. That’s why you get 2. Finally, in p1.add(), the left-side of the add() method call is p1. That’s why this.x and this.y correctly resolve to x and y values of the p1 object and that’s why you get 4.

The conclusion is, if you’re using a constructor function, then this refers to the object that you created. Now, you know that based on the context, this can have three different values.

  1. Within an object, this refers to that object.
  2. In global scope, this refers to a window object.
  3. In case of the copy of the constructor function, this refers to the object that is created.

Finally, in JavaScript, we have flexibility to set the this to whatever object that we are interested in!


Consider the example, where I have the following two objects.

const person1 = {
  firstName: "Kiran",
  lastName: "Chauhan",
  greet: function (a, b) {
    console.log(a + b);
    console.log(`Hello from ${this.firstName} ${this.lastName}`);
  },
};
const person2 = {
  firstName: "Punit",
  lastName: "Tewani",
  bye: function (a, b) {
    console.log(a - b);
    console.log(`Goodbye from ${this.firstName} ${this.lastName}`);
  },
};

Notice, person1 has greet() method whereas person2 has bye() method. We can easily call these methods from respected objects.

person1.greet(2, 2); // 4 Hello from Kiran Chauhan
person2.bye(2, 2);   // 0 Goodbye from Punit Tewani

But, what I want to do is, I want to call the bye() method on person1 and greet() method on person2 as Kiran wants to say Goodbye and Punit wants to say Hello.

You can do it using call() or apply() methods. Both methods are almost the same except how they accept the arguments is different in terms of syntax.

The first argument to the call() method is this that you want to make as an object and remaining arguments are the arguments that the method that you’re calling needs separated by commas which are a and b.

call() method is called. In other words, you need to chain this method on the method that you’re interested in calling. In our case, the method is, greet(). So, we need to write,

person1.greet.call(); // This is partial code. Won't run.

Yup, we don’t have to write () after greet as it is call()’s responsibility to call this greet() method.

As mentioned previously, The first argument to call() method is the object that you want to make this which is in our case, person2 as Punit wants to say Hello even though he doesn’t have greet() method.

person1.greet.call(person2, 2, 2); // 4 Hello from Punit Tewani

this.firstName and this.lastName within the greet method are resolved against the values from person2 object using the call method.

If you use, apply() method instead of a call, you’ll get the same result. But, instead of passing arguments as comma separated values, we need to pass within a single array.

person1.greet.apply(person2, [2, 2]); // 0 Hello from Punit Tewani

The difference between call() and apply() is how you pass the argument to the method greet(). In case, if you have a method that doesn’t accept any argument. Then both call() and apply() methods serve the same purpose.

Because of the call() and apply() methods, Kiran can say Goodbye even though he doesn’t have bye() method.

// You can read the following as _call person2's bye method on person1_.
person2.bye.call(person1, 2, 2); // 0 Goodbye from Kiran Chauhan

// You can read the following as _apply person1 object in person2's bye method_.
person2.bye.apply(person1, [2, 2]); // 0 Goodbye from Kiran Chauhan

Let’s define the following callTwice() function.

function callTwice(cb) {
  cb();
  cb();
}

This function accepts the callback and calls the callback twice. If we do,

Note: For a moment, I’m not worried about passing values of a and b.

callTwice(person1.greet); // NaN Hello from undefined undefined x2
callTwice(person2.bye); // NaN Goodbye from undefined undefined x2

Even though person1 and person2 are clearly mentioned for greet and bye method, this.firstName and this.lastName are not correctly resolved. Things are tricky in callbacks. To resolve this, we need to use the .bind() method. This .bind() method correctly binds the scope of this and returns a new method. This method accepts an object that you want to makethis. In the case of person1.greet we need to call the bind method and pass person1 as an object to correctly bind the person1 object. Same is true for person2.

const greet = person1.greet.bind(person1);
const bye = person2.bye.bind(person2);

Now, call the callTwice function as we did previously.

callTwice(greet); // NaN Hello from Kiran Chauhan x2
callTwice(bye); // NaN Goodbye from Punit Tewani x2


Wednesday, September 11, 2024

Hono Tutorial Pt. 4

As the adjustment for the credentials are done in the previous article, we are now ready to create the CRUD end-points for the Contact resource. But, before that, I would like to cover a few methods that are needed in CRUD end-points such as fetching value(s) from dynamic params, reading request body, fetching value(s) from query string, and so on.

Let’s start with the dynamic params for this end-point: /contacts/:id. To fetch the id, we need to use c.req.param() method. This method returns a param object that we can de-structure to get id.

const { id } = c.req.param();

// OR

const id = c.req.param('id');

Next, to read the request body, c.req.json() method is used. Again, this method returns a request body object that we can de-structure to get the given field. In the case of contacts end-point, it might be firstName and lastName.

const { firstName, lastName } = c.req.json();

// OR

const firstName = c.req.json('firstName');
const lastName = c.req.json('lastName');

c.req.json() method is useful when you’re reading a request body with type application/json. But, if the request is in plaintext as text/plain type, then need to use c.req.text() or if request body type is blob, then need to use c.req.blob(). There are a couple of methods available within the c.req object based on the type of data you get in the request body. Refer the HonoRequest documentation for the more details.

Finally, to read the data from the querystring, c.req.query() method is used. This method returns a querystring object that we can de-structure to get the given querystring.

const { limit, offset } = c.req.query();

// OR

const limit = c.req.query('limit');
const offset = c.req.query('offset');

We are now ready to build the CRUD end-points for the Contacts resource.

Saturday, September 7, 2024

Styling in React

Although, there are many possible ways you can use styles in React application, following are the general categories to choose from.

  1. Default: React supports the default styling via class. Actually, via className. Or you can write the inline styles. But, you need to define it as an object and kebab-case converted to camelCase for the CSS properties whereas values must be defined as string. Event further you can use framework classes such as from Bootstrap or Tailwind. Some frameworks have React component wrapper around it such as react-bootstrap. But, still internally they use className.
// React component with class/className 
// (true for Bootstrap or Tailwind).
const Component = () => {
  return (
    <h1 className="title">Header</h1>
  )
};
// React component with inline styles.
const Component = () => {
  return (
    <h1 style={{ fontSize: "14px", paddingTop: "14px" }}>
      Header
    </h1>
  )
}
// React component with inline styles as style object.
const headerStyles = {
  fontSize: "14px",
  paddingTop: "14px",
};

const Component = () => {
  return (
    <h1 style={headerStyles}>Header</h1>
  )
}
  1. CSS Modules: This is not the default one that you can write within normal HTML/CSS. But, React provide the supports for it when you create an application using create-react-app. If you have a component in Component.jsx then you can define a styles for it in Component.module.css (here .module.css is important) and then you can import whole styles as JavaScript object and use the classes defined within this Component.module.css. This make sure that styles don’t have the side-effect on other components and correctly scoped.
  2. CSS-in-JS: People like the idea of inline style in React as you can define it as an object and you have autocomplete and suggestion for it. So, developers extend it further and make some good changes on top of it by making a style as component. Examples are styled-components, Emotion, etc.


Sunday, August 25, 2024

Phoenix Framework Pt. 1

This article assumes that you already know Elixir programming language. Further, following are the software you have on your computer.

  1. Elixir 1.14 or later
  2. Erlang 24 or later.
  3. PostgreSQL

If you do not have Elixir and do not know Elixir programming language, please refer the official website to install the Elixir language on your computer and go through this very well written Getting Started docs on Elixir language. This Getting Started covers Elixir in detail and that is what you need before jumping into this series on Phoenix framework. I will explain some Elixir concepts but those will be brief and specific within the context of framework only.

If you do not have Phoenix framework on you computer, please follow this excellent Installation guide. Please install all the suggested software written on this Installation guide. Please also confirm the version of these software as stated above.

-–

Open terminal and run the following command.

mix phx.new hello

This command will scaffold the application with default files and folders. Then it will ask you to install dependencies or not. Press Y or y and wait for the dependencies to be installed.

Once the dependencies are installed, you will get further instructions on what to do next. For example, the first thing to do is to cd into the created hello application/folder.

Next, open the application or hello folder into your favorite text-editor. For example, I am using Visual Studio Code.

code .

Then as suggested in further instructions, open config/dev.exs file. In this file, we are going to adjust the database connection config. Following is a part of the code copied from the config/dev.exs file that we need to adjust for the same.

# Configure your database
config :hello, Hello.Repo,
  username: "postgres",
  password: "postgres",
  hostname: "localhost",
  database: "hello_dev",
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

As the database is now configured, we can follow the command to create the database and associated tables in it.

mix ecto.create

The stage is set up. Run the application using the following command.

mix phx.server

The application is up and running at http://localhost:4000. Open this URL in the web browser and you should see a landing page with text “Peace of mind from prototype to production.” I like this phrase!

Congratulations! You have just created an application in the Phoenix framework.


Wednesday, August 7, 2024

Hono Tutorial Pt. 3

Before we go further and learn more about the Hono framework, the one thing that is bothering me is database credentials written in code. We should adjust the existing code by removing the credentials from the code and put it within the .env file. We should never hard-code the secret(s) as per 12factor app.

Go ahead and create a .env file in the root of the project.

touch .env

Let’s define the following environment variables within this created .env file.

DB_NAME="hono"
DB_HOST="localhost"
DB_PORT=5432
DB_USER="postgres"
DB_PASS="password"

We should not commit this file in the Git. The file should be added within .gitignore file. But, to give and idea about the existence of the .env file, we should have the .env.example file in the repo with same content as we have in .env file but with dummy environment variables or env vars.

cp .env .env.example

Hono has an adapter to read the env vars from the .env file. But, unfortunately it is not working for the Node and outside of c or context. For now, we are going to use the dotenv package. Let’s first install the dotenv package.

npm i -E dotenv

Import dotenv/config in app.js and we are good to go.

import { serve } from "@hono/node-server";
import { Hono } from "hono";
import postgres from "postgres";
import "dotenv/config";

const sql = postgres({
  host: "localhost",
  port: 5432,
  username: "postgres",
  password: "password",
  database: "hono",
});

const app = new Hono();

app.get("/", async (c) => {
  const result = await sql`SELECT 1 + 1`;
  console.log(result);
  return c.text("Hello!");
});

serve(app, (info) => {
  console.log(`Listening on http://localhost:${info.port}`);
});

Now, replace the hard-coded database connection options within app.js with the env vars using process.env as follows.

import { serve } from "@hono/node-server";
import { Hono } from "hono";
import postgres from "postgres";
import "dotenv/config";

const sql = postgres({
  host: process.env.DB_HOST,
  port: process.env.DB_PORT,
  username: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: process.env.DB_NAME,
});

const app = new Hono();

app.get("/", async (c) => {
  const result = await sql`SELECT 1 + 1`;
  console.log(result);
  return c.text("Hello!");
});

serve(app, (info) => {
  console.log(`Listening on http://localhost:${info.port}`);
});

With these changes, we have removed the hard-coded secrets from our code. These changes should not effect the output. Run the application to confirm the same!

npm run dev
Listening on http://localhost:3000

Visit the http://localhost:3000 in the web browser and you should see the following output in terminal while displaying Hello! in the browser.

Result(1) [ { '?column?': 2 } ]
Note
The code is available at GitHub. Checkout chapter03 branch for the code of this chapter.

Tuesday, August 6, 2024

Hono Tutorial Pt. 2

In this article, we are going to connect the database in Hono application. For the purpose of demonstration, we are going to use PostgreSQL database and postgres as npm package. You are free to use any other package and database as steps will be similar except you need to consult the respected documentation for initial setup.

Let’s install a postgres package.

npm i -E postgres

As per postgres package documentation, we first need to import the postgres function. This function accepts the connection options, and we can save the connection within sql variable as follows.

import { serve } from "@hono/node-server";
import { Hono } from "hono";
import postgres from "postgres";

const sql = postgres({
  host: "localhost",
  port: 5432,
  username: "postgres",
  password: "password",
  database: "hono",
});

const app = new Hono();

app.get("/", async (c) => {
  return c.text("Hello!");
});

serve(app, (info) => {
  console.log(`Listening on http://localhost:${info.port}`);
});

You need to adjust the connection options to the postgres() function based on your PostgreSQL setup. Once done, you now have the active connection within sql variable.

Let’s execute the simple SELECT 1 + 1 query within root route and print the result of it. The query returns the promise, and due to this we need to update the signature of the callback function to async/await.

import { serve } from "@hono/node-server";
import { Hono } from "hono";
import postgres from "postgres";

const sql = postgres({
  host: "localhost",
  port: 5432,
  username: "postgres",
  password: "password",
  database: "hono",
});

const app = new Hono();

app.get("/", async (c) => {
  const result = await sql`SELECT 1 + 1`;
  console.log(result);
  return c.text("Hello!");
});

serve(app, (info) => {
  console.log(`Listening on http://localhost:${info.port}`);
});

That’s it! Run the application using the following command.

npm run dev

Visit http://localhost:3000 in the browser and check the terminal. You should see the following console message.

Result(1) [ { '?column?': 2 } ]

We got the result 2 as 1+1 and when you don’t define the column name in SELECT clause in PostgreSQL, PostgreSQL by default gives ?column? as column name.

With these changes, our Hono application is connected with database. You might say that this is boring and nothing specific to Hono. That is right! As Hono doesn’t re-invent wheel in every aspect of the web development and database connection is one such a case!

Note: The code is available at GitHub. Checkout chapter02 branch for the code of this chapter.