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.

Monday, August 5, 2024

Phoenix Framework Pt. 3

The routes of the application are defined within the lib/hello_web/router.ex file. In this file, look for the following code snippet.

scope "/", HelloWeb do
  pipe_through :browser

  get "/", PageController, :home
end

In a future article, we will cover what other code does in this file. But, for now we can focus on this code block and specifically, this line - get "/", PageController, :home. It means when a GET / or root route is requested, execute the home action (function) of PageController.

You can find this PageController or page_controller.ex file within the lib/hello_web/controllers folder. Open this file, and you should see the following code.

defmodule HelloWeb.PageController do
  use HelloWeb, :controller

  def home(conn, _params) do
    # The home page is often custom made,
    # so skip the default app layout.
    render(conn, :home, layout: false)
  end
end

There we have the home function! This function renders home template or home.html.heex file without layout and located at lib/hello_web/controllers/page_html folder.

Don’t worry about the location of these files! Don’t memorize the locations. With some practice, you know all these eventually.

So, we have the router.ex file that defines the routes of the application that point to the controller’s action and the controller’s action to render a view. There is one more file page_html.ex located at the lib/hello_web/controllers folder. But, we haven’t talked about it yet and we’ll talk about it in future articles. But, it is required to have to complete this request-response cycle.

---

Let’s add a new route GET /about. Open router.ex file, and write the following.

scope "/", HelloWeb do
  pipe_through :browser

  get "/", PageController, :home
  get "/about", PageController, :about
end

When the GET /about is requested, we are going to call about the action of the PageController file. Let’s add this about function in the page_controller.ex file. Open the page_controller.ex file and write the following.

defmodule HelloWeb.PageController do
  use HelloWeb, :controller

  def home(conn, _params) do
    # The home page is often custom made,
    # so skip the default app layout.
    render(conn, :home, layout: false)
  end

  def about(conn, _params) do
    # The home page is often custom made,
    # so skip the default app layout.
    render(conn, :about)
  end
end

I almost copied the home function with two notable changes - we are going to render about.html.heex file and remove the layout argument to use default layout. We do not have this about.html.heex file in the lib/hello_web/controllers/page_html file. So, let’s create one.

touch lib/hello_web/controllers/page_html/about.html.heex

Open this about.html.heex file and write the following code.

<h1>About<h1>

Visit http://localhost:4000/about file and you should see this header text with navigation. This navigation is coming from the default layout.

With these changes, now we have the Phoenix application with a new route!


Sunday, August 4, 2024

Phoenix Framework Pt. 2

In this article, I am going to briefly explain to you the purpose of a few files and folders present in this hello Phoenix project. This will be brief. Because, as you start to become familiar with Phoenix projects, it will become second nature to you.

We have a _build folder. When you run the application, Elixir compiles the project and saves the artifacts within this folder. You can ignore what is within this folder because Git also ignored this folder in the .gitignore file!

We have an assets folder. You can put static assets such as JavaScript or CSS within this folder that further require compilation such as TailwindCSS. But, if you want to save the static assets that don’t require the compilation such as plain CSS files or images, you can put it within the priv/static folder.

We have a config folder. This folder contains the configurations of the project. config.exs is the starting point and then we have files after each of the environments. We already know the config/dev.exs file as modified for the database configuration for the dev or development environment.

We have a deps folder. This folder contains the dependencies of this project. Again, we do not have to worry about this file as this file is ignored in .gitignore.

We have a lib folder. This folder contains the application code. We are going to spend most of the time in this folder.

We have a priv folder. This folder contains files which are directly not needed in production but the application depends on. For example, migration scripts. We need migration scripts for database modification but production does not directly include the code of it.

Finally, all the test cases are written within the test folder. And mix.exs and mix.lock file contains the dependencies of the project.

I think this brief overview is enough to get started and I will cover in details where it requires.

---

Let’s do a quick modification in scaffold code to print the hello, world text instead of the default landing page. What you are seeing at http://localhost:4000 is coming from lib/hello_web/controllers/page_html/home.html.heex. Open this file and replace the whole HTML with the following.

<h1>hello, world</h1>

You do not have to reload the page in the browser thanks to the Phoenix framework as it will auto reload for you! There you have the hello, world application in the Phoenix framework.


Friday, August 2, 2024

Migration

I'm migrating this blog to chauhankiran.pages.dev. This chauhankiran.blogspot.com blog is created in a first-generation blog theme that I highly customized. I want to customize more. But, I'm not getting any help on it. Also, I'm unable to find the documentation on first-generation themes. Current theme is too complicated for me! I remember copying one widget of 12-15 lines of code and expanding it into a couple of hundred lines of code is complicated for me.

Although, chauhankiran.pages.dev looks the same as this one, it is created in Jekyll and the theme is customized to retain the curren look-and-feel. The repo. is hosted on GitHub and deployed on CloudFlare pages.

I'm slowly migrating these articles. I do not plan to delete this current blogspot. But, the new content will be posted there!

Hono Tutorial Pt. 1

This is the first article in a series where we are going to build a simple back-end application in Hono in Node.js environment.

Create a new folder with the name hono-app.

mkdir hono-app

Go inside the created hono-app folder.

cd hono-app

Create a package.json file using npm with default values (-y).

npm init -y

Let’s install the hono package.

npm i -E hono

In order to use Hono in the Node.js environment, we also need to the @hono/node-server package.

npm i -E @hono/node-server

Finally, let’s install nodemon to ease the development. We should install this as a development dependency (-D).

npm i -E -D nodemon

For now, we are done with setup. Let’s move on to the coding side. Create a new file with name app.js.

touch app.js

Open this file (or hono-app folder) in your favorite text-editor/IDE and write the following code.

import { Hono } from 'hono';

const app = new Hono();

We are using the latest import...from syntax in our Node.js application! We have imported the Hono and then created an instance of the Hono() using a new keyword and saved it as an app.

Let’s now define a root route(/) and return a simple Hello! text as a response (don’t forget to return the response).

import { Hono } from 'hono';

const app = new Hono();

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

Here, c is a context and it contains both request and response in it with many useful methods such as .text(). The .text() method return response in plain text with string passed to it e.g. Hello!.

Now, to run the application in Node, we need to use @hono/node-server package. Import serve from @hono/node-server package.

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

const app = new Hono();

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

Use this serve method as follows.

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

const app = new Hono();

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

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

serve() function takes Hono instance as the first argument and second argument should be a callback function with info param that gives useful information such as port. If you don’t define the port value as we did, the default is 3000.

Before we go further and run the application, we need to do some adjustment in the package.json file. As we are using the latest import...from syntax, we need to define type as module. Also, update the main entry point to app.js file.

{
    "name": "hono-app",
    "version": "1.0.0",
    "main": "app.js",
    "type": "module",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "description": "",
    "dependencies": {
        "@hono/node-server": "1.14.0",
        "hono": "4.7.5"
    },
    "devDependencies": {
        "nodemon": "3.1.9"
    }
}

Finally, let’s add two scripts dev and start to run the app.js file with nodemon and node respectively.

{
    "name": "hono-app",
    "version": "1.0.0",
    "main": "app.js",
    "type": "module",
    "scripts": {
        "dev": "nodemon app.js",
        "start": "node app.js"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "description": "",
    "dependencies": {
        "@hono/node-server": "1.14.0",
        "hono": "4.7.5"
    },
    "devDependencies": {
        "nodemon": "3.1.9"
    }
}

With these changes, we are now ready to run the application. Run the following command in the terminal.

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

Open http://localhost:3000 in the web browser and you should see Hello! as the plan text. If that is the case, congratulations! You have just created first Hono application in Node.js.

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