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

vaports

v0.0.3

Published

A micro framework for typescript express apps.

Readme

VaporTs

VaporTs is a minimal framework built to support a simple and only slightly opinionated object oriented approach to implementing and unit testing Expressjs applications. VaporTs is primarily focused on the use of two primary decorators. The Controller decorator, which provides a simple way to describe express controllers, their paths, and middleware. The Route decorator, which provides simple HTTP method declaration, extension to the Controller path, individual middleware, and optional error, response, and validation handling.


Installation:

Existing project:

npm i --save vaports

Using the CLI:

> npm i -g @vaports/cli
> mkdir PROJECT_NAME
> cd PROJECT_NAME && vaports

Example application

  • https://github.com/mwinb/VaporTs/tree/main/example

CLI

  • https://github.com/mwinb/VaporTs-CLI

Decorators

Controller

Parameters:

  • Path: String (required)
  • Middleware: Middleware[] (optional)

Examples:

Without middleware:

@Controller('/example')
class ExampleController {}

With Middleware:

@Controller('/example', [controllerMiddleware])
class ExampleController {}

Route

  • @Route Does not alter the original class method declaration.

Parameters:

  • Method: string (required)
    • 'POST' | 'PATCH' | 'PUT' | 'GET' | 'DELETE' | 'HEAD' | 'CONNECT' | 'OPTIONS' | 'TRACE'
  • RouteParams: { } (optional)
    • path: string | string[] (optional)
    • middleware: Middleware[] (optional)
    • handleErrors: boolean (optional, default: true)
    • handleResponse: boolean (optional, default: true)
    • responseCode: number (optional, default: 200)

Examples:

@Controller('/example')
class ExampleController {
  @Route('GET')
  async exampleGetFunction(): Promise<ExampleData[]> {}

  @Route('POST', { responseCode: 201 })
  async postWithCustomResponseCode({ body: postData }: express.Request): Promise<void> {}

  @Route('GET', { path: '/:id', middleware: [routeMiddleware] })
  async exampleGetOneFunction({ params: { id } }: express.Request): Promise<ExampleData> {}

  @Route('GET', { path: ['/pathone', '/pathtwo'] })
  async exampleGetWithTwoPaths(req): Promise<ExampleData[]> {}

  @Route('PATCH', { handleErrors: false, handleResponse: false })
  async examplePatchNoHandlers(req, res, next): Promise<void> {}
}

Route Handlers

handleErrors

Error handling is applied to all Routes and Validators by default, but may be disabled in the RouteParams. This handler wraps asynchronous Route or Validate decorated functions in a try catch that listens for an HttpError to be thrown. The HttpError takes in the code and the response message. When caught by the handler, the express response status is set to the HttpError code and a json object with the code and message are sent. All non HttpErrors are caught and handled with a 500 and a non-specific error message. Non HttpErrors are also logged with the VaporApp logger;

  • Using @Route maintains the class method functionality and only alters the bound method prior to being applied to the express route see the example unit and integration tests for functionality.

Example - response (caught):

res.status(404).send({ code: 404, message: 'Not Found' });

Example - response (uncaught):

res.status(500).send({ code: 500, message: 'Oops something went wrong.' });

handleResponse

Response handling is applied to all Routes by default, but may be disabled in the RouteParams. This handler wraps an @Route decorated method. When the decorated method returns data it is handled with res.status and res.send. For void methods res.sendStatus is used instead. The default status code is 200, but can be changed in the RouteParams.

  • Using @Route maintains the class functionality and only alters the bound method prior to being applied to the express route see the examples unit and integration tests for functionality.

Example:

@Controller('/example')
class ExampleController {
  // Calls res.status(200).send(Example[]) if successful
  @Route('GET')
  async getExamples(): Promise<Example[]> {
    return await this.exampleService.getAllExamples();
  }
  // Calls res.sendStatus(201) if successful
  @Route('PATCH', { responseCode: 201 })
  @Validate(new ExamplePatchValidator(), 'body')
  async patchExampleNoReturn(req: express.Request): Promise<void> {
    await this.exampleService.patch(req.body);
  }
}

Validate

In order to use @Validate an instance of a class that uses one of the Validation Decorators is needed. @Validate Handles all validation failures with the HttpError handler. See the VaporValidator Class description below.

  • @Validate does not alter the original class method.
  • @Validate only works with @Route (order in relation to @Route does not matter)
  • When using more than one @Validate they are executed bottom to top.

Parameters:

  • validator: VaporValidator (required)
    • The VaporValidator meta data is applied to any class that uses a validator decorator.
    • Throws an HttpError with a 400 status and information about the value and field that failed.
  • requestFieldToValidate: string (required)
    • The Express Request(req) field upon which validation will be run:
      • 'body' | 'params' | 'query' ...
      • Throws an HttpError with a 501 status and logs information about the function if invalid field is provided.
  • validateConfig: ValidateConfig (optional):
    • isArray (optional, default: false)
      • Adds Array Validation to the Express.Request field.
    • evaluateEachItem (optional, default: true)
      • Validates each item in array when field isArray.
      • Disabling removes type evaluation and only ensures that the field is an Array.
    • strip: (optional, default: true)
      • Removes any fields not specified in the provided Validator.

Examples:

class ExampleValidator {
  @String()
  someString: string;
}

@Controller('/example')
class ExampleController {
  @Route('PATCH')
  @Validate(new ExampleValidator(), 'body')
  examplePatch(req, res) {}

  @Validate(exampleBodyValidator, 'body') // Executes second.
  @Validate(exampleHeaderValidator, 'headers', { strip: false }) // Executes first.
  @Route('POST', { responseCode: 201 }) // Ordering does not matter.
  async addSat({ body: sat }: express.Request): Promise<ExampleModel> {}
}

Validators

Field decorators can be used to create a VaporValidator needed in the Validate Decorator. There are two types of Validators:

Primitive

@String

  • Validates that field is a string.

@Boolean

  • Validates that field is a boolean.

@Number

  • Validates that field is a number.

@JsonObject

  • Validates that field is of type object ({})

Params

  • validationConfig: ValidatorFieldConfig {} (optional)
    • isArray: boolean (optional, default: false)
      • Adds Array Validation to the field.
    • evaluateEachItem: boolean (optional, default: true)
      • Validates each item in array when field isArray.
      • Disabling removes type evaluation and only ensures that the field is an Array.
    • optional: boolean (optional, default: false)
      • Ignores if undefined.
      • Still validates the field if it exists.
    • evaluators: Evaluator[] (optional)
      • Additional custom validation functions to be run against the field.
      • Does not replace the default evaluator.
    • type Evaluator = (valueToValidate: any) => boolean

Example

class ExampleVaporValidator {
  @String({ evaluators: [isObjectId] })
  id: ObjectId;

  @String()
  stringField: string;

  @Number()
  numberField: number;

  @Boolean()
  booleanField: boolean;

  @JsonObject({ optional: true })
  jsonObject: {};

  @String({ isArray: true })
  stringArray: string[];
}

Specialized

@Validator (for nested objects)

  • Params

    • validator: VaporValidator (required)
    • validationConfig: ValidatorConfig (required)
      • isArray: boolean (optional, default: false)
        • Adds Array Validation to the field.
      • evaluateEachItem: boolean (optional, default: true)
        • Validates each item in array when field isArray.
        • Currently disabling removes type evaluation and only ensures that the field is an Array.
      • optional: boolean (optional, default: false)
        • Will not throw HttpError if undefined.
        • Still validates the field if it exists.
      • evaluators: Evaluator[] (optional)
        • Additional custom validation functions to be run against the field.
        • Does not override the default Evaluator.
      • strip: boolean (optional, default: true)
        • Removes any fields not specified in the provided Validator.
  • Defaults to JsonObject validation if VaporValidator is not passed in the validator param.

@SetPropertyEvaluators

  • Suggested as return value for custom validators, but it may be used directly.

  • validationConfig: ValidatorFieldConfig (required)

Example

class SpecializedValidators {
  @SetPropertyEvaluators({
    isArray: false,
    evaluateEachItem: false,
    optional: true,
    evaluators: [isObjectId]
  })
  id: ObjectId;

  @Validator(new ExampleVaporValidator(), { isArray: true })
  examples: ExampleVaporValidator[];
}

Example Custom Validator

const ValidateObjectId = (validationConfig: ValidatorFieldConfig = {}): PropertyDecorator => {
  const config = { ...DEFAULT_VALIDATOR_CONFIG, ...validationConfig };
  const evaluators = [isObjectId, ...config.evaluators];
  return SetPropertyEvaluators({ ...config, evaluators });
};

class CustomValidatorExample {
  @ValidateObjectId()
  Id: ObjectId;
}

Classes

VaporApp

The VaporApp class is responsible for binding all routes, methods, and middleware to an express application.

Parameters:

  • VaporConfig: {}
    • path: string (optional, defaults to root)
    • showApi: boolean (optional, default: false)
      • This provides a simple html page @GET/path that lists all routes as links and their respective methods.
    • middleware: Middleware[] (optional)
      • Sets the path middleware.
    • controllers: Controller[] (required)
      • any class with the Controller Decorator.
      • Throws error with information about the class if a undecorated class is passed
    • expressApplication: Express.Application (required)

Example:

// server.ts
const port = 5000;
const expressApp = express();
const appV1 = new VaporApp({
  path: '/v1',
  showApi: true,
  middleware: [pathMiddleware, express.json()],
  controllers: [new SatelliteController()],
  expressApplication: expressApp
});

expressApp.listen(port, () => {
  console.log(`App listening on the port ${port}`);
  if (appV1.showApi) console.log(`View v1: http://localhost:${port}${appV1.path}`);
});

HttpError

Extends the Error class and is caught with the HttpError handler decorator or by default in Route decorated async functions. HttpError handler can be disabled by setting the handleErrors parameter in RouteParams to false.

Parameters:

  • code: number (required)
  • message: string (required)

Example:

throw new HttpError(404, 'Not Found');

Created By Michael Winberry