mirror of
https://github.com/honojs/hono.git
synced 2024-11-22 11:17:33 +01:00
Feature/example blog (#39)
* Add blog CRUD example * Fixed some * Exclude example test
This commit is contained in:
parent
4a795dab88
commit
511c0ebd7e
5
example/blog/.gitignore
vendored
Normal file
5
example/blog/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
dist
|
||||
node_modules
|
||||
worker
|
||||
package-lock.json
|
||||
yarn.lock
|
13
example/blog/README.md
Normal file
13
example/blog/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# hono-example-blog
|
||||
|
||||
- CRUD
|
||||
- Using TypeScript
|
||||
- Test with Jest `miniflare environment`
|
||||
|
||||
## Endpoints
|
||||
|
||||
- `GET /posts`
|
||||
- `POST /posts`
|
||||
- `GET /posts/:id`
|
||||
- `PUT /posts/:id`
|
||||
- `DELETE /posts/:id`
|
7
example/blog/jest.config.js
Normal file
7
example/blog/jest.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
testEnvironment: 'miniflare', // ✨
|
||||
testMatch: ['**/test/**/*.+(ts|tsx|js)', '**/src/**/(*.)+(spec|test).+(ts|tsx|js)'],
|
||||
transform: {
|
||||
'^.+\\.(ts|tsx)$': 'ts-jest',
|
||||
},
|
||||
}
|
30
example/blog/package.json
Normal file
30
example/blog/package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "hono-example-blog",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "dist/worker.js",
|
||||
"scripts": {
|
||||
"test": "jest --verbose",
|
||||
"build": "rimraf tsc && wrangler build",
|
||||
"dev": "tsc && npm-run-all --parallel dev:*",
|
||||
"dev:tsc": "tsc --watch",
|
||||
"dev:wrangler": "wrangler dev"
|
||||
},
|
||||
"author": "Yusuke Wada <yusuke@kamawada.com> (https://github.com/yusukebe)",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hono": "^0.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^3.3.0",
|
||||
"@cloudflare/wrangler": "^1.19.7",
|
||||
"@types/jest": "^27.4.0",
|
||||
"jest": "^27.4.7",
|
||||
"jest-environment-miniflare": "^2.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ts-jest": "^27.1.2",
|
||||
"typescript": "^4.5.4",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.1"
|
||||
}
|
||||
}
|
76
example/blog/src/controller.test.ts
Normal file
76
example/blog/src/controller.test.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { app } from './index'
|
||||
import type { Post } from './model'
|
||||
|
||||
describe('Root', () => {
|
||||
it('GET /', async () => {
|
||||
const req = new Request('http://localhost/')
|
||||
const res = await app.dispatch(req)
|
||||
expect(res.status).toBe(200)
|
||||
const body = (await res.json()) as any
|
||||
expect(body['message']).toBe('Hello')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Blog API', () => {
|
||||
it('List', async () => {
|
||||
const req = new Request('http://localhost/posts')
|
||||
const res = await app.dispatch(req)
|
||||
expect(res.status).toBe(200)
|
||||
const body = (await res.json()) as any
|
||||
expect(body['posts']).not.toBeUndefined()
|
||||
expect(body['posts'].length).toBe(0)
|
||||
})
|
||||
|
||||
it('CRUD', async () => {
|
||||
let payload = JSON.stringify({ title: 'Morning', body: 'Good Morning' })
|
||||
let req = new Request('http://localhost/posts', { method: 'POST', body: payload })
|
||||
let res = await app.dispatch(req)
|
||||
expect(res.status).toBe(201)
|
||||
let body = (await res.json()) as any
|
||||
const newPost = body['post'] as Post
|
||||
expect(newPost.title).toBe('Morning')
|
||||
expect(newPost.body).toBe('Good Morning')
|
||||
|
||||
req = new Request('https://localhost/posts')
|
||||
res = await app.dispatch(req)
|
||||
expect(res.status).toBe(200)
|
||||
body = (await res.json()) as any
|
||||
expect(body['posts'].length).toBe(1)
|
||||
|
||||
req = new Request(`https://localhost/posts/${newPost.id}`)
|
||||
res = await app.dispatch(req)
|
||||
expect(res.status).toBe(200)
|
||||
body = (await res.json()) as any
|
||||
let post = body['post'] as Post
|
||||
expect(post.id).toBe(newPost.id)
|
||||
expect(post.title).toBe('Morning')
|
||||
|
||||
payload = JSON.stringify({ title: 'Night', body: 'Good Night' })
|
||||
req = new Request(`https://localhost/posts/${post.id}`, {
|
||||
method: 'PUT',
|
||||
body: payload,
|
||||
})
|
||||
res = await app.dispatch(req)
|
||||
expect(res.status).toBe(200)
|
||||
body = (await res.json()) as any
|
||||
expect(body['ok']).toBeTruthy()
|
||||
|
||||
req = new Request(`https://localhost/posts/${post.id}`)
|
||||
res = await app.dispatch(req)
|
||||
expect(res.status).toBe(200)
|
||||
body = (await res.json()) as any
|
||||
post = body['post'] as Post
|
||||
expect(post.title).toBe('Night')
|
||||
expect(post.body).toBe('Good Night')
|
||||
|
||||
req = new Request(`https://localhost/posts/${post.id}`, { method: 'DELETE' })
|
||||
res = await app.dispatch(req)
|
||||
expect(res.status).toBe(200)
|
||||
body = (await res.json()) as any
|
||||
expect(body['ok']).toBeTruthy()
|
||||
|
||||
req = new Request(`https://localhost/posts/${post.id}`)
|
||||
res = await app.dispatch(req)
|
||||
expect(res.status).toBe(404)
|
||||
})
|
||||
})
|
51
example/blog/src/controller.ts
Normal file
51
example/blog/src/controller.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import type { Handler } from '../../../dist'
|
||||
import * as Model from './model'
|
||||
|
||||
export const root: Handler = (c) => {
|
||||
return c.json({ message: 'Hello' })
|
||||
}
|
||||
|
||||
export const list: Handler = (c) => {
|
||||
const posts = Model.getPosts()
|
||||
return c.json({ posts: posts, ok: true })
|
||||
}
|
||||
|
||||
export const create: Handler = async (c) => {
|
||||
const param = (await c.req.json()) as Model.Param
|
||||
const newPost = Model.createPost(param)
|
||||
if (!newPost) {
|
||||
// Is 200 suitable?
|
||||
return c.json({ error: 'Can not create new post', ok: false }, 200)
|
||||
}
|
||||
return c.json({ post: newPost, ok: true }, 201)
|
||||
}
|
||||
|
||||
export const show: Handler = async (c) => {
|
||||
const id = c.req.params('id')
|
||||
const post = Model.getPost(id)
|
||||
if (!post) {
|
||||
return c.json({ error: 'Not Found', ok: false }, 404)
|
||||
}
|
||||
return c.json({ post: post, ok: true })
|
||||
}
|
||||
|
||||
export const update: Handler = async (c) => {
|
||||
const id = c.req.params('id')
|
||||
if (!Model.getPost(id)) {
|
||||
// 204 No Content
|
||||
return c.json({ ok: false }, 204)
|
||||
}
|
||||
const param = (await c.req.json()) as Model.Param
|
||||
const success = Model.updatePost(id, param)
|
||||
return c.json({ ok: success })
|
||||
}
|
||||
|
||||
export const destroy: Handler = async (c) => {
|
||||
const id = c.req.params('id')
|
||||
if (!Model.getPost(id)) {
|
||||
// 204 No Content
|
||||
return c.json({ ok: false }, 204)
|
||||
}
|
||||
const success = Model.deletePost(id)
|
||||
return c.json({ ok: success })
|
||||
}
|
14
example/blog/src/index.ts
Normal file
14
example/blog/src/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Hono } from '../../../dist'
|
||||
import * as Controller from './controller'
|
||||
|
||||
export const app = new Hono()
|
||||
|
||||
app.get('/', Controller.root)
|
||||
|
||||
app.get('/posts', Controller.list)
|
||||
app.post('/posts', Controller.create)
|
||||
app.get('/posts/:id', Controller.show)
|
||||
app.put('/posts/:id', Controller.update)
|
||||
app.delete('/posts/:id', Controller.destroy)
|
||||
|
||||
app.fire()
|
53
example/blog/src/model.ts
Normal file
53
example/blog/src/model.ts
Normal file
@ -0,0 +1,53 @@
|
||||
declare global {
|
||||
interface Crypto {
|
||||
randomUUID(): string
|
||||
}
|
||||
}
|
||||
|
||||
export interface Post {
|
||||
id: string
|
||||
title: string
|
||||
body: string
|
||||
}
|
||||
|
||||
export type Param = {
|
||||
title: string
|
||||
body: string
|
||||
}
|
||||
|
||||
const posts: { [key: string]: Post } = {}
|
||||
|
||||
export const getPosts = (): Post[] => {
|
||||
return Object.values(posts)
|
||||
}
|
||||
|
||||
export const getPost = (id: string): Post | undefined => {
|
||||
return posts[id]
|
||||
}
|
||||
|
||||
export const createPost = (param: Param): Post | undefined => {
|
||||
if (!(param.title && param.body)) return
|
||||
const id = crypto.randomUUID()
|
||||
const newPost: Post = { id: id, title: param.title, body: param.body }
|
||||
posts[id] = newPost
|
||||
return newPost
|
||||
}
|
||||
|
||||
export const updatePost = (id: string, param: Param): boolean => {
|
||||
if (!(param.title && param.body)) return false
|
||||
const post = posts[id]
|
||||
if (post) {
|
||||
post.title = param.title
|
||||
post.body = param.body
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const deletePost = (id: string): boolean => {
|
||||
if (posts[id]) {
|
||||
delete posts[id]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
23
example/blog/tsconfig.json
Normal file
23
example/blog/tsconfig.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"rootDir": "src",
|
||||
"outDir": "./dist",
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"types": [
|
||||
"@cloudflare/workers-types",
|
||||
"@types/jest",
|
||||
"@types/service-worker-mock"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
]
|
||||
}
|
4
example/blog/webpack.config.js
Normal file
4
example/blog/webpack.config.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
target: 'webworker',
|
||||
entry: './dist/index.js',
|
||||
}
|
9
example/blog/wrangler.toml
Normal file
9
example/blog/wrangler.toml
Normal file
@ -0,0 +1,9 @@
|
||||
name = "hono-example-blog"
|
||||
type = "webpack"
|
||||
route = ''
|
||||
zone_id = ''
|
||||
usage_model = ''
|
||||
compatibility_flags = []
|
||||
workers_dev = true
|
||||
compatibility_date = "2022-01-09"
|
||||
webpack_config = "webpack.config.js"
|
@ -1,9 +1,7 @@
|
||||
module.exports = {
|
||||
testMatch: [
|
||||
'**/test/**/*.+(ts|tsx|js)',
|
||||
'**/src/**/(*.)+(spec|test).+(ts|tsx|js)',
|
||||
],
|
||||
testMatch: ['**/test/**/*.+(ts|tsx|js)', '**/src/**/(*.)+(spec|test).+(ts|tsx|js)'],
|
||||
transform: {
|
||||
'^.+\\.(ts|tsx)$': 'ts-jest',
|
||||
},
|
||||
testPathIgnorePatterns: ['./example'],
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user