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

pretend

v4.0.0

Published

A decorator based http webservice client written in typescript

Downloads

408

Readme

pretend

npm GitHub license build codecov renovate badge

A decorator based http webservice client build with typescript (inspired bei feign).

Features

  • Handle REST based webservices
  • Configure a decoder (defaults to JSON)
  • Generic request/response interceptor chain
  • Basic authentication
  • Request parameters (currently on GET requests)
  • Custom headers per method

Usage

Installation

Install as npm package:

npm install pretend --save

Note: To work on node.js (server-side) the fetch must be polyfilled. This could easy be done importing isomorphic-fetch.

API

class Test {

  @Headers('Accept: application/json')
  @Get('/path/{id}', true)
  public async get(id: string, parameters: any) {}

  @Post('/path')
  public async post(body: any) {}

  @Post('/path')
  public async post(@FormData('name') blob: any) {}

  @Put('/path')
  public async put() {}

  @Delete('/path/:id')
  public async delete(id: string) {}

}

async function call() {
  const client = Pretend
                  .builder()
                  .target(Test, 'http://host:port/');
  const result = await client.get('some-id', {'name': 'value'});
}

// Executes a GET request to 'http://host:port/path/some-id?name=value'
call();

Decoders, basicAuthentication and requestInterceptors are all special forms of the more generic interceptors which could be chained per request/response.

// Configure a text based decoder
const client = Pretend.builder()
  .decoder(Pretend.TextDecoder)
  .target(Test, 'http://host:port/');
// Configure basic authentication
const client = Pretend.builder()
  .basicAuthentication('user', 'pass')
  .target(Test, 'http://host:port/');
// Configure a request interceptor
const client = Pretend.builder()
  .requestInterceptor((request) => {
    request.options.headers['X-Custom-Header'] = 'value';
    return request;
  })
  .target(Test, 'http://host:port/');

Interceptors

Multiple interceptors could be added to each builder. The order of interceptor calls will result in a chain of calls like illistrated below:

// Configure a request interceptor
const client = Pretend.builder()
  .interceptor(async (chain, request) => {
    console.log('interceptor 1: request');
    const response = await chain(request);
    console.log('interceptor 1: response');
    return response;
  })
  .interceptor(async (chain, request) => {
    console.log('interceptor 2: request');
    const response = await chain(request);
    console.log('interceptor 2: response');
    return response;
  })
  .target(Test, 'http://host:port/');
             +---------------+    +---------------+
Request ---> |               | -> |               |
             | Interceptor 1 |    | Interceptor 2 | -> HTTP REST call
Response <-- |               | <- |               |
             +---------------+    +---------------+

This leads to the following console output:

interceptor 1: request
interceptor 2: request
interceptor 2: response
interceptor 1: response

Data Mappers

DataMappers could be used to map response structures to TypeScript classes. This is done using the @ResponseType decorator.

class User {
  public name: string;

  constuctor(data: { name: string }) {
    this.name = data.name;
  }
}

class API {
  @Get('/path/{id}')
  @ResponseType(User)
  public async loadUser(id: string): Promise<User> {
    /*
     * `/path/{id}` returns a JSON like this from the server:
     *
     *  {
     *    name: 'some string'
     *  }
     */
  }
}

const client = Pretend.builder().target(API, 'http://host:port/');
const result: User = await client.loadUser(1);

There is a second parameter to the @ResponseType decorator which is a transform function. The input is the server response, the output need to match the class constructor parameters.

Note: The constructor parameters are always an array!

class User {
  public get name(): string {
    return this.data.name;
  }

  constuctor(private data: { name: string }) {}
}

class API {
  @Get('/path/{id}')
  @ResponseType(User, (data) => [
    { name: `${data.firstname} ${data.lastname}` }
  ])
  public async loadUser(id: string): Promise<User> {
    /*
     * `/path/{id}` returns a JSON like this from the server:
     *
     *  {
     *    firstname: 'John',
     *    lastname: 'Doe'
     *  }
     */
  }
}

const client = Pretend.builder().target(API, 'http://host:port/');
const result: User = await client.loadUser(1);

Future ideas / Roadmap

  • Named parameters