Skip to content

pomle/paths

Repository files navigation

Paths

A JavaScript and TypeScript lib for handling type rich URLs in web apps.

Usage

Paths

Start by defining paths. Paths are usually created once in a single module, then imported for usage.

import { createPath, codecs } from "@pomle/paths";

const user = createPath("/user/:userId", {
  userId: codecs.string,
});

const userPost = user.append("/posts/:postId", {
  postId: codecs.number,
});

export const paths = {
  user,
  userPost,
};

Create URLs

import { paths } from "./paths";

const url = paths.userPost.build({
  userId: "pomle",
  postId: 24,
});

console.log(url); // Prints "/user/pomle/posts/24"

Parse path params from URLs

import { paths } from "./paths";

const values = paths.userPost.parse("/user/pomle/posts/24");

console.log(values); // Prints {userId: "pomle", postId: 24}

Non-matching or under-matching returns null

import { paths } from "./paths";

// Returns null
paths.userPost.parse("/not/user/posts");

// Returns null
paths.userPost.parse("/user/pomle");

Decode params already parsed

import { paths } from "./paths";

const values = paths.userPost.decode({
  userId: "pomle",
  postId: "24",
});

console.log(values); // Prints {userId: "pomle", postId: 24}

Query

Define query params

import { createQuery, codecs } from "@pomle/paths";

const searchFilter = createQuery({
  username: codecs.string,
  range: codecs.number,
});

Parse query string from URL

// Location ?username=Pontus+Alexander&range=3&range=5

const values = searchFilter.parse(window.location.search);

console.log(values);
/*
{
  username: ["Pontus Alexander"],
  range: [3, 5]
}
*/

Update query string

// Location ?username=Pontus+Alexander&range=3&range=5

const qs = searchFilter.build({
  username: ["Pontus Alexander"],
  range: [3, 5],
});

console.log(qs);
// ?username=Pontus+Alexander&range=3&range=5

Codecs

There are 4 codecs bundled

import { codecs } from "@pomle/paths";

codecs.string

Encodes strings safely

import { createQuery, codecs } from "@pomle/paths";

const searchOptions = createQuery({
  query: codecs.string,
});

codecs.boolean

Encodes true/false as "0" and "1"

import { createQuery, codecs } from "@pomle/paths";

const viewOptions = createQuery({
  showAll: codecs.boolean,
});

codecs.number

Encodes number safely

import { createQuery, codecs } from "@pomle/paths";

const searchOptions = createQuery({
  pageSize: codecs.number,
  pageNo: codecs.number,
});

codecs.oneOf

Ensures values are in a list of known values. If no match is found, first is returned.

import { createQuery, codecs } from "@pomle/paths";

const CAR_TYPES = ["sedan", "hatchback", "truck"] as const;

const carType = codecs.oneOf(CAR_TYPES);

const carOptions = createQuery({
  type: carType,
});

Creating custom codecs

Do not encode and decode with encodeURIComponent or decodeURIComponent manually. It will be handled by the library.

import { createPath, createCodec } from "@pomle/paths";
import { DateTime } from "luxon";

const dateCodec = createCodec<DateTime>(
  (date) => {
    return date.toISO();
  },
  (text: string) => {
    return DateTime.fromISO(text);
  },
);

const bookings = createPath("/bookings/:dayOfArrival", {
  dayOfArrival: dateCodec,
});

const url = bookings.url({
  dayOfArrival: DateTime.now(),
});

Codec Tips!

The oneOf codec can take enums.

enum State {
  Low,
  Medium,
  High,
}

const states = [State.Low, State.Medium, State.High] as const;
const intensity = codecs.oneOf(states);

const options = createQuery({
  strength: intensity,
});

The oneOf codec can take any object that implements .toString.

const MyTypeOne = {
  toString() {
    return "one";
  },
};

const MyTypeTwo = {
  toString() {
    return "two";
  },
};

const states = [MyTypeOne, MyTypeTwo] as const;
const state = codecs.oneOf(states);

About

TypeScript library for handling paths and query strings in URLs with rich types.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published