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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@toplo/api

v1.2.0

Published

Various NodeJS API utils with TS support

Readme

Install

npm install @toplo/api

Requirements

  • NodeJS version >= v15.4.0

Table of Contents

HotConfiguration

⬆️Top

If you want to easily configure the @toplo/api package

  1. npm i config
  2. create a config folder in the root of your project
  3. create a default.js file in it
  4. check this Example Config

HotRequests

⬆️Top

Supports all http methods


import { HotRequests } from "@toplo/api";

    const res = await HotRequests.get({
        url: "https://wisdoms-app.herokuapp.com/api/{myPath}",
        options: {
            timeout: 400,
            pathParams: { myPath: "getWisdom" },
            queryParams: { lang: "en" }
        }
    });

  // A successful response results in the following
  res: {
    success: true,
    status: 200,
    result: {
      sucess: true,
      wisdom: 'But he who gives them to him, the bad guys get along easily, one does not live on bread alone.',
      lang: 'en'
    },
    elapsed: 374
  }
  // Or if it times out
  res: {
    success: false,
    error: 'The operation was aborted.',
    status: 500,
    elapsed: 422
  }

Response is one of the two:

type HttpSuccessResponse<T> = {
    success: true;
    status: number;
    elapsed: number;
    result: T;
};

type HttpErrorResponse<T> = {
    success: false;
    error: string;
    elapsed: number;
    status: number;
    result?: T;
};

HotWatch

⬆️Top

For timing


import { HotWatch } from "@toplo/api";

const myWatch = new HotWatch();

(() => Promise)(); // Some action

myWatch.getElapsedMs(); // elapsed miliseconds since construction
myWatch.getElapsedS(); // elapsed seconds since construction

HotUrl

⬆️Top

Build your URL from an already existing URL or a string with optional query params


import { HotUrl } from "@toplo/api";

// Results in "http://localhost:4444/base/path/somedynamicpath/?someQ=1"
HotUrl.build({
    base: "http://localhost:4444/{someBasePath}",
    path: "/path/{dynamic}/",
    pathParams: { someBasePath: "base", dynamic: "somedynamicpath" },
    queryParams: { someQ: "1"}
});

// Results in "Some text."
HotUrl.replacePathParams("Some {replacement}.", { replacement: "text" });

// All three result in http://localhost:4444/?query1=val1&query2=true
HotUrl.buildQuery("http://localhost:4444/", { query1: "val1", query2: true  });
HotUrl.buildQuery("http://localhost:4444", { query1: "val1", query2: true  });
HotUrl.buildQuery(new URL("http://localhost:4444/"), { query1: "val1", query2: true  });

// Nullables are ignored
// Results in http://localhost:4444/?query1=val1
HotUrl.buildQuery(new URL("http://localhost:4444/"), { query1: "val1", query2: undefined  });

// Domain can end with / or not
// Path can start with / or not
// Both result in "http://localhost:4444/some/path/params/"
HotUrl.buildFromString("http://localhost:4444/", "/some/path/params/");
HotUrl.buildFromString("http://localhost:4444", "some/path/params/");

HotLogger

⬆️Top

Log all you need


Example config:

    log: {
        level: "DEBUG",
        filters: [{
            key: "eventName",
            values: [
                "/_filterRoute"
            ]
        }],
        serializers: [{
            key: "eventName",
            values: ["/serializeThisEvent"],
            modifiers: [{ properties: ["data.smh"] }]
        }]
    }
import { HotLogger } from "@toplo/api";

const myLogger = HotLogger.createLogger("MyLoggerContext");

myLogger.trace("Some trace msg");
// Will not be logged due to the log.level = "DEBUG" from the above configuration

myLogger.debug("Some trace filter route", { someKey: "/_filterRoute" });
// Will not be logged due to the log.filters[0].key = "someKey" with value "/_filterRoute"

myLogger.debug("Some info msg", { data: { smh: "ye"}, someKey: "/serializeThisEvent" });
// [{"Message":"Some info msg","LogLevel":"Debug","SourceContext":"MyLoggerContext","Properties":{"ProcessID":22172,"AppVersion":"1.0.3","AppName":"@toplo/api","AppId":"development-@toplo/api","Env":"development","data":{"smh":"********"},"someKey":"/serializeThisEvent"},"LogTimestamp":"2021-12-10T06:46:17.478Z"}]

myLogger.error("Some err msg", { err: new Error("Test err"), data: { smh: "ye"}, someKey: "/serializeThisEvent" });
//[{"Message":"Some err msg","LogLevel":"Error","SourceContext":"MyLoggerContext","ExceptionMessage":"Test err","ExceptionStacktrace":"Error: Test err\n    at Object.<anonymous> (G:\\hehe\\@toplo/api\\@toplo/api\\index.js:19:39)\n    at Module._compile (internal/modules/cjs/loader.js:1068:30)\n    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1097:10)\n    at Module.load (internal/modules/cjs/loader.js:933:32)\n    at Function.Module._load (internal/modules/cjs/loader.js:774:14)\n    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)\n    at internal/main/run_main_module.js:17:47","Properties":{"ProcessID":4444,"AppVersion":"1.0.3","AppName":"@toplo/api","AppId":"development-@toplo/api","Env":"development","data":{"smh":"********"},"someKey":"/serializeThisEvent"},"LogTimestamp":"2021-12-10T06:51:21.375Z"}]

HotObj

⬆️Top

Object utils


import { HotObj } from "@toplo/api";

const a = { 1: "one", "one": 1 };
const b = { 1: "one", "one": 1 };

HotObj.shallowEquals(a, b); // true

const someObj = {
    prop: "val",
    propTwo: "val2",
    propThree: "val3"
};
const result = HotObj.extract(someObj, ["propTwo", "propThree"]);; // { propTwo: "val2", propThree: "val3" }

// Type def for extract is inferred
(method) HotObj.extract<{
    prop: string;
    propTwo: string;
    propThree: string;
}, "propTwo" | "propThree">(obj: {
    prop: string;
    propTwo: string;
    propThree: string;
}, keys: ("propTwo" | "propThree")[]): {
    propTwo: string;
    propThree: string;
}

HotObj.hasProp(a, 1); // true
HotObj.hasProp(a, "one"); // true
HotObj.getValue(a, "one"); // 1
HotObj.getValue(a, "e"); // Argument of type '"e"' is not assignable to parameter of type '"one" | 1'.
// Type definition is inferred:
HotObj.getValue<{
    1: string;
    one: number;
}, "one" | 1>(obj: {
    1: string;
    one: number;
}, key: "one" | 1): string | number | undefined


const nullableObj = {
    ble: "undefined",
    notaNum: Number("undefined"),
    thisIsFine: 1
};

HotObj.cleanUpNullables(nullableObj); // { thisIsFine: 1 }

HotPromise

⬆️Top

Promise utils


import { HotPromise } from "@toplo/api";

// Results in
// {
//  isGood: false,
//  error: Timed out in 1000 ms.
// }
//
const promiseTimeout = async () => {
    try {
        await HotPromise.promiseTimeout(HotPromise.delay(1010), 1000);
        return { isGood: true, result: "good" };
    } catch (error) {
        return { isGood: false, result: error };
    }
};


// Results in
// {
//  isGood: false,
//  error: Error: Fail, total tries: 5
// }
//
const runTillSuccess = async () => {
    try {
        // Default retry times are 5, you can pass in "forever" to retry indefinitely
        // retries will s⬆️Top as soon as isGood: true has been returned by the retried action
        const ehee = await HotPromise.runTillSuccess(promiseTimeout);
        return { isGood: true, result: { code: "OK", ehee } };
    } catch (error) {
        return { isGood: false, error };
    }
};

HotServer

⬆️Top

Simple http server

Please check HotServer.e2e.test.ts for a more broad example usage.

import { HotServer } from "@toplo/api";

new HotServer({
    host: "localhost", // if not passed, taken from config
    port: 5433, // if not passed, taken from config
    staticRoute: {
        html: "./tests/mocks/test.html",
        route: "/staticHtml"
    },
    routes: {
        "/json": {
            onSuccess: () => {
                return {
                    result: {
                        some: "record"
                    }
                };
            }
        },
        "/text": {
            responseHeaders: {
                "Content-Type": "text/plain"
            },
            onSuccess: () => {
                return {
                    result: "My text response."
                };
            }
        }
    }
});

release/1.4.1