Hono with MongoDB

Intro

Hono is a web framework that works on a number of javascript frameworks. It is used primarily used for running web applications on the edge (like Cloudflare Workers). We can Hono with Bun to create our web applications and then we can host them on Linux or containers.

Note: Hono has a excellent getting-started guide at https://hono.dev/getting-started/bun. A some of content in this post is going to be a repeat of their guide

Hono

We will use hono-create package to setup the basic project structure for our application. When prompted select bun as the template.

% bun create hono api 

create-hono version 0.3.2
✔ Using target directory … api
✔ Which template do you want to use? › bun

Change directory to the newly created project and install dependencies

% cd api
% bun install

The autogenerated index.ts file already has code to import Hono, initialize it and serve a text response at base URL.

Since we are using Bun, we can change the scripts in the package.json file to

{
    "scripts": {
    "dev": "NODE_ENV=development bun run --hot src/index.ts",
    "prod": "NODE_ENV=production bun run src/index.ts"
  },
}

Let’s add a HTTP request logger and CORS policy to the application using Hono’s built in middleware. After initializing but before adding routes to the app, we can add the following

app.use('*', logger())
app.use('*', cors({
    origin: (origin) => {
        if(process.env.NODE_ENV == 'development')
            return '*'
        else {
            let url = new URL(origin)
            return url.hostname.endsWith('projects.yasakdogra.com') ? origin : ''
        }
    }
}))

The CORS middleware will allow all origins in development environment but only allow origins ending with projects.yasakdogra.com in other environments (like production)

Check out some of my project posts to see how to add additional routes and return different types of responses. A example is also given further down in this post

MongoDB

NOTE: This step requires a working MongoDB installation. If you do not have one, you can sign up for the free tier of MongoDB Atlas

We will use Mongoose, object model tool, for working with MongoDB. We will also add Dotenv to load the database connection string

% bun add dotenv mongoose

NOTE: Only use the .env file method for development, testing and hosting non essential applications. For production applications store sensitive information in encrypted password stores. I will write more about that in a separate post

Create a .env file in project root. Add MongoDB connection string to it

MONGO_URI=mongodb+srv://<user>:<pass>@<instance>.mongodb.net/quotes?retryWrites=true&w=majority

NOTE: Since this file contains sensitive information, do not check in this file with version control. Keep sensitive information out of .env file if its used in version control

Now we can access the connection string inside our source code with

import 'dotenv/config'
const MONGO_URI = process.env.MONGO_URI

We can connect using Mongoose and the connection string with

import 'dotenv/config'
import mongoose from "mongoose";

if (process.env.MONGO_URI)
    mongoose.connect(process.env.MONGO_URI)
else
    throw new Error('MONGO_URI not found')

Putting it together

Now we will create a sample application that pulls a random quote from quotes database

The quotes are stored in the format

{
    "quote": "A house divided against itself cannot stand.",
    "author": "Abraham Lincoln"
}

Create a schema and model using mongoose in index.ts file

const quoteSchema = new mongoose.Schema({
    quote: String,
    author: String
});

const Quote = mongoose.model('Quote', quoteSchema);

To get a random quote we can use MongoDB’s aggregate and sample functions using similar mongoose functions: aggregate and sample. To send the response we can use json function from Hono context

We can modify the GET request at base URL

app.get('/', async (c) => {
    const tmp = await Quote.aggregate().sample(1).exec()
    return c.json({text: tmp[0]['quote'], author: tmp[0]['author']})
})

Now we can run the application with bun run dev and visit http://localhost:3000 or run curl localhost:3000 we will get a random quote

{"text":"Kind words do not cost much. Yet they accomplish much.","author":"Blaise Pascal"}