tutorials
Basic Restful Web Service - Repo
· KM
Let’s develop Restful Web Service
Tech Stack
- Hono
- Cloudflare D1 Database
- Cloudflare Worker
- Github For version control
Developer လုပ်တော့မယ်ဟေ့ဆိုရင်
- Github
- Cloudflare
- Namecheap
- Amazon
- Google Cloud
- Firebase
ဒါတွေ ဖောက်ထားသင့်တယ်။
Setup
npm create hono@latest my-restful-web-service
npm create hono@latest .
cd my-restful-web-service
npm install
## create database
npx wrangler d1 create my-restful-web-service-db
## save the database id and update wrangler.toml file.
# running sql command in local
npx wrangler d1 execute my-database --local --command="CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);"
# expense tracker
npx wrangler d1 execute my-database --local --command="CREATE TABLE expenses (id INTEGER PRIMARY KEY AUTOINCREMENT, description TEXT, amount REAL, date TEXT);"
```toml
[[d1_databases]]
binding = "DB"
database_name = "my-restful-web-service-db"
database_id = "your-generated-database-id-here"
create bindings
import { Hono } from 'hono'
// 1. Define your Cloudflare bindings
type Bindings = {
DB: D1Database // 'DB' must match the binding name in wrangler.toml
}
// 2. Pass the Bindings type to Hono
const app = new Hono<{ Bindings: Bindings }>()
in src/index.ts
import { Hono } from 'hono'
const app = new Hono()
// 1. GET: Simple Text Response
app.get('/', (c) => {
return c.text('Welcome to the Hono API!')
})
// 2. GET: Returning JSON data
app.get('/api/users', (c) => {
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]
return c.json(users)
})
// 3. GET: Reading Path Parameters
app.get('/api/users/:id', (c) => {
// Extract the 'id' parameter from the URL
const userId = c.req.param('id')
return c.json({
id: userId,
name: 'Alice',
message: `Fetched user ${userId}`
})
})
// 4. GET: Reading Query Parameters (e.g., /api/search?q=hono)
app.get('/api/search', (c) => {
const query = c.req.query('q')
return c.json({ searchQuery: query })
})
// 5. POST: Reading JSON from the Request Body
app.post('/api/users', async (c) => {
// Parse the incoming JSON body
const body = await c.req.json()
// Return a 201 Created status with the data
return c.json({
message: 'User created successfully',
data: body
}, 201)
})
export default app
Basic Expense Tracker
import { Hono } from 'hono'
// Define the D1 binding matching your wrangler.toml
type Bindings = {
DB: D1Database
}
const app = new Hono<{ Bindings: Bindings }>()
// 1. POST: Add a new expense
app.post('/expenses', async (c) => {
// Parse the JSON payload
const { description, amount, date } = await c.req.json()
// Basic safety check to ensure data exists
if (!description || amount === undefined || !date) {
return c.json({ error: 'Missing required fields: description, amount, or date' }, 400)
}
// Insert the data securely using parameterized inputs (?)
const { success } = await c.env.DB.prepare(
'INSERT INTO expenses (description, amount, date) VALUES (?, ?, ?)'
)
.bind(description, amount, date)
.run()
if (success) {
return c.json({ message: 'Expense logged successfully' }, 201)
}
return c.json({ error: 'Failed to insert expense' }, 500)
})
// 2. GET: List all expenses
app.get('/expenses', async (c) => {
// Fetch all records, sorted by newest first
const { results } = await c.env.DB.prepare(
'SELECT * FROM expenses ORDER BY date DESC'
).all()
return c.json(results)
})
// 3. GET: Calculate total expenses
app.get('/expenses/total', async (c) => {
// Let SQLite do the math for us
const result = await c.env.DB.prepare(
'SELECT SUM(amount) as total FROM expenses'
).first()
return c.json({ total: result?.total || 0 })
})
// 4. PUT: Update an existing expense
app.put('/expenses/:id', async (c) => {
const id = c.req.param('id')
// Destructure the payload, allowing values to be undefined
const { description, amount, date } = await c.req.json()
// Execute the update
const { success, meta } = await c.env.DB.prepare(
`UPDATE expenses
SET description = COALESCE(?, description),
amount = COALESCE(?, amount),
date = COALESCE(?, date)
WHERE id = ?`
)
.bind(description ?? null, amount ?? null, date ?? null, id)
.run()
// meta.changes tells us how many rows were actually modified
if (success && meta.changes > 0) {
return c.json({ message: 'Expense updated successfully' })
}
// If no rows changed, the ID probably doesn't exist
return c.json({ error: 'Expense not found or update failed' }, 404)
})
// 5. DELETE: Remove an expense
app.delete('/expenses/:id', async (c) => {
const id = c.req.param('id')
const { success, meta } = await c.env.DB.prepare(
'DELETE FROM expenses WHERE id = ?'
)
.bind(id)
.run()
if (success && meta.changes > 0) {
return c.json({ message: 'Expense deleted successfully' })
}
return c.json({ error: 'Expense not found' }, 404)
})
export default app
Sample API Call
curl -X POST http://localhost:8787/expenses \
-H "Content-Type: application/json" \
-d '{"description": "Coffee", "amount": 4.50, "date": "2026-07-15"}'
Souce Code
https://github.com/aungkoman/basic-restful-web-service
Live API Endpoint
https://api.software100.com.mm
API Doc
https://mcis-s-team.docs.buildwithfern.com/expense-tracker/introduction