npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@thecodepace/fastify-http-query

v0.0.4

Published

Plugin to enable HTTP query method in Fastify.

Readme

@thecodepace/fastify-http-query

CI NPM version neostandard javascript style

Fastify HTTP QUERY plugin; with this you can enable the HTTP QUERY method in Fastify.

QUERY is a safe, idempotent, cacheable HTTP method that — unlike GET — carries a request body describing the query operation. It is defined by the IETF draft HTTP QUERY Method (draft-ietf-httpbis-safe-method-w-body).

Install

npm i @thecodepace/fastify-http-query

Requirements

Requires a Node.js version that lists QUERY in http.METHODS (Node.js >= 22), so the HTTP parser accepts incoming QUERY requests.

Compatibility

| Plugin version | Fastify version | | -------------- | --------------- | | >=1.x | ^5.x |

Please note that if a Fastify version is out of support, then so are the corresponding versions of this plugin in the table above. See Fastify's LTS policy for more details.

Usage

Register the plugin before declaring any QUERY route. The plugin adds the QUERY method to Fastify (via addHttpMethod) and exposes the query route shorthand:

import Fastify from 'fastify'
import fastifyHttpQuery from '@thecodepace/fastify-http-query'

const app = Fastify()
await app.register(fastifyHttpQuery)

// QUERY route — the body carries the query, just like GET carries the URL.
app.query('/search', {
  schema: {
    body: {
      type: 'object',
      properties: { q: { type: 'string' } },
      required: ['q']
    }
  }
}, async (request) => {
  return runSearch(request.body.q)
})

await app.listen({ port: 3000 })
curl -X QUERY http://localhost:3000/search \
  -H 'content-type: application/json' \
  --data '{"q":"fastify"}'

The plugin has no options.

Specification compliance

The plugin is strict about the parts of the specification a server is required to enforce:

| Behavior | Result | | --- | --- | | QUERY route shorthand + supportedMethods entry | provided | | Request body is parsed (per the request Content-Type) | provided | | Request without a Content-Type header | rejected with 400 (FST_ERR_QUERY_MISSING_CONTENT_TYPE) | | Request without a body | rejected with 400 (FST_ERR_QUERY_EMPTY_BODY) | | Unsupported media type | 415 (Fastify's native content-type handling) | | Cacheable responses / Cache-Control | via @fastify/caching — see below | | Conditional requests (ETag / 304) | via @fastify/etag — ETag from results | | Content-Location on 2xx | set by your handler — see below | | Range / partial requests | not implemented (spec §2.8) |

Caching & conditional requests

QUERY responses are cacheable and support conditional requests, just like GET. This plugin follows Fastify's model: core enables the method; caching is composed from the ecosystem — exactly as you would cache GET. There is no bespoke cache here.

Conditional requests (ETag / 304) with @fastify/etag

@fastify/etag computes an ETag from the response payload and answers If-None-Match with 304. Because the ETag is derived from the results, two QUERY requests with different bodies naturally produce different ETags — so conditional handling is correct for QUERY with no extra configuration.

import fastifyHttpQuery from '@thecodepace/fastify-http-query'
import etag from '@fastify/etag'

await app.register(fastifyHttpQuery)
await app.register(etag)

app.query('/search', (request) => runSearch(request.body))
// QUERY /search {"q":"a"}                         -> 200, ETag: "…"
// QUERY /search {"q":"a"}  If-None-Match: "…"      -> 304 Not Modified
// QUERY /search {"q":"b"}  If-None-Match: "<a>"    -> 200 (different results)

Cache-Control with @fastify/caching

@fastify/caching manages Cache-Control/Expires and provides reply.etag():

import caching from '@fastify/caching'

await app.register(caching, { privacy: caching.privacy.PRIVATE, expiresIn: 3600 })
// QUERY responses now carry: Cache-Control: private, max-age=3600

⚠️ Shared-cache caveat (body-keying)

The spec requires a cache key that incorporates the request body. Origin-side tools above key on the results and are safe. However, shared intermediary caches (CDNs/proxies) key on method + URL only and are not body-aware — they can serve the wrong result for a different body sent to the same URL. So do not let an untrusted shared cache store QUERY responses: keep them private / no-store at the edge, or place a body-aware cache in front. (Fastify likewise does not manage downstream caches for GET.)

Content-Location (spec §2.3)

A successful response may name a GET-able resource for the results. Set it in your handler:

app.query('/search', async (request, reply) => {
  const { id, results } = await runSearch(request.body)
  reply.header('content-location', `/search/results/${id}`)
  return results
})

Not implemented

  • Range/partial requests (spec §2.8) — the spec itself notes byte ranges "offer little value" for query results; use your query format's own paging.

Safety & idempotency

QUERY is safe (it does not request a change to the target resource) and idempotent. Treat your QUERY handlers accordingly — do not mutate state.

Commit conventions

This project uses Conventional Commits, enforced locally by lefthook + commitlint and in CI by .github/workflows/commitlint.yml.

  • commit-msg hook → commitlint (config in commitlint.config.mjs, extends @commitlint/config-conventional).
  • pre-commit hook → eslint on staged files, then npm test.
  • On every PR, the Lint commits workflow runs two jobs:
    • commitlint — lints every commit in the PR with commitlint --from <base> --to <head>.
    • pr-title — lints the PR title itself with wagoid/commitlint-github-action, using the same commitlint.config.mjs. This matters when the PR is squash- or rebase-merged, since the title becomes the commit message on main.

Allowed types follow the conventional preset (feat, fix, chore, docs, style, refactor, perf, test, build, ci, revert).

After cloning, run npm install (or npx lefthook install manually if ignore-scripts=true is set in .npmrc) to install the Git hooks.

Releasing

Releases are automated by the release workflow (.github/workflows/release.yml), which uses release-it with the @release-it/conventional-changelog plugin. The version bump and CHANGELOG.md are derived from the Conventional Commits history, and the GitHub release is created from the same changelog. Publishing to npm uses OIDC (trusted publishing), so no npm token is stored in the repo.

Release flow

  1. Merge your feature/fix PRs into main as usual.
  2. Trigger the release workflow from the Actions tab (workflow_dispatch).
  3. release-it computes the next SemVer bump from the commits since the last tag, updates CHANGELOG.md, commits (chore(release): vX.Y.Z), tags, pushes, creates the GitHub release (with the conventional-commit notes), and publishes the package to npm via OIDC — all in one run.

Previewing locally

| Script | Purpose | | --- | --- | | npm run release:dry | Preview the next version, changelog, and tag without writing anything. | | npm run release | Run release-it interactively (prompts for bump override). | | npm run release:ci | Run release-it non-interactively. Used by the workflow. |

Forcing a specific bump

To override the auto-detected bump, pass --release-version to release-it (e.g. npx release-it --release-version=0.1.0). This is useful for the occasional case where a feat: was missed or you want a hotfix patch on top of a feature-only window.

License

Licensed under MIT.