1 | [![npm downloads](https://img.shields.io/npm/dm/h3.svg?style=flat-square)](https://npmjs.com/package/h3)
|
2 | [![version](https://img.shields.io/npm/v/h3/latest.svg?style=flat-square)](https://npmjs.com/package/h3)
|
3 | [![bundlephobia](https://img.shields.io/bundlephobia/min/h3/latest.svg?style=flat-square)](https://bundlephobia.com/result?p=h3)
|
4 | [![build status](https://img.shields.io/github/workflow/status/unjs/h3/ci/main?style=flat-square)](https://github.com/unjs/h3/actions)
|
5 | [![coverage](https://img.shields.io/codecov/c/gh/unjs/h3/main?style=flat-square)](https://codecov.io/gh/unjs/h3)
|
6 | [![jsDocs.io](https://img.shields.io/badge/jsDocs.io-reference-blue?style=flat-square)](https://www.jsdocs.io/package/h3)
|
7 |
|
8 | > H3 is a minimal h(ttp) framework built for high performance and portability
|
9 |
|
10 |
|
11 |
|
12 | ## Features
|
13 |
|
14 | ✔️ **Portable:** Works perfectly in Serverless, Workers, and Node.js
|
15 |
|
16 | ✔️ **Compatible:** Support connect/express middleware
|
17 |
|
18 | ✔️ **Minimal:** Small, tree-shakable and zero-dependency
|
19 |
|
20 | ✔️ **Modern:** Native promise support
|
21 |
|
22 | ✔️ **Extendable:** Ships with a set of composable utilities but can be extended
|
23 |
|
24 | ✔️ **Router:** Super fast route matching using [unjs/radix3](https://github.com/unjs/radix3)
|
25 |
|
26 | ## Install
|
27 |
|
28 | ```bash
|
29 | # Using npm
|
30 | npm install h3
|
31 |
|
32 | # Using yarn
|
33 | yarn add h3
|
34 |
|
35 | # Using pnpm
|
36 | pnpm add h3
|
37 | ```
|
38 |
|
39 | ## Usage
|
40 |
|
41 | ```ts
|
42 | import { createServer } from 'http'
|
43 | import { createApp } from 'h3'
|
44 |
|
45 | const app = createApp()
|
46 | app.use('/', () => 'Hello world!')
|
47 |
|
48 | createServer(app).listen(process.env.PORT || 3000)
|
49 | ```
|
50 |
|
51 | <details>
|
52 | <summary>Example using <a href="https://github.com/unjs/listhen">listhen</a> for an elegant listener.</summary>
|
53 |
|
54 | ```ts
|
55 | import { createApp } from 'h3'
|
56 | import { listen } from 'listhen'
|
57 |
|
58 | const app = createApp()
|
59 | app.use('/', () => 'Hello world!')
|
60 |
|
61 | listen(app)
|
62 | ```
|
63 | </details>
|
64 |
|
65 | ## Router
|
66 |
|
67 | The `app` instance created by `h3` uses a middleware stack (see [how it works](#how-it-works)) with the ability to match route prefix and apply matched middleware.
|
68 |
|
69 | To opt-in using a more advanced and convenient routing system, we can create a router instance and register it to app instance.
|
70 |
|
71 | ```ts
|
72 | import { createApp, createRouter } from 'h3'
|
73 |
|
74 | const app = createApp()
|
75 |
|
76 | const router = createRouter()
|
77 | .get('/', () => 'Hello World!')
|
78 | .get('/hello/:name', req => `Hello ${req.context.params.name}!`)
|
79 |
|
80 | app.use(router)
|
81 | ```
|
82 |
|
83 | **Tip:** We can register same route more than once with different methods.
|
84 |
|
85 | Routes are internally stored in a [Radix Tree](https://en.wikipedia.org/wiki/Radix_tree) and matched using [unjs/radix3](https://github.com/unjs/radix3).
|
86 |
|
87 | ## More usage examples
|
88 |
|
89 | ```js
|
90 | // Handle can directly return object or Promise<object> for JSON response
|
91 | app.use('/api', (req) => ({ url: req.url }))
|
92 |
|
93 | // We can have better matching other than quick prefix match
|
94 | app.use('/odd', () => 'Is odd!', { match: url => url.substr(1) % 2 })
|
95 |
|
96 | // Handle can directly return string for HTML response
|
97 | app.use(() => '<h1>Hello world!</h1>')
|
98 |
|
99 | // We can chain calls to .use()
|
100 | app.use('/1', () => '<h1>Hello world!</h1>')
|
101 | .use('/2', () => '<h1>Goodbye!</h1>')
|
102 |
|
103 | // Legacy middleware with 3rd argument are automatically promisified
|
104 | app.use((req, res, next) => { req.setHeader('X-Foo', 'bar'); next() })
|
105 |
|
106 | // Force promisify a legacy middleware
|
107 | // app.use(someMiddleware, { promisify: true })
|
108 |
|
109 | // Lazy loaded routes using { lazy: true }
|
110 | // app.use('/big', () => import('./big'), { lazy: true })
|
111 | ```
|
112 |
|
113 | ## Utilities
|
114 |
|
115 | ### Built-in
|
116 |
|
117 | Instead of adding helpers to `req` and `res`, h3 exposes them as composable utilities.
|
118 |
|
119 | - `useRawBody(req, encoding?)`
|
120 | - `useBody(req)`
|
121 | - `useCookies(req)`
|
122 | - `useCookie(req, name)`
|
123 | - `setCookie(res, name, value, opts?)`
|
124 | - `deleteCookie(res, name, opts?)`
|
125 | - `useQuery(req)`
|
126 | - `getRouterParams(event)`
|
127 | - `send(res, data, type?)`
|
128 | - `sendRedirect(res, location, code=302)`
|
129 | - `getRequestHeaders(event, headers)` (alias: `getHeaders`)
|
130 | - `getRequestHeader(event, name)` (alias: `getHeader`)
|
131 | - `setResponseHeaders(event, headers)` (alias: `setHeaders`)
|
132 | - `setResponseHeader(event, name, value)` (alias: `setHeader`)
|
133 | - `appendResponseHeaders(event, headers)` (alias: `appendHeaders`)
|
134 | - `appendResponseHeader(event, name, value)` (alias: `appendHeader`)
|
135 | - `createError({ statusCode, statusMessage, data? })`
|
136 | - `sendError(res, error, debug?)`
|
137 | - `defineHandle(handle)`
|
138 | - `defineMiddleware(middlware)`
|
139 | - `useMethod(req, default?)`
|
140 | - `isMethod(req, expected, allowHead?)`
|
141 | - `assertMethod(req, expected, allowHead?)`
|
142 |
|
143 | 👉 You can learn more about usage in [JSDocs Documentation](https://www.jsdocs.io/package/h3#package-functions).
|
144 |
|
145 | ### Add-ons
|
146 |
|
147 | More composable utilities can be found in community packages.
|
148 |
|
149 | - `validateBody(event, schema)` from [h3-typebox](https://github.com/kevinmarrec/h3-typebox)
|
150 | - `validateQuery(event, schema)` from [h3-typebox](https://github.com/kevinmarrec/h3-typebox)
|
151 |
|
152 | ## How it works?
|
153 |
|
154 | Using `createApp`, it returns a standard `(req, res)` handler function and internally an array called middleware stack. using`use()` method we can add an item to this internal stack.
|
155 |
|
156 | When a request comes, each stack item that matches the route will be called and resolved until [`res.writableEnded`](https://nodejs.org/api/http.html#http_response_writableended) flag is set, which means the response is sent. If `writableEnded` is not set after all middleware, a `404` error will be thrown. And if one of the stack items resolves to a value, it will be serialized and sent as response as a shorthand method to sending responses.
|
157 |
|
158 | For maximum compatibility with connect/express middleware (`req, res, next?` signature), h3 converts classic middleware into a promisified version ready to use with stack runner:
|
159 |
|
160 | - If middleware has 3rd next/callback param, the promise will `resolve/reject` when called
|
161 | - If middleware returns a promise, it will be **chained** to the main promise
|
162 | - If calling middleware throws an immediate error, the promise will be rejected
|
163 | - On `close` and `error` events of res, the promise will `resolve/reject` (to ensure if middleware simply calls `res.end`)
|
164 |
|
165 | ## License
|
166 |
|
167 | MIT
|