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

api-router-abstraction

v1.0.1

Published

Typescript library for easy rest API prototyping

Downloads

14

Readme

Api Router Abstraction

coverage MIT License version

Typescript library to help abstract router details from controller implementations. Typesafe way to combine controllers with API URLs.

Imgur

Usage

Install

pip install api-router-abstraction

Define controllers

//set the arguments and (optional) return types for your controllers
controllerRegistry: {
    getPostById: {
        args: t.type({ //arguments are represented using io-ts types
            id: t.number,
        }),
    },
    getPostsByDate: {
        args: t.type({
            date: t.union([t.string, t.undefined]),
        }),
        returnType: t.type(...) //optional
    },
    createPost: {
        args: t.type({
            draft: t.boolean,
        }),
        body: "post", //this can be any key from the body registry
    },
},

//body registry can help define large groups of arguments that won't appear in the URL path
bodyRegistry: {
    post: {
        fields: t.type({
            author: t.string,
            content: t.string,
        }),
    },
}

Design your api interface structure

import { RouterGenerator } from "api-router-abstraction"

const generator = RouterGenerator.withConfig({
    controllerRegistry,
    bodyRegistry,
})

const { c, a, f } = generator.design()

Chain

Function c or chain will arrange validators in sequence. For example

c({ GET: c({ "/search": c({ "?q=string": f("search") }) }) })

will require that the request matches all 3 sequentially (ex. GET /search?q=cars).

Alt

Function a or alt will arrange validators in parallel. For example

c({
    POST: a(
        { "/posts": f("createPost") },
        { "/users": f("createUser") },
        { "/comments": f("createComment") }
    ),
})

will require that the request matches any of the arguments of a (ex. POST /users).

Controller

Function controller or f takes a key of controllerRegistry as argument. It used to type-check your interface design and format url paths (see example bellow).

Compile

In order for your code to compile your all controller nodes must be able to be supplied with all their arguments (as assigned in the controllerRegistry object).

const generator = RouterGenerator.withConfig({
    likePost: {
        args: t.type({ postId: t.number }),
    },
})

const router = generator.fromSchema(
    c("/posts": c("/:postId(number)": f("likePost"))) //this compiles
)

//these don't!
const router = generator.fromSchema(
    c("/posts": f("likePost"))
    //too few arguments
)

const router = generator.fromSchema(
    c("?isLogedIn=boolean":
        c("/:postId(number)": f("likePost"))
    ) //too many arguments
)

Use

The router object comes with format and parse methods out of the box.

const router = generator.fromSchema(schema) //see image example above

router.parse({ path: "post/3", method: "GET" })
// controller: "getPostsById", consumed: {id: 3}}

router.format("createPost", {
    draft: false,
    body: { author: "John Doe", content: "Lorem ipsum..." },
})
// {path: "/posts?draft=false", method: "POST", body: {...}}

Available Validators

| type | format examples | notes | | ---------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Method | "GET", "POST", "PUT", "DELETE" and even combinations like "GET, POST" etc. | These will match with the request method. If a method is not provided to the router.parse function, these validators will be ignored. | | Path Param | "/pathName" or "/:paramName(string)", "/:paramName(number)", "/:paramName(boolean)". | They will match with the path URL string. The router.parse function will return a consumed object containing param names and values. For "/:paramName(number)" and "/:paramName(boolean)" if the provided path cannot be parsed according to the specified type, the validation won't resolve. "/:paramName(string)" will match everything. | | Query | "?name=string", "?name1=number&name2=boolean", "name1=string&name2=number!", etc... | They will match to the query part of the request url. If a ! is provided after the query param type, the matching won't resolve without if the param is not present. | | body | "post_body", "user_body", etc... | They will use io-ts parsers of the corresponding bodyRegistry key in order to validate the request body payload. |

Controller Implementations

You can enforce controller implementation types by providing the router as an argument of the ControllerImplementations type.

import { ControllerImplementations } from "api-router-abstractions"

const controllerImpls: ControllerImplementations<typeof router> = {
    getPostById: function (
        args: { id: number },
        router? //the router object
        ...rest: any
    ): unknown {...},
    //if no returnType arg is provided in the controllerRegistry
    getPostsByDate: function (
        args: { date: string | undefined },
        router?
        ...rest: any
    ): *ReturnType* {...},
    //from the controllerRegistry
   ...
}

Limitations

  • Authentication is not handled in this library. User object should be provided in the rest parameters of controller functions.

  • Safety: this library should not be used in production without extensive security testing.

  • Unexpected behaviour: thorough testing on all possible url configurations, charsets and lengths has not been carried out.

  • Addapters for server libraries (like express.js) will have to be written manually.

Testing

To test run npm run test.

Contribute 💓💗

If you like the pronciples behind this project and would like to cooperate contact me or open a PR!

Check out more

io-ts Runtime type system for IO decoding/encoding

Pissflix Civilized smartTV content platform for 2023