Skip to content

Instantly share code, notes, and snippets.

@pmn4
Last active September 20, 2023 19:28
Show Gist options
  • Save pmn4/1d76986184d7b11f90ca7a88747f5340 to your computer and use it in GitHub Desktop.
Save pmn4/1d76986184d7b11f90ca7a88747f5340 to your computer and use it in GitHub Desktop.
a TypeScript type for parsing URL path parameters
/*
I find that the edges of serialization are the greatest weaknesses for
TypeScript projects. Further, with API routes, I like to be as declarative
as possible, so the code (the types) can be documentation for both client
and server.
With this type, URL path parameters can be extracted from path string:
*/
// URL param values come in as a string or an array of strings
type AsQuery<T extends string> = { [K in T]: string | string[] };
// split out Path into a prefix, param, and suffix, where param is wrapped in
// square brackets, then convert that param into a Query object and continue
// parsing recursively, returning an empty object when there are no params to
// parse
export type RouteParamsObjectFromPath<Path extends string> =
Path extends `${infer Prefix}[${infer Param}]${infer Suffix}`
? RouteParamsObjectFromPath<Prefix> & // recurse on prefix
AsQuery<Param> & // convert param into RouteParams
RouteParamsObjectFromPath<Suffix> // recurse on suffix
: {};
@pmn4
Copy link
Author

pmn4 commented Sep 20, 2023

now we can define a method that take the path, and a handler, and ensures that the query the handler receives has the parameters referred to by the path string:

import type { IncomingMessage } from 'http';

export interface ApiRequest<T extends string> extends IncomingMessage {
  query: RouteParamsObjectFromPath<T>;
}

function handleRoute<T extends string>(path: T, handler: (req: ApiRequest<T>) => void) {
  // ... some common route handling code
}

in practice, you will find that abc below, will result in a TypeScript error:

handleRoute('/api/users/[userId]', (req) => {
  const { userId, abc } = req.query;

  // ... behavior unique to this route
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment