Build a Simple Note-Taking API with Bun, Hono, and Sequelize
A beginner’s guide to understanding the fundamentals of building a CRUD backend
When learning a new framework or tool, I believe it’s better to start small and grasp the core fundamentals first. That’s why I built this minimal note-taking API using Hono (a lightweight web framework) and Sequelize (an ORM), all powered by Bun for speed.
This guide is perfect for anyone starting with REST APIs, TypeScript, or backend development in general.
What We’re Building
We’ll create a simple CRUD API for managing notes:
- Create a note
- Read all notes or one by ID
- Update a note
- Delete a note
It uses:
- Hono for handling HTTP routes
- Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Oracle Database, Amazon Redshift and Snowflake’s Data Cloud
- TypeScript for better structure and type safety
- SQLite for lightweight local storage
- Bun for fast runtime and package management
Folder Structure Overview
make-note/
├── bun.lockb # Bun's lockfile
├── database.sqlite # SQLite database
├── package.json # Scripts and dependencies
├── tsconfig.json # TypeScript config
└── src/
├── index.ts # Main entry — bootstraps server & routes
├── config/
│ └── db.ts # Sequelize DB instance setup
├── models/
│ └── Notes.model.ts # Sequelize schema for the Note model
├── types/
│ └── notes.ts # Type definition for Note structure
├── service/
│ ├── create-note.ts # Logic to create a note
│ ├── list-all.ts # Logic to fetch all notes
│ ├── list-note-by-id.ts # Logic to fetch note by ID
│ ├── update-note.ts # Logic to update an existing note
│ └── delete-note.ts # Logic to delete a note
└── controller/
├── handle-create-note.ts # HTTP handler for POST /note
├── handle-all-notes.ts # HTTP handler for GET /notes
├── handle-note-by-id.ts # HTTP handler for GET /note/:id
├── handle-update-note.ts # HTTP handler for PUT /note/:id
└── handle-delete-note.ts # HTTP handler for DELETE /note/:id
Step 1: Set Up the Project
Install Bun (if you haven’t already):
curl -fsSL https://bun.sh/install | bash
Create and enter the project folder:
bun create hono@latest make-note
cd make-note
I Choose the Node template.
Install dependencies:
bun add sequelize sqlite3
bun add -d typescript @types/node
Step 2: Configure SQLite with Sequelize
src/config/db.ts
Purpose: Sets up Sequelize with SQLite.
import { Sequelize } from "sequelize";
export const sequelizeInstance = new Sequelize({
dialect: "sqlite",
storage: "database.sqlite",
logging: console.log,
});
Step 3: Define the Note Model
src/models/Notes.model.ts
Purpose: Defines the Note model schema with fields like
_title_,_content_,_createdAt_, and_updatedAt_
import { DataTypes, Model } from "sequelize";
import { sequelizeInstance } from "../config/db.js";
export class Note extends Model {
declare id?: number;
declare title: string;
declare content: string;
}
Note.init({
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
title: { type: DataTypes.STRING, allowNull: false },
content: { type: DataTypes.STRING, allowNull: false },
}, {
sequelize: sequelizeInstance,
modelName: "Notes",
timestamps: true,
});
Step 4: Add Type Definitions
src/types/notes.ts
Purpose: Defines the TypeScript type for a note object.
export type NoteAttribute = {
id?: number;
title: string;
content: string;
};
Step 5: Add Service Logic
Each file here performs the business logic — database operations for each endpoint.
src/service/create-note.ts
Creates a new note in the database
import { Note } from "../models/Notes.model.js";
export const createNote = async (title: string, content: string) => {
if (!title || !content) throw new Error("Title and content are required");
return await Note.create({ title, content });
};
src/service/list-all.ts
Fetches all notes
import { Note } from "../models/Notes.model.js";
export const allNotes = async () => {
return await Note.findAll();
};
src/service/list-note-by-id.ts
Fetches a single note by ID
import { Note } from "../models/Notes.model.js";
export const listNoteById = async (id: number) => {
return await Note.findByPk(id);
};
src/service/update-note.ts
Updates an existing note by ID
import { Note } from "../models/Notes.model.js";
export const updateNote = async (id: number, title: string, content: string) => {
const note = await Note.findByPk(id);
if (!note) throw new Error("Note not found");
await note.update({ title, content });
return note;
};
src/service/delete-note.ts
Deletes a note by ID
import { Note } from "../models/Notes.model.js";
export const deleteNote = async (id: number) => {
const note = await Note.findByPk(id);
if (!note) throw new Error("Note not found");
await note.destroy();
return note;
};
Step 6: Add Controllers
These map service logic to API routes using Hono’s context.
src/controller/handle-create-note.ts
POST /note — Creates a new note
import { createNote } from "../service/create-note.js";
import type { Context } from "hono";
export const handleCreateNote = async (c: Context) => {
const { title, content } = await c.req.json();
const note = await createNote(title, content);
return c.json({ status: 200, message: "Note created", data: note });
};
src/controller/handle-all-notes.ts
GET /notes — Gets all notes
import { allNotes } from "../service/list-all.js";
import type { Context } from "hono";
export const handleAllNotes = async (c: Context) => {
const notes = await allNotes();
return c.json({ status: 200, data: notes });
};
src/controller/handle-note-by-id.ts
GET /note/:id — Gets a single note
import { listNoteById } from "../service/list-note-by-id.js";
import type { Context } from "hono";
export const handleNoteById = async (c: Context) => {
const { id } = c.req.param();
const note = await listNoteById(Number(id));
return c.json({ status: 200, data: note });
};
src/controller/handle-update-note.ts
PUT /note/:id — Updates a note
import { updateNote } from "../service/update-note.js";
import type { Context } from "hono";
export const handleUpdateNote = async (c: Context) => {
const { id } = c.req.param();
const { title, content } = await c.req.json();
const note = await updateNote(Number(id), title, content);
return c.json({ status: 200, message: "Note updated", data: note });
};
src/controller/handle-delete-note.ts
DELETE /note/:id — Deletes a note
import { deleteNote } from "../service/delete-note.js";
import type { Context } from "hono";
export const handleDeleteNote = async (c: Context) => {
const { id } = c.req.param();
const note = await deleteNote(Number(id));
return c.json({ status: 200, message: "Note deleted", data: note });
};
Step 7: Bootstrap the Server
src/index.ts
Boots up the Hono app, syncs the DB, and registers routes
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { sequelizeInstance } from "./config/db.js";
import {
handleCreateNote,
handleAllNotes,
handleNoteById,
handleUpdateNote,
handleDeleteNote,
} from "./controller/index.js";
const app = new Hono();
/* .sync() ensures your model schema is applied to the DB. In dev,
it helps auto-create the table if not present. */
await sequelizeInstance.sync()
app.get("/notes", handleAllNotes);
app.get("/note/:id", handleNoteById);
app.post("/note", handleCreateNote);
app.put("/note/:id", handleUpdateNote);
app.delete("/note/:id", handleDeleteNote);
serve({ fetch: app.fetch, port: 3000 }, (info) =>
console.log(`Server running at http://localhost:${info.port}`)
);
Step 8: Test the API
Start the server:
bun run dev
Example requests
Create a note:
curl -X POST http://localhost:3000/note \
-H "Content-Type: application/json" \
-d '{"title":"Hello","content":"My first note"}'
Get all notes:
curl http://localhost:3000/notes
Get a note by ID:
curl http://localhost:3000/note/1
Update a note:
curl -X PUT http://localhost:3000/note/1 \
-H "Content-Type: application/json" \
-d '{"title":"Updated","content":"Changed content"}'
Delete a note:
curl -X DELETE http://localhost:3000/note/1
🎯 Why Start Simple?
Instead of diving into advanced topics like authentication or validation, this project helped me build a strong foundation in:
- Routing with Hono
- Working with Sequelize models
- Clean project structuring
- Using Bun for a snappy dev experience
What’s Next?
You can easily extend this API with:
- Input validation using Zod
- JWT-based user authentication
- Deploy to Render or Vercel
- Add a frontend using React or SvelteKit
Final Thoughts
This project started as a way for me to explore the fundamentals of building a backend with Bun, Hono, and Sequelize — and it’s been an incredibly fun and fast experience. If you’re just getting into backend development, I highly recommend creating a simple CRUD API like this from scratch. You’ll gain a deeper understanding of routing, databases, and project structuring by doing it yourself.
🛠️ Want to build on this or learn by contributing? The project is open-source and beginner-friendly! Whether you’re exploring Bun, learning REST APIs, or just want to help improve this project, your contributions are welcome.
👉 Check it out here: github.com/adarshswaminath/bun-hono-notes-api