├── .gitignore ├── README.md ├── package.json ├── src ├── components.tsx └── index.tsx ├── todo.sql ├── tsconfig.json └── wrangler.sample.toml /.gitignore: -------------------------------------------------------------------------------- 1 | .wrangler 2 | node_modules 3 | yarn-error.log 4 | yarn.lock 5 | wrangler.toml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Todo Example 2 | 3 | Stack: 4 | 5 | * Hono 6 | * JSX (Hono middleware) 7 | * htmx 8 | * Zod 9 | * Cloudflare Workers 10 | * Cloudflare D1 11 | 12 | ## Usage 13 | 14 | Install: 15 | 16 | ``` 17 | npm install 18 | ``` 19 | 20 | Setup: 21 | 22 | ``` 23 | wrangler d1 create todo 24 | wrangler d1 execute todo --local --file=todo.sql 25 | ``` 26 | 27 | Dev: 28 | 29 | ``` 30 | npm run dev 31 | ``` 32 | 33 | Deploy: 34 | 35 | ``` 36 | npm run deploy 37 | ``` 38 | 39 | ## Author 40 | 41 | Yusuke Wada 42 | 43 | ## License 44 | 45 | MIT -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "wrangler dev --live-reload src/index.tsx", 4 | "deploy": "wrangler deploy --minify src/index.tsx" 5 | }, 6 | "dependencies": { 7 | "@hono/zod-validator": "^0.1.9", 8 | "hono": "^3.8.0-rc.2", 9 | "zod": "^3.21.4" 10 | }, 11 | "devDependencies": { 12 | "@cloudflare/workers-types": "^4.20231002.0", 13 | "wrangler": "^3.11.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components.tsx: -------------------------------------------------------------------------------- 1 | import { html } from 'hono/html' 2 | import { jsxRenderer } from 'hono/jsx-renderer' 3 | 4 | export const renderer = jsxRenderer(({ children }) => { 5 | return html` 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Hono + htmx 14 | 15 | 16 |
17 |

Todo

18 | ${children} 19 |
20 | 21 | 22 | ` 23 | }) 24 | 25 | export const AddTodo = () => ( 26 |
27 |
28 | 29 |
30 | 33 |
34 | ) 35 | 36 | export const Item = ({ title, id }: { title: string; id: string }) => ( 37 |

42 | {title} 43 | 44 |

45 | ) 46 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import { Hono } from 'hono' 2 | import { z } from 'zod' 3 | import { zValidator } from '@hono/zod-validator' 4 | 5 | import { renderer, AddTodo, Item } from './components' 6 | 7 | type Bindings = { 8 | DB: D1Database 9 | } 10 | 11 | type Todo = { 12 | title: string 13 | id: string 14 | } 15 | 16 | const app = new Hono<{ Bindings: Bindings }>() 17 | 18 | app.get('*', renderer) 19 | 20 | app.get('/', async (c) => { 21 | const { results } = await c.env.DB.prepare(`SELECT id, title FROM todo;`).all() 22 | const todos = results 23 | return c.render( 24 |
25 | 26 | {todos.map((todo) => { 27 | return 28 | })} 29 |
30 |
31 | ) 32 | }) 33 | 34 | app.post( 35 | '/todo', 36 | zValidator( 37 | 'form', 38 | z.object({ 39 | title: z.string().min(1) 40 | }) 41 | ), 42 | async (c) => { 43 | const { title } = c.req.valid('form') 44 | const id = crypto.randomUUID() 45 | await c.env.DB.prepare(`INSERT INTO todo(id, title) VALUES(?, ?);`).bind(id, title).run() 46 | return c.html() 47 | } 48 | ) 49 | 50 | app.delete('/todo/:id', async (c) => { 51 | const id = c.req.param('id') 52 | await c.env.DB.prepare(`DELETE FROM todo WHERE id = ?;`).bind(id).run() 53 | c.status(200) 54 | return c.body(null) 55 | }) 56 | 57 | export default app 58 | -------------------------------------------------------------------------------- /todo.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE todo ( 2 | id TEXT PRIMARY KEY, 3 | title TEXT NOT NULL 4 | ); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "lib": [ 9 | "esnext" 10 | ], 11 | "types": [ 12 | "@cloudflare/workers-types" 13 | ], 14 | "jsx": "react-jsx", 15 | "jsxImportSource": "hono/jsx" 16 | }, 17 | } -------------------------------------------------------------------------------- /wrangler.sample.toml: -------------------------------------------------------------------------------- 1 | name = "todo" 2 | compatibility_date = "2023-01-01" 3 | 4 | [[d1_databases]] 5 | binding = "DB" # i.e. available in your Worker on env.DB 6 | database_name = "todo" 7 | database_id = "" 8 | preview_database_id = "local-todo" --------------------------------------------------------------------------------