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

typescript-object-schema

v1.0.0

Published

<div align="center"> <h1> TypeScript Object Schema <br/> <br/> </h1> <br/> <a href="https://www.npmjs.com/package/typescript-object-schema"> <img src="https://img.shields.io/npm/v/typescript-object-schema.svg" alt="npm package" />

Downloads

6

Readme

Motivation

TypeScript is a powerful assistant when your are developping, with autocomplete suggestion and complilation errors. I made some utils to define a schema object and then avoid to always import types or interfaces if your shema doesn't change.

Tutorial

My use case come from HTTP request pattern. Our REST API schema is always the same and can be define in one place in the application. Let's see our requirement for make HTTP request:

  • the request need an url to work correctly.
  • the request have a specific method, like GET, POST, PATCH...
  • the url can contain path params, like a resource id.
  • the url can contain query params, like a pagination
  • the request can contain data (or body)
  • the request contain a response, with always the same data types

Some of these data are dynamics, and others never change. url and method never change, while path params, query params, data and response depends on the context.

Let's define our schema:

const schema = {
  'PATCH users/:id': {
    url: (pathParams: { id: string }) => `users/${pathParams.id}`,
    method: 'PATCH',
    queryParams: null,
    data: {} as {
      username?: string,
      email?: string
    },
    response: {} as {
      id: string,
      username: string,
      email: string
    }
  },
}

Wait, what is that object ? Is a plain JavaScript object or a TypeScript definition ?

It's both !

Technicaly, it's a plain JavaScript object, but it's also used as TypeScript definition for some keys with the powerful of as TypeScript keyword.

Now, let's build a request() function, base on native fetch browser:

import queryString from 'query-string'

function request(config) {
  const {
    url,
    method,
    data,
    queryParams,
    ...restConfig
  } = config

  const baseURL = 'https://api.com'
  const queryParamsStr = queryString.stringify(queryParams)
  let fullURL = `${baseURL}/${url}`
  if (Object.keys(queryParamsStr).length) {
    fullURL += `?${queryParamsStr}`
  }

  return fetch(fullURL, {
    method,
    body: JSON.stringify(data),
    ...restConfig
  }).then(res => res.json())
}

It's a very basic function with some data handling, like stringify query params and data, concat baseURL and return a promise with plain JavaScript object.

Currently, TypeScript doesn't know anything about the request schema. It could be usefull if TS can autocomplete config data depends on the request ?

typescript-object-schema provide 2 utils types to build a powerfull config schema:

import { GetConfig, GetOutput } from 'typescript-object-schema'
type Schema = typeof shema
type RouteName = keyof Schema
type FetchParams = NonNullable<Parameters<typeof fetch>[1]>

function request<T extends RouteName>(config: GetConfig<Schema, T, FetchParams>): Promise<GetOutput<T, Schema>> {
  const {
    name,
    pathParams,
    data,
    queryParams = {},
    ...restConfig
  } = config

  const {
    url,
    method,
    queryParams: defaultQueryParams
  } = apiSchema[name]

  const finalQueryParams = {
    ...defaultQueryParams,
    ...queryParams,
  }

  const urlWithPathParams = typeof url === 'function' && pathParams
    ? url(pathParams)
    : url

  const baseURL = 'https://api.com'
  const queryParamsStr = queryString.stringify(finalQueryParams)
  let fullURL = `${baseURL}/${urlWithPathParams}`
  if (Object.keys(queryParamsStr).length) {
    fullURL += `?${queryParamsStr}`
  }

  return fetch(fullURL, {
    method,
    body: JSON.stringify(data),
    ...restConfig
  }).then(res => res.json())
}

Now TypeScript can infer and automcomplete the config and response.

Usages:

const updatedUser = await request({
  name: 'PATCH users/:id',
  data: {
    email: '...',
  }
})

IntelliSense examples

  • name
    Name

  • data
    Data

  • queryParams
    queryParams

  • response
    Response

  • othersProperties
    Response

Things to know

Schema keys names

Each keys of the schema object can be named like you want. In examples, names are GET users, GET users/:id, but you can named it GET_users, users get, retrieve users, update users/id, etc. Keys are use by TypeScript to find the correct route schema, so it's completely arbitrary. TypeScript will autocomplete keys for you, so even with a complicated format like GET users/:id, you don't have to remember it.