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

@lfgroup/query-coder

v0.4.3

Published

URL query coder-decoder

Downloads

7

Readme

@lfgroup/query-coder

URL query coder/decoder with configurable user pattern. It provides the most comfortable experience for encoding and decoding complex nested filter objects. Check out example for more info.

  1. Quickstart
  2. Advanced example
  3. Usage
    1. QueryCoder
      1. Encoding and decoding
      2. Decoding with a default value
    2. QueryHandler
      1. Ignoring required fields
      2. Defining value type
      3. Aliasing query values
      4. Encoding non-string values
      5. Accessing handlers outside of a coder
      6. Using handlers between different coders
      7. Excluding values from encoding

Quickstart

yarn add @lfgroup/query-coder

Query coder is designed to repeat an interface of provided object with QueryCoder, only replacing tree leaves with parser instances — QueryHandlers.

WIP

Advanced example

WIP

Imaging having a such interface for your querying filters:

type SearchGroupsFilter = {
  gameId?: "WorldOfWarcraft" | "WildRift" | "LostArk";
  gameMode?: GameMode;
  from?: Date;
  language?: Language;

  lostArk?: LostArkFilterInput;
  wildRift?: WildRiftFilterInput;
  wow?: WowFilterInput;
};

Let's take 1 example of this:

const filters: SearchGroupsFilter = {
  gameId: "WorldOfWarcraft",
  gameMode: GameMode.WowMythicPlus,
  language: Language.En,
  wow: {
    faction: WowFaction.Alliance,
    dungeon: WowDungeon.MistsOfTirnaScithe,
    minRioRating: 1400,
    region: WowRegion.Europe,
  },
};

Which should result in this:

const query =
  "game=wow&mode=mplus&language=en&faction=alliance&dungeon=mots&rating=1400&region=eu";

So we want to:

  • flatten nested filter object in a query string
  • rename certain keys (gameMode -> mode)
  • re-map certain values (WorldOfWarcraft -> wow)

With this lib, you can init QueryCoder and that's it! 🎉

import { QueryCoder, QueryHandler, Type } from "@lfg/query-coder";

// passing generic interface checks, whether node keys
// are the same as in provided interface
const query = new QueryCoder<SearchGroupsFilter>({
  gameId: new QueryHandler({
    query: "game",
    aliases: {
      WorldOfWarcraft: "wow",
      WildRift: "wr",
      LostArk: "la",
    },
  }),
  gameMode: new QueryHandler({ query: "mode" }),
  language: new QueryHandler({ query: "language" }),
  wow: {
    faction: new QueryHandler({
      query: "faction",
      /**
       * decodeCondition is Partial of generic
       * if decodeCondition is set, search query would be handled
       * with this handler only if decodeCondition matches query
       * For more info, check out section below
       */
      decodeCondition: { gameId: "WorldOfWarcraft" },
    }),
    dungeon: new QueryHandler({
      query: "dungeon",
      decodeCondition: { gameId: "WorldOfWarcraft" },
    }),
    minRioRating: new QueryHandler({
      query: "minRioRating",
      decodeCondition: { gameId: "WorldOfWarcraft" },
      /**
       * You should provide a primitive type, which is different from string
       * Otherwise, url query "foo=313" can result in an unexpected result
       */
      decodeType: Type.Number,
    }),
    region: new QueryHandler({
      query: "region",
      decodeCondition: { gameId: "WorldOfWarcraft" },
    }),
  },
});

query.encode(filters).toString(); // should result in query variable
query.decode(query); // should result in filters variable

Usage

1. QueryCoder

QueryCoder is a main instance you will use to encode and to decode your data. Typically, you should create coders at a global scope and provide a generic of an interface you are planing to encode/decode.

import { QueryCoder, QueryHandler } from "@lfg/query-coder";


interface ObjectToSerialize {
  foo: string;
  bar: {
    baz: string;
  };
}

const coder = new QueryCoder<ObjectToSerialize>({
  foo: new QueryHandler({ query: "foo_in_query" }),
  bar: {
    baz: new QueryHandler({ query: "baz_in_query" }),
  },
});

Encoding and decoding

Using a coder we've created above we can encode object to url query or decode query string to an object:

const object: ObjectToSerialize = {
  foo: 'value of foo',
  bar: {
    baz: 'bazz-value'
  },
};

// Encoding with .encode(T) method, which returns URLSearchParams
const urlSearchParams = coder.encode(object);
console.log(urlSearchParams.toString()); // foo_in_query=value%20of%20foo&baz_in_query=bazz-value

// Decoding with .decode(string) method, which returns T
const decodedObject = coder.decode(urlSearchParams.toString());
console.log(decodedObject); // same as var `object`

Decoding with a default value

Sometimes you may want to provide a default value for decoding proccess to fill required gaps of an interface. Providing default value will deep assign decoded object to default object, overwriting default value with decoded if any provided.

const query = `foo_in_query=url-value`


const defaultObj: ObjectToSerialize = {
  foo: "default",
  bar: {
    baz: "default",
  },
};

const decodedObj = coder.decode(query, { defaultValue: defaultObj });

console.log(decodedObj) // { foo: "url-value", bar: { baz: "default" } }

2. QueryHandler

QueryHandler is a handler for each node leaf of a codable object. It must include a name for a query key and provides additional options. You should mind that:

  • Only leaves, that have QueryHandler will be encoded/decoded
  • Values that are not strings should has decodeType param

interface ObjectToSerialize {
  foo: string;
  bar: {
    baz: string;
  };
}

const coder = new QueryCoder<ObjectToSerialize>({
  foo: new QueryHandler({ query: "foo_in_query" }),
  bar: {
    baz: new QueryHandler({ query: "baz_in_query" }),
  },
});

Ignoring required fields

Refer decode default

Defining value type

Aliasing query values

Encoding non-string values

Accessing handlers outside of a coder

Using handlers between different coders

Excluding values from encoding