Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Gift Egwuenu
Gift Egwuenu

Posted on

     

Build a Bookmark Manager with the HONC Stack

What Even is the Best Stack? 🤔

As developers, we are constantly in pursuit of the best way to build applications that are frictionless, scalable, and a joy to work with. The ecosystem is filled with countless frameworks, libraries, and tools, each promising to make development easier. But what if there was a stack that combined performance, simplicity, and flexibility?

Enter theHONC Stack.

What is the HONC Stack?

HONC is a modern full-stack development approach optimized for speed, developer experience, and global scalability. It stands for:

  • HHono: A lightweight, fast, and Edge-first web framework for building APIs and applications.
  • ODrizzle ORM: A type-safe ORM designed for SQL databases with a great developer experience.
  • NName your Database: Whether it'sCloudflare D1(SQLite on the Edge),Neon (serverless Postgres), or any other preferred database, HONC allows for flexibility.
  • CCloudflare: A powerful developer platform offering Workers, KV, R2, D1, and more, making it an ideal environment for deploying modern apps at the Edge.

Why the HONC Stack?

The HONC stack is designed to take advantage of modern cloud and Edge computing principles, enabling developers to:

  • ⚡ Build fast, globally distributed applications with Cloudflare Workers and Hono.
  • 🛡️ Ensure type safety and maintainability with Drizzle ORM.
  • 🗃️ Use a flexible database solution depending on the use case.
  • 🚀 Deploy effortlessly with Cloudflare’s robust global infrastructure.

Getting Started with HONC

Want to try the HONC stack for yourself? Setting up a new project is as easy as running:

npm create honc-app@latest
Enter fullscreen modeExit fullscreen mode

This command sets up a new application with Hono, Drizzle ORM, and Cloudflare Worker bindings pre-configured. During setup, you’ll be prompted to choose a template. Select theD1 base template to ensure your application is optimized for Cloudflare D1 as the database solution.

Build a Bookmark Manager with the HONC Stack

To showcase the HONC Stack in action, let's build a simpleBookmark Manager that allows users to:

  • Add bookmarks (title, URL, description, tags)
  • View saved bookmarks
  • Delete bookmarks

Set Up Your HONC App

This part was already done in the step above, go ahead and run the application using:

npm run db:setupnpm run dev
Enter fullscreen modeExit fullscreen mode

Configure Cloudflare D1

Let's create a new D1 database:

npx wrangler d1 create bookmarks_db
Enter fullscreen modeExit fullscreen mode

Add it towrangler.toml and update the database name inpackage.json scripts accordingly:

[[d1_databases]]binding="DB"database_name="bookmarks_db"database_id="<your-database-id>"migrations_dir="drizzle/migrations"
Enter fullscreen modeExit fullscreen mode

Define the Database Schema (Drizzle ORM)

Update theschema.ts to define the bookmarks table:

import{sql}from'drizzle-orm';import{integer,sqliteTable,text}from'drizzle-orm/sqlite-core';exporttypeNewBookmark=typeofbookmarks.$inferInsert;exportconstbookmarks=sqliteTable('bookmarks',{id:integer('id',{mode:'number'}).primaryKey(),title:text('title').notNull(),url:text('url').notNull(),description:text('description'),tags:text('tags'),createdAt:text('created_at').notNull().default(sql`(CURRENT_TIMESTAMP)`)});
Enter fullscreen modeExit fullscreen mode

Create API Routes with OpenAPI (Hono)

Next, update theindex.ts to define API endpoints with OpenAPI support:

import{drizzle,typeDrizzleD1Database}from"drizzle-orm/d1";import{eq}from"drizzle-orm";import{createFiberplane}from"@fiberplane/hono";import{OpenAPIHono,createRoute,z}from"@hono/zod-openapi";import*asschemafrom"./db/schema";typeBindings={DB:D1Database;};typeVariables={db:DrizzleD1Database;};constapp=newOpenAPIHono<{Bindings:Bindings;Variables:Variables}>();app.use(async(c,next)=>{constdb=drizzle(c.env.DB);c.set("db",db);awaitnext();});constBookmarkSchema=z.object({id:z.number().openapi({example:1}),title:z.string().openapi({example:"My Bookmark"}),url:z.string().url().openapi({example:"https://example.com"}),description:z.string().optional().openapi({example:"A useful link"}),tags:z.string().optional().openapi({example:"tech, coding"}),}).openapi("Bookmark");constgetBookmarks=createRoute({method:'get',path:'/api/bookmarks',responses:{200:{content:{'application/json':{schema:z.array(BookmarkSchema)}},description:'Bookmarks fetched successfully',},},});constcreateBookmark=createRoute({method:"post",path:"/api/bookmark",request:{body:{required:true,content:{"application/json":{schema:BookmarkSchema,},},},},responses:{201:{content:{"application/json":{schema:BookmarkSchema,},},description:"Bookmark created successfully",},},});constdeleteBookmark=createRoute({method:'delete',path:'/api/bookmark/{id}',responses:{200:{content:{'application/json':{schema:z.object({message:z.string()})},},description:'Bookmark deleted successfully',},},});app.openapi(getBookmarks,async(c)=>{constdb=c.get("db");constbookmarks=awaitdb.select().from(schema.bookmarks);returnc.json(bookmarks);});app.openapi(createBookmark,async(c)=>{constdb=c.get("db");const{title,url,description,tags}=c.req.valid("json");const[newBookmark]=awaitdb.insert(schema.bookmarks).values({title,url,description,tags}).returning();returnc.json(newBookmark,201);});exportdefaultapp;
Enter fullscreen modeExit fullscreen mode

Seed the Database

To populate the database with actual sample bookmarks, update the existingscripts/seed.ts file to include:

import{bookmarks}from'./src/db/schema';...constsampleBookmarks=[{title:"Hono Framework",url:"https://hono.dev",description:"A lightweight web framework for building APIs and applications.",tags:"hono, framework, edge",},{title:"Drizzle ORM",url:"https://orm.drizzle.team",description:"A type-safe ORM designed for SQL databases.",tags:"orm, database, typescript",},{title:"Cloudflare D1",url:"https://developers.cloudflare.com/d1/",description:"Cloudflare’s globally distributed, serverless database.",tags:"cloudflare, database, d1",},{title:"HTMX",url:"https://htmx.org",description:"A library that allows access to modern browser features directly from HTML.",tags:"htmx, frontend, html",},{title:"MDN Web Docs",url:"https://developer.mozilla.org",description:"Comprehensive documentation for web technologies.",tags:"documentation, web, mdn",},];seedDatabase();asyncfunctionseedDatabase(){...try{awaitdb.insert(bookmarks).values(sampleBookmarks);console.log('✅ Database seeded successfully!');if(!isProd){}}catch(error){console.error('❌ Error seeding database:',error);process.exit(1);}finally{process.exit(0);}}
Enter fullscreen modeExit fullscreen mode

Build a Frontend with Hono JSX Renderer

Use Hono's JSX renderer to serve the frontend dynamically:

import{jsxRenderer}from'hono/jsx-renderer';app.use('*',jsxRenderer());app.get('/',async(c)=>{constdb=c.get('db');constbookmarks:Bookmark[]=awaitdb.select().from(schema.bookmarks);returnc.render(<html><head><title>Bookmark Manager</title><scriptsrc="https://cdn.tailwindcss.com"></script><scriptsrc="https://unpkg.com/htmx.org@1.9.5"></script></head><bodyclass="bg-gray-100 min-h-screen"><h1class="text-4xl text-center font-bold mb-4 my-6">📌 Bookmark Manager</h1><divclass="max-w-xl w-full mx-auto bg-white p-6 my-6 rounded shadow-md"><formhx-post="/api/bookmark"hx-target="#bookmarkList"hx-swap="beforeend"class="flex flex-col gap-2 mb-4"><inputtype="text"name="title"placeholder="Title"requiredclass="border p-2 rounded"/><inputtype="url"name="url"placeholder="URL"requiredclass="border p-2 rounded"/>{/* Description and Tags (optional) */}<inputtype="text"name="description"placeholder="Description"class="border p-2 rounded"/><inputtype="text"name="tags"placeholder="Tags (comma-separated)"class="border p-2 rounded"/><buttontype="submit"class="bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700">              Add Bookmark</button></form></div><ulid="bookmarkList"class="space-y-2 max-w-xl w-full mx-auto">{bookmarks.map((b)=>(<liclass="p-2 border rounded flex justify-between items-center bg-white"id={`bookmark-${b.id}`}><divclass="flex flex-col"><spanclass="font-semibold">{b.title}</span><small>{b.description}</small><smallclass="text-gray-500">{b.tags??''}</small></div><divclass="space-x-2"><ahref={b.url}target="_blank"class="text-blue-600 hover:underline">                    Visit</a><buttonhx-delete={`/api/bookmark/${b.id}`}hx-target={`#bookmark-${b.id}`}hx-swap="outerHTML"class="bg-red-500 text-white px-2 py-1 rounded hover:bg-red-600">                    Delete</button></div></li>))}</ul></body></html>);});
Enter fullscreen modeExit fullscreen mode

To serve the frontend with JSX, rename your entry files to .tsx (for example, index.tsx) instead of .ts, ensuring that Hono can properly compile and serve JSX. For the full code checkout theGitHub Repo.

Deploy Everything to Cloudflare

Afterwards, run the migration script for production and optionally seed the database:

npm run db:migrate:prodnpm run db:seed:prod
Enter fullscreen modeExit fullscreen mode

Finally, deploy to production:

npm run deploy
Enter fullscreen modeExit fullscreen mode

The Bookmark Manager is now live with a minimal frontend!

Wrapping Up

The HONC stack makes it easy to build modern, efficient applications. Try it out and start building today!

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
brettimus profile image
Boots
I like things that I don't understand on the first try
  • Location
    Amsterdam
  • Pronouns
    he/him
  • Work
    Engineer at Fiberplane
  • Joined

love seeing honc apps pop up! do you have your app deployed somewhere?

CollapseExpand
 
lauragift21 profile image
Gift Egwuenu
Developer Advocate at Cloudflare.
  • Location
    Netherlands
  • Work
    Developer Advocate at Cloudflare
  • Joined
• Edited on• Edited

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Developer Advocate at Cloudflare.
  • Location
    Netherlands
  • Work
    Developer Advocate at Cloudflare
  • Joined

More fromGift Egwuenu

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp