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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@phila/philaroute

v1.0.14

Published

Basic HTTP router for AWS API Gateway / Proxy Lambdas with composable pipelines built with TypeScript.

Downloads

9

Readme

philaroute

TypeScript Hosted on AWS License

Build Status Test Status

A lightweight, composable HTTP router for Node.js Lambda/API Gateway environments, written in TypeScript. Supports composable middleware pipelines and CORS out of the box.


Features

  • TypeScript-first API with full type definitions
  • Simple route and method registration
  • Functional middleware pipeline composition
  • Built-in input validation (environment, query params, request body)
  • Built-in CORS support for Lambda/API Gateway
  • Automatic error handling with proper HTTP status codes

Installation

npm install philaroute

Quick Start

import { Router, http } from 'philaroute';

const router = Router({ cors: { 'Access-Control-Allow-Origin': '*' } });
const hello = router.path('/hello');

hello.get([
  async (acc) => {
    acc.data.message = 'Hello World!';
    return acc;
  }
]);

// Lambda handler
export const main = async (event) => {
  return router.routeToPath(event);
};

Core Concepts

The Accumulator Pattern

All request data flows through a RestAccumulator object that gets passed through your pipeline functions:

type RestAccumulator<TData = Record<string, any>> = {
  request?: {
    params?: { [key: string]: string },  // Query string parameters
    body?: any,                           // Parsed JSON request body
    headers?: { [key: string]: string },  // HTTP headers
    mvHeaders?: { [key: string]: string[] }, // Multi-value headers
  },
  data: TData,                            // Pipeline state (see note below)
  response: {
    statusCode?: number,
    headers?: { [key: string]: string },
    isBase64Encoded?: boolean,
    body?: string | Record<string, any> | any[]
  }
}

Important: If response.body is not explicitly set, the data object becomes the response body automatically. This allows pipelines to build up response data without explicitly setting the body.

Pipeline Functions

Each function in a pipeline receives the accumulator and returns a modified version:

type PipelineFn = (acc: RestAccumulator) => Promise<RestAccumulator>

Functions execute sequentially. Each receives the output of the previous function.

Type-Safe Pipelines

For full compile-time type safety, use typedPipeline which tracks the accumulator shape through each transformation:

import { typedPipeline, TransformFn } from 'philaroute';

type Input = { value: number };
type Output = { value: number; doubled: number };

const double: TransformFn<Input, Output> = async (acc) => ({
  ...acc,
  doubled: acc.value * 2
});

const pipeline = typedPipeline(double);
const result = await pipeline({ value: 5 });
// result.doubled is typed as number

The typedPipeline uses TypeScript's tuple inference to validate that each function's output type matches the next function's input type at compile time.


API Reference

Router

Create a router instance with optional CORS configuration:

import { Router } from 'philaroute';

const router = Router({
  cors: {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type',
  }
});

Registering Routes

const users = router.path('/users');

users.get([...pipeline]);    // GET /users
users.post([...pipeline]);   // POST /users
users.put([...pipeline]);    // PUT /users
users.patch([...pipeline]);  // PATCH /users
users.delete([...pipeline]); // DELETE /users

OPTIONS requests are handled automatically with a 204 No Content response.

Dispatching Requests

// In your Lambda handler
export const handler = async (event) => {
  return router.routeToPath(event);
};

Validation

Built-in validators throw AssertionError on failure, which the router converts to 400 Bad Request responses. All validators provide compile-time type inference for the validated fields.

Environment Variables

import { validate } from 'philaroute';

users.get([
  validate.environment(['DATABASE_URL', 'API_KEY']),
  async (acc) => {
    // TypeScript knows these are strings:
    const dbUrl: string = acc.data.valid.environment.DATABASE_URL;
    const apiKey: string = acc.data.valid.environment.API_KEY;
    return acc;
  }
]);

Query Parameters

users.get([
  validate.parameters(['page', 'limit']),
  async (acc) => {
    // TypeScript knows these are strings:
    const page: string = acc.data.valid.parameters.page;
    const limit: string = acc.data.valid.parameters.limit;
    return acc;
  }
]);

Request Body

Validate body fields with type checking. The schema maps to TypeScript types:

users.post([
  validate.body({
    name: 'string',
    age: 'number',
    active: 'boolean',
    metadata: 'object',
    tags: 'array'
  }),
  async (acc) => {
    // TypeScript infers the correct types:
    const name: string = acc.data.valid.body.name;
    const age: number = acc.data.valid.body.age;
    const active: boolean = acc.data.valid.body.active;
    return acc;
  }
]);

Response Handling

Explicit Response

Use http.response() to set response properties:

import { http } from 'philaroute';

users.post([
  validate.body({ name: 'string' }),
  async (acc) => {
    const user = await createUser(acc.data.valid.body);
    acc.data.user = user;
    return acc;
  },
  http.response({
    statusCode: 201,
    body: { success: true }
  })
]);

Implicit Response

If you don't set response.body, the data object is returned:

users.get([
  async (acc) => {
    acc.data.users = await fetchUsers();
    acc.data.total = 100;
    return acc;
  }
  // Response body will be: { users: [...], total: 100 }
]);

Error Handling

The router automatically handles errors:

| Error Type | HTTP Status | |-----------|-------------| | Path not found | 404 Not Found | | Method not allowed | 405 Method Not Allowed | | AssertionError (validation) | 400 Bad Request | | Any other error | 500 Internal Server Error |

CORS headers are included in all error responses.


Complete Example

import { Router, validate, http } from 'philaroute';

const router = Router({
  cors: { 'Access-Control-Allow-Origin': '*' }
});

const users = router.path('/users');

users.get([
  validate.parameters(['page']),
  async (acc) => {
    const page = parseInt(acc.data.valid.parameters.page, 10);
    acc.data.users = await db.users.findMany({ skip: page * 10, take: 10 });
    return acc;
  }
]);

users.post([
  validate.body({ name: 'string', email: 'string' }),
  async (acc) => {
    const user = await db.users.create(acc.data.valid.body);
    acc.data.user = user;
    return acc;
  },
  http.response({ statusCode: 201 })
]);

export const handler = (event) => router.routeToPath(event);

Module Structure

| Module | Purpose | |--------|---------| | index.ts | Router factory, route registration, request dispatch | | fn.ts | Pipeline composition (pipeline, typedPipeline, restPipeline) | | parse.ts | AWS event to RestAccumulator transformation | | http.ts | Response formatting and HTTP status codes | | validate.ts | Input validation middleware with type inference | | types.ts | Core TypeScript type definitions | | aws.ts | AWS API Gateway v1 payload interface |


Development

npm run build   # Build TypeScript
npm test        # Run tests
npm run lint    # Lint code
npm run format  # Format code

License

MIT © City of Philadelphia