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

toto-api-controller

v14.1.3

Published

The Toto API Controller is a façade to expressJS to make it easier building an API.

Readme

Toto API Controller

The Toto API Controller is a façade to expressJS to make it easier building an API.

The complete documentation of each version is here:


Once started, the API Controller will listen on port 8080. It will also publish the following endpoints:

  • GET / - Smoke test endpoint of the API
  • GET /health - Health endpoint of the API

Publishing on NPM

To publish this package on NPM, first build it and the publish it... it's that easy.

npm run build
npm publish

Version 14.0.0

  1. Event Handling: event handlers are now registered separately than a normal REST endpoint. Support for:

    • AWS SNS
    • GCP PubSub
  2. Toto Token: it is now possible to generate a JWT Token for a backend service, instead of relying on propagating tokens. This choice has been made to support scenarios where a service is called without a token (e.g. SNS HTTPS push endpoints). It is also more correct, for event handlers that need to call other Toto Services to use their own token (identity) rather than propagating something like a PubSub Service Account.

  3. Toto Controller Config is now an Abstract Class. Now you will need to extend TotoControllerConfig instead of using implement as it is no longer an interface but an abstract class. That was done to avoid boilerplate code to be rewritten all the time in every implementation of a Toto Microservice.

Version 13.2.0

  1. Option to start the server on other ports. It is now possible to change the default port of 8080 to anything else.

Version 13.0.0

  1. Simplified JWT Token Verification. Custom JWT tokens are now verified without the need of an API call. They are just verified by using the jwt.verify() method, that uses the secret (JWT Signing Key) to check the validity of the token. If the key stored in your Secrets Manager is the key that was used to sign the token than the auth succeeds.

That also means that:

  • getCustomAuthVerifier() in the Config is no longer needed (it has been removed from the interface).
  • getProps() should now return customAuthProvider: "toto" as part of the payload, if the service supports Toto Auth.
  • getSigningKey() is a new method that should return the JWT signing key used to sign Toto tokens (the key must be loaded from the secrets manager).
  1. Multi-hyperscalers A Toto Microservice can now run on GCP and AWS. That means that concretely:
    • Secrets are extracted from GCP Secrets Manager or AWS Secrets Manager based on the enviornment the Microservice runs in. The environment is made by:
      • A Hyperscaler (env var)
      • An Enviroenmtn (e.g. dev, test, prod)

Version 12.0.4

Added exports from the root of all exported modules. This allows imports to look like this:

import { TotoRuntimeError } from "toto-api-controller";

instead of the old

import { TotoRuntimeError } from "toto-api-controller/dist/model/TotoRuntimeError";

Minor release 11.6.0

Added support to path-level options to streamGET path types.

Minor release 11.5.0

Now allows to configure authorization on a path-level: it is now possible to specify if a specific route (path) can be open (auth free).

To do so, you can now pass in the api.path() registration method a TotoPathOptions object with noAuth: true if that specific route is not subject to Authentication.

Major release 11.0.0

Non-backward compatible release moving to Typescript.

Major release 10.0.0

Now moved to Typescript. Also contains a simplified Auth check. For more info, see the documentation on Version 10.0.0

Major release 10.0.0

Now requires the getAuthorizedClientIDs() method to return, for the "google" key, an object mapping different applications to their client ID. For more info, see the documentation on Version 10.0.0

Minor release: 9.5.0

Now supports providing a different Content-Type on streamGET paths.

To do that, just pass an options object in the following way:

api.streamGET('/path/to/pdf/stream', getMyPDF, { contentType: "application/pdf" });

Minor release: 9.4.0

Now supports validating the x-app-version header.

To do that, in your Config class, in the getProps() method, you can pass a minAppVersion field (string, in the format major.minor.patch), like this:

    getProps() {
        return {
            noCorrelationId: true, 
            minAppVersion: "0.16.0" <- you can do this or use something you have loaded from a configuration or secret
        }
    }

If the minAppVersion field is passed, the controller will automatically check the provided x-app-version header and if lower than the min app version, a validation error will be provided, that looks like this: {code: 'app-version-not-compatible', message: "The App Version is not compatible with this API"}

Note: if the x-app-version header is not provided, the controller will not block the request. Clients that do not provide that header are expected to be able to run on the latest version of the backend APIs they are calling.

Minor release: 9.3.0

Now supports x-app-version. This gives clients (apps) the opportunity to provide the app version so that the backend can block old apps from using specific API versions.

The app version is now provided as a field in the executionContext when the API delegate is called. The cid is also provided:

executionContext.cid = req.headers['x-correlation-id']; 
executionContext.appVersion = req.headers['x-app-version'];

Major release: 9.0.0

Now supporting Apple login!

Other breaking changes: now the Config class needs to provide a getAuthorizedClientIDs() method, that will return an object {"google": "clientID", "apple", "clientID", etc..}

class Config {

    load() {
        ...
    }

    getAuthorizedClientIDs() {
        return {
            "google": this.googleClientID, 
            "apple": this.appleClientID, 
            "fb": this.fbClientID
        }
    }

}

This replaces the previous methods getAuthorizedClientId(), getAuthorizedFBClientId(), etc..

Major release: 8.0.0

Now supporting custom Auth Providers!! How does that work? Now you config can provide a getCustomAuthVerifier function. That function will need to return an object with a function called verifyIdToken that given an idToken will be able to verify it's validity.

Let's look at an example of your new config file, when using a custom auth provider:

class Config {

    load() {
        ...
    }

    getCustomAuthVerifier() {
        return {
            verifyIdToken: function({idToken}) {

                return new Promise((success, failure) => {

                    // Here verify the the validity of the received idToken
                    // .... 

                    // Now return the results
                    success({
                        userId: ...,
                        email: ...,
                        authProvider: ...
                    })

                    // Or fail
                    failure(error);
                })
            }
        }
    }
}

Minor release: 7.6.0

Now the controller passes an executionContext object to the delegate. That object containes an instance of Logger which can be used to log messages with contextual information, like the name of the microservice, etc..

ATTENTION: Logger is a class, not an object anymore!

Minor release: 7.1.0

It is now possible to configure properties such as:

  • noAuth - set to true to bypass the authorization checks
  • noCorrelationId - set to true to accept HTTP calls without a correlation ID

The config object can now (optionally) provide a getProps() function that must return an object with the fields above... and probably more to come soon :)

Major update: 7.0.0

Now the auth check also supports Facebook on top of Google auth. To do that, the Config class also needs to provide:

  • getAuthorizedFBClientId() - a function that will return the FB app id that is authorized to call this microservice.

Another update is that now, to avoid redundant call to the auth providers to get user profile from a (access or id) token, the api controller will provide a userContext object that will contain the basic user profile.

That means that when delgates receive a call to their do() method, they will receive:

  • req - the HTTP request, as always
  • userContext - an object that contains:
    • userId - the auth provider specific user ID (e.g. google user id, or facebook user id)
    • email - the user email

So when you create a delegate your signature will look like this, if you want the user context:

exports.do = function (request, userContext) {
    ...
}

Major update: 6.0.0

In this version the following major changes have been made:

  • Config: now a Config object has to be passed to the Controller. This config must provide two methods:
    • load() - an asynchronous function to load the configuration of the microservice, that will return a Promise
    • getAuthorizedClientId() - a function that will return the authorized client id, used to verify that the client app calling the microservice is authorized to do so. Note that this used to be provided as an Environment Variable, but is now expected out of the config object. This was chosen for a better security.
  • Streaming: you can now stream files as a response!

Authorization check

This Controller performs a few mandatory checks on the requests.

One of those checks is to verify that the provided Authorization header is passed and only the authorized CLIENT is able to access this API.

IMPORTANT: This authorized client ID has to be provided by the getAuthorizedClientId() method of the config object passed in the constructor of this controller.

How to use it

  1. Include it:
var Controller = require('toto-api-controller');
  1. Instantiate it
var api = new Controller('api-name', config eventProducer, eventConsumer);

The constructor takes the following arguments:

  • apiName mandatory, the name of the microservice (e.g. training-session)
  • config mandatory, the configuration object that will provide the two methods specified above.
  • eventProducer optional, the Toto Event Producer (see https://github.com/nicolasances/node-toto-event-publisher) if this API publishes events
  • eventConsumer optional, the Toto Event Consumer (see https://github.com/nicolasances/node-toto-event-consumer) if this API consumes events
  1. Start it
api.listen()

Example

An example of usage:

let Controller = require('toto-api-controller');
let config = require('./Config');

let api = new Controller('training-session', config.config, totoEventPublisher);

// APIs
api.path('GET', '/sessions', getSessions);
api.path('POST', '/sessions', postSession);

api.path('GET', '/sessions/:id', getSession);
api.path('DELETE', '/sessions/:id', deleteSession);

api.path('GET', '/sessions/:id/exercises', getSessionExercises);
api.path('POST', '/sessions/:id/exercises', postSessionExercise);

api.path('GET', '/sessions/:id/exercises/:eid', getSessionExercise);
api.path('PUT', '/sessions/:id/exercises/:eid', putSessionExercise);
api.path('DELETE', '/sessions/:id/exercises/:eid', deleteSessionExercise);

api.listen();

Example of Config

// Imports the Secret Manager library
const { SecretManagerServiceClient } = require('@google-cloud/secret-manager');

// Instantiates a client
class Config {

    load() {

        return new Promise((success, failure) => {

            // Load your configurations here .... 
            // ....

            // Among those load the authorizedClientId
            this.authorizedClientId = ...

            success();

        })
    }

    getAuthorizedClientId() {
        return this.authorizedClientId;
    }
}

exports.config = new Config();

Streaming files as a response

To provide an API that streams a file back to the calling client a method is provided in the controller to register that path: streamGET(path, delegate)

This method requires two arguments:

  • path - the path to register (like the path() method of this controller)
  • delegate - the delegate that is going to process the request. The delegate must provide a do() method (like other delegates used in the path() registration) and this method must return a Promise, which must return a ReadableStream (see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).

Registering static content

To provide access to static content (folders) in your service, use the staticContent() method:

api.staticContent(path, folder)

For example:

api.staticContent('/img', '/app/img');

Note that the folder is an ABSOLUTE folder