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 🙏

© 2024 – Pkg Stats / Ryan Hefner

jsonapi-zod-query

v2.0.0

Published

Utilities to work with JSON:API on the frontend

Downloads

418

Readme

JSON:API Query Helpers utilizing Zod

Release codecov

This package helps you to work with JSON:API 1.1 compliant API servers. In order to validate and parse responses, it is assumed that you have zod installed.

There are no assumption made about how you query your data, except that you are using fetch. Selectors from this library can be used with e.g. TanStack Query.

Selectors from this library will flatten down all resources and relationships. This makes it easier to work with those entities in the frontend.

Installation

npm

npm i jsonapi-zod-query

pnpm

pnpm add jsonapi-zod-query

Usage

At its core you create selectors there are three kinds of selectors you can create, namely resource, nullable resource and resource collection. All selectors take the same configuration but will yield different parsers.

First you define the primary resource of the document. A resource is defined by its type and optionally an attributes schema and a relationships definition.

Simple example

In this example we show off how to create a selector for a single resource with only attributes defined.

import { z } from "zod";
import { createResourceSelector } from "jsonapi-zod-query";

const articleSelector = createResourceSelector({
    type: "article",
    attributesSchema: z.object({
        title: z.string(),
    }),
});

You can now use the selector in your query functions like this:

const response = await fetch("https://example.com");
const body = await response.json() as unknown;
const document = articleSelector(body);

Response error handling

Of course, the example above assumes that the response is always successful. In the real world you cannot make that assumption. For this reason there is a utility function which automatically handles errors for your:

import { handleJsonApiError } from "jsonapi-zod-query";

const response = await fetch("https://example.com");
await handleJsonApiError(response);

If the request is successful (2xx range), the function call is a no-op. Otherwise, it will try to parse the error response and throw a matching JsonApiError.

Nullable and collection selectors

If a response can contain a nullable primary resource, you want to use createNullableResourceSelector() instead. If the response is for a resource collection, you must use createResourceCollectionSelector().

They are configured the exact same way as createResourceSelector().

Extracting data

The resource selectors will always return the entire (flattened) document. In most cases you might only be interested in the data property. To facilitate this you can wrap the selector:

import { createDataSelector, createResourceSelector } from "jsonapi-zod-query";

const articleSelector = createDataSelector(createResourceSelector(/* … */));

Typing document meta data

By default, document metadata are considered as an optional record of unknown properties. You can pass a documentMetaSchema option to resource selector creators, which will enforce a specific schema.

Handling pagination

This library assumes that you never actually use the links properties in the JSON:API documents, but are primarily interested in the pagination functionality for your own queries. Page params are automatically extracted by the selector created through createResourceCollectionSelector().

You can access the page parameters through the pageParams properties, which will contain the parameters defined in the links through the first, prev, next and last properties.

You can pass these parameters to your query function. Before performing your fetch, you have to inject the parameters into the URL again:

import { injectPageParams } from "jsonapi-zod-query";

const url = new URL("https://example.com");
injectPageParams(pageParams);

Relationships

You can define relationships for each resource through the relationships object. Each key matches the field name in the JSON:API body and an object defines how the relationship should be handled.

You must always define a relationshipType, which can be either one, one_nullable or many. Additionally, you must define one of the following two properties:

  • resourceType

    When defining the resource type, the relationship is considered be just an identifier. In this case it will result in an entity with just an id defined.

  • include

    If the response document contains included resource, you can define this to inline the resource into the result. This parameter has the same configuration as the primary resource.

TypeScript limitation

Due to limitations in TypeScript, the configuration fails to apply type hinting for relationships within relationships. To work around this, you can utilize the satisfies operator:

const selector = createResourceSelector({
    type: "article",
    relationships: {
        author: {
            resourceType: "person",
            relationshipType: "one",
            include: {
                type: "person",
                relationships: {
                    profile: {
                        relationshipType: "one",
                        include: {
                            type: "profile",
                            attributesSchema: z.object({
                                emailAddress: z.string(),
                            }),
                        },
                    },
                } satisfies Relationships,
            },
        },
    },
});

Resource links and meta

If you want to retrieve links or meta for resources, you can specify linksSchema and metaSchema respectively. Once defined, you'll either get a $links or $meta property on your resources. The $ prefix is a design decision in order to avoid name clashes with attributes or relationships with the same name.