npm package: @qezor/fastserve
Description: HTTP-first routing and middleware framework for Node.js and Fetch-style runtimes.
Author: Abdul Ahad
Latest version: 1.0.7

# @qezor/fastserve

HTTP-first routing and middleware framework for Node.js and Fetch-style runtimes.

`fastserve` is built on:
- `@qezor/urlex` for host + path routing
- `@qezor/reqex` for request parsing
- `@qezor/resmake` for response building

It is intentionally focused on HTTP, Fetch, middleware, static delivery, proxying, and WebSocket upgrades.
Raw TCP/UDP/Unix socket handling belongs in `@qezor/nettool`, not in `fastserve` itself.

## Installation

```bash
npm install @qezor/fastserve
```

## Quick Start

```js
const { createApp } = require("@qezor/fastserve")

const app = createApp()

app.get("/", (ctx, res) => {
  res.json({ hello: "world" })
})

app.get("/users/:id", (ctx, res) => {
  res.json({ id: ctx._params.id })
})

app.listen(3000)
```

## Handler Signature

```js
(ctx, res, next, ...extras)
```

- `ctx`: HTTP request context
- `res`: response context from `@qezor/resmake`
- `next`: continue middleware chain or forward error with `next(err)`
- `extras`: runtime-specific extras from `createApp({ extras })`

When using positional route APIs like `app.get(...)`, keep error handlers as 4-argument functions and read runtime extras through the trailing `...extras` rest parameter on normal handlers.

## Request Context

`ctx` includes:
- `method`
- `url`
- `path`
- `host`
- `protocol`
- `query`
- `cookies`
- `headers`
- `ip`
- `_params`

Helpers:
- `get(name)`
- `hasBody()`
- `isUpgrade()`
- `isPreflight()`
- `rawBody(options?)`
- `body(options?)`
- `bodyStream()`

## Routing

```js
app.get("/posts", handler)
app.post("/posts", createHandler)
app.any("/catch-all", handler)

app.add({
  method: "GET",
  pattern: "/admin",
  when: ({ host }) => host[0] === "app",
  handler,
})
```

Pattern matching is powered by `@qezor/urlex`, so host-aware and capture-heavy route patterns are supported.

## Middleware

```js
app.use(logger)
app.use("/api/**", auth)

app.get("/private", requireAuth, handler)

app.onError((err, ctx, res) => {
  res.json({ error: err.message }, 500)
})
```

Built-ins:
- `cors(opts)`
- `bodyLimit(opts)`
- `static(dir, opts)`
- `validate(opts)`
- `rateLimit(opts)`
- `csrf(opts)`
- `proxy(upstreamName, opts)`

## Groups And Mounting

```js
app.group("/api", (g) => {
  g.get("/health", handler)
  g.get("/users/:id", handler)
})

const admin = createApp()
admin.get("/", handler)

app.mount("/admin", admin)
```

## Fetch Runtimes

```js
const app = createApp({
  extras: (request, env, executionCtx) => [env, executionCtx],
})

export default {
  fetch(request, env, executionCtx) {
    return app.fetch(request, env, executionCtx)
  },
}
```

## AWS Lambda

Use the optional subpath export when you want to run a `fastserve` app behind API Gateway or Lambda Function URLs without pulling AWS utility code into the core package.

```js
const { createApp } = require("@qezor/fastserve")
const { createLambdaHandler } = require("@qezor/fastserve/lambda")

const app = createApp()

app.get("/health", (ctx, res) => {
  res.json({ ok: true })
})

exports.handler = createLambdaHandler(app, {
  basePath: "/prod",
  binaryTypes: ["image/*", "application/octet-stream"],
})
```

`@qezor/fastserve/lambda` is intentionally thin:
- HTTP event -> request bridge
- `app.fetch()` integration
- response -> Lambda result shaping

Broader AWS/Lambda helper utilities should live in `@qezor/lambase`, not inside `fastserve` itself.

## WebSocket Upgrade Routes

```js
app.ws("/chat/:room", {
  onConnect(ws, ctx) {
    ws.send("connected")
  },
  onMessage(ws, message, ctx) {
    ws.send("echo: " + message)
  },
})
```

## HTTP/2

```js
app.http2({
  port: 443,
  key: keyPem,
  cert: certPem,
})
```

## Runtime Adapter

```js
const { setRuntimeAdapter } = require("@qezor/fastserve")
```

Adapter fields:
- `Buffer`
- `URL`
- `setTimeout`
- `clearTimeout`
- `setInterval`
- `clearInterval`
- `requireBuiltin(name)`
- `reportError(error, meta?)`

## Notes

- `fastserve` is HTTP-first by design.
- Raw net/socket work should be composed with `@qezor/nettool`, not built directly into `fastserve`.
- `bodyLimit()` can enforce limits even when `content-length` is missing.
- `proxy()` forwards request bodies using stream paths where available.