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

@metreeca/qest

v0.13.0

Published

Foundations for client-driven, queryable REST/JSON APIs.

Readme

@metreeca/qest

npm

Foundations for client-driven, queryable REST/JSON APIs.

@metreeca/qest standardises critical capabilities that vanilla REST/JSON APIs typically lack or implement in ad-hoc, non-portable ways:

  • Client-Driven: clients specify what they need, retrieving complex envelopes in a single call
  • Queryable: advanced filtering and aggregation, supporting faceted search and analytics
  • Localised Content: full support for internationalised content with language-tagged dictionaries

Developers seek these features in frameworks like GraphQL; @metreeca/qest brings them to REST/JSON, achieving:

  • Familiar Patterns: standard REST and JSON conventions, no new paradigms to learn
  • Simple Clients: no specialised libraries, preprocessors, or code generators
  • Automated Servers: model-driven development, dramatically reducing implementation effort
  • Standard Caching: compatibility with CDNs and browser caches using standard GET requests

For a formal specification of the data model and query protocol, see the QEST: Queryable REST/JSON APIs memo.

Ecosystem

@metreeca/qest focuses on semantics and core data types, leaving applications free to handle validation, storage, and publishing as they see fit; its standardised data model is the foundation of an integrated ecosystem that delivers a powerful model-driven stack for rapid development of linked data applications:

| Package | Description | |-----------------------------|----------------------------------------------------------------| | @metreeca/qest | Foundations for client-driven, queryable REST/JSON APIs | | @metreeca/blue | Declarative blueprints for model-driven linked data processing | | @metreeca/keep | Turnkey model-driven storage for linked data resources | | @metreeca/gate (upcoming) | Zero-code model-driven endpoints for linked data resources | | @metreeca/tile (upcoming) | Minimalist model-driven UI toolkit for linked data resources |

Installation

npm install @metreeca/qest

[!WARNING]

TypeScript consumers must use "moduleResolution": "nodenext"/"node16"/"bundler" in tsconfig.json. The legacy "node" resolver is not supported.

Usage

[!NOTE]

This section introduces essential concepts; for complete coverage, see the API reference:

| Module | Description | |--------------------------|----------------------------------| | @metreeca/qest | Shared options and defaults | | @metreeca/qest/state | Resource state representation | | @metreeca/qest/model | Client-driven resource retrieval |

@metreeca/qest types define payload semantics and formats for standard REST operations:

| Method | Type | Description | |--------|--------------|----------------------------------| | GET | Resource | Resource retrieval | | GET | Resource | Collection retrieval | | GET | Template | Client-driven resource retrieval | | POST | Resource | Resource creation | | PUT | Resource | Complete resource state update | | DELETE | none | Resource deletion |

Resources

A Resource is a property map describing the state of a resource, with optional links to other resources:

GET https://data.example.com/products/123
{
  "id": "https://data.example.com/products/123",
  "name": "Widget",
  "category": "Electronics",
  "tags": [
    "gadget",
    "featured"
  ],
  "vendor": "https://data.example.com/vendors/456",
  "price": 99.99,
  "inStock": true
}

The same format is used for complete resource updates:

PUT https://data.example.com/products/123
({
    name: "Widget",
    category: "Electronics",
    tags: ["gadget", "premium"],
    vendor: "https://data.example.com/vendors/456",
    price: 79.99,
    // inStock                     // not included → deleted
});

Client-Driven Retrieval

Client-driven retrieval lets clients specify exactly what data to retrieve from both single resources and collections. Expansions and nested queries can be arbitrarily deep: no over-fetching of unwanted fields, no under-fetching requiring additional calls to resolve linked resources.

This is the core contribution of @metreeca/qest: vanilla REST/JSON APIs lack a standard way for clients to control retrieval, forcing them to accept fixed server responses or rely on ad-hoc query parameters. Client-driven retrieval fills this gap, supporting precise control over responses while remaining fully compatible with standard HTTP caching.

[!IMPORTANT]

Client-driven retrieval is fully optional. Servers may provide defaults, typically derived from the underlying data model, preserving standard REST/JSON behaviour while enabling advanced capabilities when needed.

Resources — A Template specifies which properties to retrieve from a single resource and how deeply to expand linked resources.

GET https://data.example.com/products/123?<template>

where <template> is the URL-encoded JSON Template:

({
    id: {},
    name: {},
    price: {},
    vendor: {
        id: {},
        name: {},
    },
});

The response includes only the requested properties, with the linked vendor expanded to show just id and name:

{
  "id": "https://data.example.com/products/123",
  "name": "Widget",
  "price": 99.99,
  "vendor": {
    "id": "https://data.example.com/vendors/145",
    "name": "Acme"
  }
}

Collections — A collection is reached through the resource that owns it, and constrained there: the entry naming it carries the Criteria for filtering, sorting, and pagination alongside the per-item keys. Those keys are a nested Template, or a Projection for computed aggregates (faceted search and analytics).

GET https://data.example.com/products/?<template>

where <template> is the URL-encoded JSON Template whose items entry retrieves the collection and constrains it:

({
    items: {

        id: {},
        name: {},
        price: {},
        vendor: {
            id: {},
            name: {},
        },

        ">=price": 50,                // filter: price ≥ 50
        "<=price": 150,               // filter: price ≤ 150
        "^price": "asc",              // sort: by price ascending
        "#": 25,                      // limit: 25 results

    },
});

A single call returns exactly what the client requested:

  • projected: product id, name, price
  • expanded: linked vendor with only id and name (not its full state)
  • filtered: price between 50 and 150
  • sorted: by price ascending
  • paginated: up to 25 results
{
  "items": [
    {
      "id": "https://data.example.com/products/456",
      "name": "Gadget",
      "price": 59.99,
      "vendor": {
        "id": "https://data.example.com/vendors/145",
        "name": "Acme"
      }
    },
    {
      "id": "https://data.example.com/products/123",
      "name": "Widget",
      "price": 99.99,
      "vendor": {
        "id": "https://data.example.com/vendors/145",
        "name": "Acme"
      }
    },
    {
      "id": "https://data.example.com/products/789",
      "name": "Gizmo",
      "price": 129.99,
      "vendor": {
        "id": "https://data.example.com/vendors/236",
        "name": "Globex"
      }
    }
  ]
}

Analytics — A Projection replaces the nested template with computed property bindings, enabling faceted search and analytics in a single call.

GET https://data.example.com/products/?<template>

where <template> groups products by category, counting each group and sorting by count:

({
    items: {
        "category=category": {},         // grouping column (non-aggregate)
        "count=count:": {},              // count per group
        "^count:": "desc",               // sort groups by count descending
    },
});

A single call returns pre-aggregated category counts, ready for UI faceting or analytics dashboards:

{
  "items": [
    {
      "category": "Electronics",
      "count": 150
    },
    {
      "category": "Home",
      "count": 89
    },
    {
      "category": "Garden",
      "count": 34
    }
  ]
}

Localised Properties

Resource properties can hold localised text in a dictionary: a language map associating BCP 47 language tags with text values:

{
  "id": "https://data.example.com/products/123",
  "name": {
    "en": "Widget",
    "fr": "Bidule"
  },
  "description": {
    "en": [
      "Compact",
      "Durable"
    ],
    "fr": [
      "Compact",
      "Résistant"
    ]
  }
}

A Dictionary supports both single-valued and multi-valued forms per language. Within a single dictionary, all values must be uniformly scalar or uniformly array, in the form the property declares: a state carrying the other form under any tag is rejected as malformed. Language-neutral values are tagged with the und (Undetermined) language tag:

({
    name: { und: "Widget" },
    tags: { und: ["compact", "durable"] },
});

Projections can target localised properties through tag-range placeholders. Each row carries a complete Dictionary value (the entries for the tags the binding's pattern matches) rather than fanning out one row per tag:

({
    items: {
        "id=id": {},
        "label=title": { "*": {} },         // all matching tags, as one Dictionary
    },
});

Localised properties can also be filtered and sorted through their coalesced label: the value, or values, resolved from the dictionary by a request-level language priority (derived server-side, for example from Accept-Language). Search (~) acts on the resolved label, and sort order (^) and focus (+) on its single-valued form; the priority is supplied out of band, never in the query. See the memo for the full semantics.

JSON-LD Foundations

JSON-LD (JSON for Linked Data) is a W3C standard for publishing linked data on the web. It extends JSON with web identifiers (IRIs) to link resources across systems and domains, and to give property names precise, machine-readable meaning by mapping them to shared vocabularies, a capability at the heart of the Web Data Activity (Semantic Web) and modern knowledge graphs.

@metreeca/qest defines a controlled JSON-LD subset designed to feel like plain idiomatic JSON, letting JavaScript developers work with linked data using familiar REST/JSON patterns without mastering JSON-LD technicalities, while retaining full compatibility with standard JSON-LD processors.

This controlled subset is specified by:

  • compacted documents with short property names and nested objects, just like regular JSON
  • ECMAScript identifiers as property names (terms), enabling dot notation access; JSON-LD keywords (@id, @type, etc.) and blank node identifiers are not allowed and must be mapped to identifiers via an application-provided @context (for example, "id": "@id"); @context must also map property names to IRIs for semantic interoperability
  • native JSON primitives (boolean, number, string) as values; typed literals with arbitrary datatypes are not allowed and must be represented as strings with datatype coercion declared in @context
  • language maps for localised text; @none keys for non-localised values in language maps are not allowed and must be handled using the und (Undetermined) language tag
  • IRI references for linking resources across systems and domains; data structures require absolute IRIs; codec functions handle conversion to/from root-relative forms

Support

  • Open an issue to report a problem or to suggest a new feature
  • Start a discussion to ask a how-to question or to share an idea

License

This project is licensed under the Apache 2.0 License. See LICENSE for details.