H3 (pronounced as /eɪtʃθriː/, like h-3) is a minimal h(ttp) framework built for high performance and portability.
Local development
Published under the MIT license.
Made by @pi0 and community 💛
🤖 auto updated with automd
⚡️ Minimal H(TTP) framework built for high performance and portability
Home Page: https://h3.unjs.io/
License: MIT License
Hello @pi0 . I can not find for place to question. So I leave my question here.
I saw yesterday your commit. I understood early return (break to return) is nice pattern in the upper diff src/app.ts
.
but, I didn't understand to use setImmediate
. All of request already asynchronous. Why need Promise
with setImmediate
?
I tried to benchmark before / after setImmediate
. I got almost same result.
I tested with an api endpoint on a Nuxt3 project but you can probably reproduce with any local call I guess.
server/api/user.ts
export default async (req: IncomingMessage, res: ServerResponse) => {
const body = await useBody(req)
}
Using ohmyfetch to call the api locally with default GET method (and without body)
await $fetch('/api/user')
The Promise in the useRawBody function never gets resolved because the req.on is never called
Lines 29 to 41 in 25a6195
I guess it's tied to unenv fetch setting the req.body to null
https://github.com/unjs/unenv/blob/909d75e48667ce0732ce0fa8b6a2af4895f54d8e/src/runtime/fetch/call.ts#L31
req.body = context.body || null
I don't know if it's something that needs to be fixed here or in unenv.
As solution in h3 could be using this syntax (Node.js v10 + only) :
const bodyData = [];
for await (const chunk of req) {
bodyData.push(chunk);
}
req[RawBodySymbol] = Buffer.concat(bodyData)
resolve(encoding ? req[RawBodySymbol].toString(encoding) : req[RawBodySymbol])
app.use().use()
@pi0 Hello, Thank you very much createRouter
for solving my problem. I used the add function to successfully add a route for a get request, and it works normally, but if I add a query parameter after the URL, such as /a?title=a
, Such a route will fail to match;
Originally posted by @1018715564 in #76 (comment)
Hello I made nuxt-nitro demo clone without nitro. URL is here. https://nuxt-without-nitro-demo.vercel.app/
I try with /api/hello
made with h3, It failed after deploy. When I test on local, It make error.
ERROR Cannot set headers after they are sent to the client 20:39:12
at ServerResponse.setHeader (_http_outgoing.js:518:11)
at sendError (node_modules/h3/dist/index.js:502:7)
at node_modules/h3/dist/index.js:514:14
My server middleware is https://github.com/ChangJoo-Park/nuxt-without-nitro-demo/blob/main/server/hello.js#L4-L8.
I tested with remove res.setHeader('Access-Control-Allow-Origin', '*')
. but it fails too.
I am trying to use h3 to return some responses from a service (in my case, it's contentful). Of course some values from the service can be undefined, because in the CMS they are not required, but JSONValue (more specifically, JSONObject) doesn't accept undefined values.
interface JSONObject {
[x: symbol]: JSONValue;
}
This fixes errors, but I'm not sure it's correct.
@antfu cc
Hello, I found that PATCH
is not included in type HTTPMethod
https://www.jsdocs.io/package/h3#HTTPMethod
May I know why it is missing?
A match factory utility to match params (/[user]/[foo?]/
) and populate req.params
In my program, there are nested routes, such as /api/a
and /api/a/detail
, but due to fast prefix matching, when I access /api/a/detail
, the trigger is still /api/a'
s callback function, what should I do now?
Hello,
I'm trying to use server-sent events (SSE) with h3 (nuxt3 with @graphql-yoga/node API endpoint) but the response with text/event-stream
content-type returns and closes as soon as it comes in instead of staying connected.
Here's a reproduction repository: https://github.com/lewebsimple/nuxt3-sse-reproduction
with automatic promisifying, if a handler returns undefined, the promise never resolves and the server hangs...
Is there a reason we don't immediately resolve(undefined)
if the handler does?
Hello!
Is there a way already to respond a request with a stream of data?
I am planning specifically to use puppeteer page.createPDFStream, as Firebase Functions don't give us much initial memory, and I only found a way to increase it via regular cloud function, and not using nuxt /server/api
folder.
So, probably in this case, stream the data would be a good fit.
Thanks a lot!
Do you planing use http/2 protocol?
Example: /api/moduleLists
will not work, we need to set it lowercase.
when i use useBody
(h3 version 0.3.8) in the context of a middleware, the request doesn't fulfill. Removing the method call solves the issue.
How can i get the request body in the context of a middleware so i can use it for logging ? it seems calling useBody results in the next
handler to not be invoked...
Here is my demo code:
import { useBody } from "h3";
export default async function (req, res, next) {
const { statusCode } = res;
if(["POST", "PUT", "DELETE"].includes(req.method)) {
req.body = await useBody(req);
}
if (process.$winstonLog) {
process.$winstonLog(req, statusCode);
}
next();
}
Found while experimenting with nuxt3.
In my nuxt app, h3 will use a lazy/promisify/async middleware handle, which will eventually be invoked in callHandle
:
Lines 18 to 21 in 2805d4c
But async function will return a promise instance, if async handler does not send/close the response before they return, callHandle
resolve immediately.
The following patch is helpful on my case:
--- a/node_modules/h3/dist/index.mjs
+++ b/node_modules/h3/dist/index.mjs
@@ -109,7 +109,7 @@ function callHandle(handle, req, res) {
const next = (err) => err ? reject(err) : resolve(void 0);
try {
const returned = handle(req, res, next);
- if (returned !== void 0) {
+ if (returned !== void 0 && !(returned instanceof Promise)) {
resolve(returned);
} else {
res.once("close", next);
Suspect it is root cause of #21.
Related to #73
The current approach for universal compatibility is to take node req
and res
as references and emulate them with unenv. With the new Event interface, we can move towards the native web events.
H3Event
class wrapping req
and res
and implementing FetchEvent interface (minimal)H3EventRequest
class wrapping H3Event
and implimenting Request interface (minimal)i'm trying to upload file from client and save it locally on the server...
for the server side i' using busboy.... and it work correctly only with small files.... with large file i get an error:
ERROR [h3] write EPIPE 17:21:55
at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:94:16
Hi, it turns out I'm passing a blob through FormData and useBody isn't picking it up right. Is it supported?
Does h3 support all request methods? The examples only showcase get
-- tried looking through the code to see if I could glean any additional information but came up empty.
get
post
put
patch
delete
Hello, I have a nuxt app with a proxy middleware and server middleware using h3.
nuxt.config.js
axios: {
proxy: true,
},
proxy: {
'/api/': {
target: process.env.MYTOKEN,
pathRewrite: { '^/api/': '' },
logLevel: 'debug',
onProxyReq: (proxyReq: any) => {
proxyReq.path += '?api_key_token=' + process.env.MYTOKEN;
},
},
},
serverMiddleware: [
{ path: '/api', handler: '~/server/api.ts' }
],
api.ts
import { createApp } from 'h3';
const app = createApp();
app.use('/hello', () => {
return {
hello: 'world'
}
});
export default app;
All other /api
routes in the proxy returns 404. Only the /api/hello
custom server middleware works.
When using express
, everything works fine. I also tried to have 2 custom server middlewares using h3:
nuxt.config.js
serverMiddleware: [
{ path: '/api', handler: '~/server/api.ts' }
{ path: '/api', handler: '~/server/api-two.ts' }
],
api.ts
import { createApp } from 'h3';
const app = createApp();
app.use('/hello', () => {
return {
hello: 'world'
}
});
export default app;
api-two.ts
import { createApp } from 'h3';
const app = createApp();
app.use('/hi', () => {
return {
hi: 'world'
}
});
export default app;
The first one works but api-two.ts
returns 404. Do I need to setup something here? Thank you!
I'm trying to get started with h3 in a Cloudflare worker - are there any examples of how to get started with h3 in workers?
import { createApp } from 'h3'
const app = createApp()
app.use('/', () => 'Hello world')
addEventListener('fetch', (e) => {
// ???
})
Hello, when using useBody
I get a string instead of an object. Something like this field=value&another=value
. Is there an option to make this an object? What I currently do is this:
let body = await useBody(req)
body = Object.fromEntries(new URLSearchParams(body))
thanks
Hello!
How to use websocket server with h3?
Hi! Very interested on this. I can get express and the core http server to serve over HTTPS quite easily, but I... don't know enough about h3 to do that with your library? Is there documentation on the topic?
Not a preferred way but can cover more flavors of use and less unintended behavior. (linked from nuxt/nuxt#11769)
h3 was born with the idea of being a platform-agnostic server framework for all JavaScript environments and backward compatibility with legacy server middleware format for Node.js and Express compatibility.
Initially, we started with the most backward-compatible format (req, res)
and improved it by async support async (req, res)
and also directly returning a response for a pleasant experience with (req, res) => 'Hello World'
format.
unjs/unenv provides a createCall and mocks of Node.js HTTP
built-ins. Thanks to this mocking framework, h3 is able to work nicely on almost any serverless or Worker platform.
However, this kind of setup keeps h3 to be away from natively supporting non Node.js environments with the cost of unenv overhead for any code not using Node.js.
Additionally, (req, res)
format is not even suitable to extend h3 supporting Node.js features like http2 without compatibility API and ws
support.
Migration to (event)
format will be progressive and backward compatibility wherever necessary however we potentially have to introduce breaking changes as semver-minor until reaching [email protected]
.
defineEventHandler
(#74)(req, res)
implicit conversion (#178)event.req
and event.res
optional and adopt more of event native (#231)automatically sort by layer.route.length to avoid ordering issues (with an AppOption to disable)
Alternative: sort(app) | app.sort()
I tried setting ''
and undefined
to setCookie()
with no luck
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.
@types/node
, @vitest/coverage-v8
, eslint
, eslint-config-unjs
, iron-webcrypto
, pnpm
, vitest
, zod
)These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
docs/package.json
undocs ^0.2.21
.github/workflows/autofix.yml
actions/checkout v4
actions/setup-node v4
autofix-ci/action ea32e3a12414e6d3183163c3424a7d7a8631ad84
.github/workflows/ci.yml
actions/checkout v4
actions/setup-node v4
codecov/codecov-action v4
package.json
cookie-es ^1.1.0
crossws ^0.2.4
defu ^6.1.4
destr ^2.0.3
iron-webcrypto ^1.1.1
ohash ^1.1.3
radix3 ^1.1.2
ufo ^1.5.3
uncrypto ^0.1.3
unenv ^1.9.0
0x ^5.7.0
@types/express ^4.17.21
@types/node ^20.12.7
@types/supertest ^6.0.2
@vitest/coverage-v8 ^1.5.2
autocannon ^7.15.0
automd ^0.3.7
changelogen ^0.5.5
connect ^3.7.0
eslint ^9.1.1
eslint-config-unjs ^0.3.0-rc.6
express ^4.19.2
get-port ^7.1.0
jiti ^1.21.0
listhen ^1.7.2
node-fetch-native ^1.6.4
prettier ^3.2.5
react ^18.3.1
react-dom ^18.3.1
supertest ^6.3.4
typescript ^5.4.5
unbuild ^2.0.0
vitest ^1.5.2
zod ^3.23.4
pnpm 9.0.6
playground/package.json
Supporting http methods could be a nice DX addition but it implicitly adds runtime overhead if implemented using a wrapper function (#14). We might support a built-in filter similar to the filter we use for quick prefix matching.
Overall downside of this feature is that we will loose lazy loading on handles based on their acceptance method which might be important for serverless environments so i think might worth waiting to use h3
more in realistic projects before making decision.
Hello @pi0,
I know you are very busy with Nuxt 3 development, so I don't expect you would be able to fulfil this request anytime soon. Maybe this could be put on the back burner. Or maybe someone else can help me here.
What I'm looking for is for a starter template to get me started with using Node + H3 + Docker + Testing.
I'll be making a series of mini-applications with somewhat different API's. Some will support only one endpoint. Some will support a few endpoints. Some need to support POST and PUT on top of GET endpoints. Some of these endpoints will share functions. I've been using the /server
directory in Nuxt 3 and I feel like H3 would be a better choice for what I need instead of going with Express as seen in this example: https://github.com/petkivim/nodejs-rest-api-example
There is no frontend needed in these mini-apps. But what I would like is a good testing framework for the endpoints and utility functions.
I would love if each of my mini-apps had a structure that looked more or less like this:
/api
/api/endpoint1.js
/api/entity/endpoint2.js
/utils
/utils/function1.js
/tests
/tests/testFunction1.js
/tests/endpoint1.js
package.json
Dockerfile
And if I ran docker compose up
from this repo, I would be able to access localhost:3000/api/endpoint
and localhost:3000/api/entity/endpoint2
. I would also love to be able to run yarn test
and get my test suite to run all my tests.
I'm just very curious of the most minimal and ideal approach to setting this up. Any sort of guidance on best practice to accomplishing something close to what I want here would be very much appreciated :)
How do I use h3 to get files from client/browser for file upload?
I am coming from ExpressJs, where I can install a package called express-fileupload.
Link:
Express Fileupload
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
app.use(fileUpload())
Then
app.post ('/someApi' , (req, res) =>{
console.log(req.files)
})
How do I achieve this?
See type error here
@pi0
if (req.method == 'DELETE') {
var deleteIs = await useBody(req)
}
return 405 (HTTP method is not allowed.)
(we probably do need to add this partially to unjs/h3)
Hi, is there an equivalent of
app.use(express.static(`${root}/dist/client`, { index: false }))
in h3? Thanks
/.output/server/chunks/nitro/server.mjs:69
const hasReqHeader = (req, header, includes) => req.headers[header] && req.headers[header].toLowerCase().includes(includes);
^
TypeError: Cannot read properties of undefined (reading 'accept')
at hasReqHeader (file:///home/garant/admin/.output/server/chunks/nitro/server.mjs:69:60)
at Object.handleError [as onError] (file:///home/garant/admin/.output/server/chunks/nitro/server.mjs:71:25)
at nodeHandler (file:///home/garant/admin/.output/server/node_modules/h3/dist/index.mjs:319:23)
Is there a way to implement session in h3?
req.session => object
This way, use
is directly for async/await
which is becoming a standard.
use(req, res)
useLegacy(req, res, next)
Hi, I am trying to stream files from local disc (for a reason it's not served as static).
I tried to use sendStream or send it directly to pipe but each time I get 404. Documentation is not mentioning too much how to use streams. How to use it correctly with h3?
export default defineEventHandler((event) ->
const stream = fs.createReadStream(path);
stream.pipe(event.res);
)
or
const stream = fs.createReadStream(path);
sendStream(event, stream);
I'm getting an error when I try to use nuxi and @nuxt/bridge-edge to run a server; the error appears to be on layer.handle.length
if layer.handle
is undefined
using
"@nuxt/bridge": "@nuxt/[email protected]",
node v14.18.0
FATAL Cannot read property 'length' of undefined
at normalizeLayer ([app]/node_modules/h3/dist/index.mjs:625:36)
at use ([app]/node_modules/h3/dist/index.mjs:580:20)
at Function.app.use ([app]/node_modules/h3/dist/index.mjs:567:35)
at createDevServer ([app]/node_modules/@nuxt/nitro/dist/index.mjs:1796:7)
at createNuxt2DevServer ([app]/node_modules/@nuxt/bridge-edge/dist/chunks/module.mjs:269:18)
at setupNitroBridge ([app]/node_modules/@nuxt/bridge-edge/dist/chunks/module.mjs:163:19)
at setup ([app]/node_modules/@nuxt/bridge-edge/dist/chunks/module.mjs:1054:13)
at ModuleContainer.normalizedModule ([app]/node_modules/@nuxt/kit/dist/index.mjs:501:29)
// file ([app]/node_modules/h3/dist/index.mjs:625:36)
function normalizeLayer(layer) {
if (layer.promisify === void 0) {
layer.promisify = layer.handle.length > 2;
}
return {
route: withoutTrailingSlash(layer.route),
match: layer.match,
handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify ? promisifyHandle(layer.handle) : layer.handle
};
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.