Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

super-route-express

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

super-route-express

Non pretentious express framework

  • 1.0.16
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

SuperRoute

Non-pretentious, Mildly-opinionated Express Framework

Build Status Coverage Status Install Size Dependency Count

SuperRoute is a framework for building readable api routes, aimed at improving code re-usability, code readability and route documentation.

Main Features:

Installation


npm install --save super-route-express

import:

import {SuperRoute} from 'super-route-express';

Usage


Create a new route class that extends SuperRoute.

You can create classes for specific route types or groups - e.g. routes that require authentication, access control, or share data with the middleware.

import {SuperRoute} from 'super-route-express';

class BasicRoute extends SuperRoute {}

// all created routes will require authentication
class AuthenticatedRoute extends BasicRoute {
  authenticate: true;
}
// all created routes will use access control function to block non admin users
class AdminOnlyRoute extends AuthenticatedRoute {
  permissions: {
    equalOrGreaterThan: 'admin'
  }
}
Instantiate Routes

A SuperRoute instance (and it's children's instances) is passed a RouteSettings configuration object. Each route has a stack of request handlers that or bound the route as their this argument when calling mount(), it allows access to error handling methods as well as additional instance and class data that might be useful.

See the Api section blow for full options.

const routes: Array<SuperRoute> = [
  new AdminOnlyRoute({
    path: 'users/new',
    verb: 'post',
    name: 'new user',
    description: 'creates a new user',
    bodyParams: [
      new BodyParameter('firstName', 'string', 'user first name', true),
      new BodyParameter('lastName', 'string', 'user last name', true),
      new BodyParameter(
        'mobilePhone',
        'string',
        'user mobile phone',
        true,
        [{
            test: (value: string) => {
              return value.length === 10
            },
            description: 'checks if mobile phone has 10 digits'
        }]
      ),
    ],
    middleware: [
      (req: Request, res: Response, next: NextFunction) => {
        // ... Some middleware logic
      }
    ]
  }),
  new AuthenticatedRoute({
    path: 'users/read/:id',
    verb: 'post',
    name: 'new user',
    description: 'creates a new user',
    authenticate: true,
    routeParams: [
      new RouteParameter('id', 'mongodb id', true, [{
            test: (value: string) => ObjectId.isValid(value),
            description: 'id must be a valid mongodb ObjectId'
        }]
      ),
    ],
    middleware: [
      // stack req processing middleware here for re-usability
      SomeGenericMiddleWare,
      SomeMoreGenericMiddleware({configuration: 'some configuration'}),
      (req: Request, res: Response, next: NextFunction) => {
        // ... Some middleware logic
      }
    ]
  }),
]
Mount routes
const apiRouter = Router();
routes.forEach(route => route.mount(router));

Access Control


SuperRoute offers two levels of access control that can be defined in the extending class.

1. Access Control function

Defined by the $$accessControlFunction property, it is used to limit access to users with defined permissions, using the route's permissions settings.

Setting Route Permissions

Use a RoutePermissions object to configure access control for a route instance or extending class with the following options.

  • equalOrGreaterThan - requester must have a permission level that is equal or greater than the given string as defined by the hierarchy array.

  • specific - requester must have all the given permissions

  • merge - when set to 'and' requester must satisfy both the specific an hierarchical rules.

All properties are optional, but the configuration must contain either equalOrGreaterThan or specific.

Example:

{
  equalOrGreaterThan: 'admin',
  specific: ['specialPermission', 'awesomeDude'],
  merge: 'and'
}

will only grant access to admins that also have the specialPermission and awesomeDude permisions

Per-route instance configuration:

const route = new BasicRoute({
  path: 'some/path',
  verb: 'post',
  name: 'some name',
  permissions: {
    equalOrGreaterThan: 'admin',
    specific: ['specialPermission', 'awesomeDude'],
    merge: 'and'
  }
  // ... //
})

Or configure a child class:

class AdminOnlyRoute extends SuperRoute {
  permissions: { specific: ['admin'] }
}

Configure Access Control function

  • Assign an AccessControlFunction to the $$accessControlFunction in the class definition. When mounted, it will be called with the route's permissions and should return a RequestHandler.
  • Use the static method SuperRoute.checkPermissions to validate the user's permissions. note that checkPermissionsreceives a hierarchy Array in which the item with the largest index is the highest in the hierarchy.
  • Also note that if the route's permissions are not defined by the class or it's instances, $$accessControlFunction will not be called.
class AuthenticatedRoute extends SuperRoute {
  $$accessControlFunction: AccessControlFunction = (permissions: RoutePermissions) => {
    return async (req: Request, res: Response, next: NextFunction) => {
      // define heirarchy
      const hierarchy = [
        'end_user',
        'editor',
        'admin'
      ]
      let auth;
      try {
        // check user permissions against route permissions ans heirarchy 
        auth = SuperRoute.checkPermissions(req.user.permissions, permissions, hierarchy);
      } catch (err) {
        return next(err);
      }
      if (!auth) {
        // Do some more logic.....
        this.handleError(req, res, next, `user does not have sufficient permissions to access ${this.path}`, 403)
      } else {
        // Do some more logic.....
        return next();
      }
    }
  }
}
2. Authentication function

Defined by the $$authenticationFunction property, it is used to verify that the requester is logged in. it can be used to perform e.g. JWT verification or another login strategy, or as a form of input validation to verify that the request reaching the middle contains an authenticated user's data. When mounted, the function is called with the route's permissions definitions. Example:

class AuthenticatedRoute extends BasicRoute {
  authenticate: true;
  $$authenticationFunction = (req: Request, res: Response, next: NextFunction) => {
    if (req.hasOwnProperty(user)) {
      next();
    } else {
      this.handle(arguments, 'Not Authenticated', 400)
    }
  }
}

Input Validation


SuperRoute offers both route parameters and body validation. Route parameters and properties of the request's body can be defined in the route settings, serving both the purpose of input validation and route documentation generation.

Input that doesn't meet the defined spec will return a detailed error and 400 status code.

Body Validation
const route = new BasicRoute({
  // ...route settings... //
  bodyParams: [
    new BodyParameter('id', 'string', 'user id', true)
  ]
})

Pass an array of BodyParameter to the route's settings with the following arguments:

NameTypeDefault valueDescription
namestring-property name
typeParameterType'any'the expected type of the parameter. if defined, will throw an error if the parameter's type doesn't match
descriptionstring''text that will be displayed in the rendered help output
requiredbooleantrueif true, will throw an error when the property is missing
additionalTestsRequestParameterTestFunction[][]an array of additional test functions and their description

BodyParameter (as well as RouteParameter) can be defined with additional validations (in addition to the type checking)

new BodyParameter('mobilePhone', 'string', 'user mobile phone', true, [
  {
    test: (value: string) => {
      return value.length === 10
    },
    description: 'mobile phone must have 10 digits'
  }
])

All validation errors are combined and returned.

Route Parameters Validation
const route = new BasicRoute({
  // ...route settings... //
  bodyParams: [
    new RouteParameter('id', 'user id', true)
  ]
})

Pass an array of RouteParameter to the route's settings with the following arguments:

NameTypeDefault valueDescription
namestring-property name
descriptionstring''text that will be displayed in the rendered help output
requiredbooleantrueif true, will throw an error when the property is missing
additionalTestsRequestParameterTestFunction[][]an array of additional test functions and their description

Route Parameters can also have additional validations same as body parameters

Error Handling


Handling with this.handle()

Middleware functions are all bound to the route as their this argument, so it is possible to call this.handle() from any of the middleware functions, with following parameters:

NameTypeDefault valueDescription
middlewareArgsIArguments-req, res, next from the express middleware function
errorOrMessagestringErrorError object, or an error message string
statusCodenumber500html response status code
respondWith?string-optional custom error message to send as response
logbooleanfalselog the error to the console if true
redirectstringfalseredirect url passed to the error handler
options?object-options object to pass to a custom error handler

It will construct a RouteError object and pass it to next().

It's also possible to just pass a vanilla Error object to next, but handling with RouteError

Generate Route Documentation


Route information, including body / route parameters and their validation rules can be exported.

Export to markdown

call toMarkdown() method on any route to generate documentation. Optionally, on an array of routes:

fs.writeFileSync('routes.md', RoutesArray.map(route => route.toMarkdown()).join('<br><br>'))

Here's an example of a route that uses all features available for documentation.

You can see the resulting markdown output here.

View Documentation in terminal

It's also possible to enable making and OPTIONS http request to a route and receive a text output of the route's documentation, which may be helpful during development.

Expample:

import {SuperRoute} from "./SuperRoute";

function shouldShowHelp() {
  return process.env.test === 'true'
}

class RouteWithHelp extends SuperRoute {
  showHelp = (self: RouteWithHelp) => {
    return process.env.test === 'true';
  }
}

// Or 

const route = new BasicRoute({
  //... route settings ...//
  showHelp: true // or false
})


curl --request OPTIONS localhost:8080/path/to/route

Route Versioning

SuperRoute can also VersionedMiddleware objects to handle route versioning. See version-router-express for further configuration details.

Example:

configure route with versioned routes

app.use(VersionedRouter.ExtractVersionFromHeader('App-version'));

// .... server logic .... //

new BasicRoute({
  path: 'versioned_route',
  verb: 'get',
  name: 'versioned route',
  versionedMiddleware: [
    {
      version: '1.0.0',
      default: false,
      middleware: [
        (req: Request, res: Response, next: NextFunction) => {
          console.log('route 2 function 1')
          next()
        },
        (req: Request, res: Response, next: NextFunction) => {
          console.log('route 2 function 2')
          res.send({route: '1'})
        },
      ]
    },
    {
      version: '>=1.2.0 <2.0.0',
      default: false,
      middleware: [
        (req: Request, res: Response, next: NextFunction) => {
          console.log('route 2 function 1')
          next()
        },
        (req: Request, res: Response, next: NextFunction) => {
          console.log('route 2 function 2')
          res.send({route: '2'})
        },
      ]
    },
    {
      version: '2.0.0',
      default: true,
      middleware: [
        (req: Request, res: Response, next: NextFunction) => {
          console.log('route 3 function 1')
          next()
        },
        (req: Request, res: Response, next: NextFunction) => {
          console.log('route 3 function 2')
          res.send({route: '3'})
        },
      ]
    }
  ]
})

make requests with custom header

curl -H 'App-version: 1.0.0' -X GET localhost:8081/versioned_rout
## ==> '{route: '1'}'
curl -H 'App-version: 1.1.0' -X GET localhost:8081/versioned_rout
## will resolve to default
## ==> '{route: '3'}' 
curl -H 'App-version: 1.3.0' -X GET localhost:8081/versioned_rout
## ==> '{route: '2'}'

API Reference


Interface: RouteSettings

Configuration Object for SuperRoute instance

const route = new SuperRoute({
  path: 'some/path',
  verb: 'post',
  name:'some name'
  // ....
})
NameTypeDescriptionRequired
pathExpressHttpVerbHttp Verbtrue
verbstringRoute pathtrue
namestringRoute Nametrue
commentsstringAdditional comments for documentationfasle
descriptionstringRoute descriptionfasle
middlewareRequestHandler[]Route Middlewarefasle
versionedMiddlewareVersionedMiddleware[]An Array of VersionedMiddleware instances - see https://www.npmjs.com/package/version-router-expressfasle
bodyParamsBodyParameter[]An Array of BodyParameter instances, defining required and/or optional parameters for request body as well as input validation testsfasle
routeParamsRouteParameter[]An Array of RouteParameter instances, defining the types and validation rules for route parametersfasle
authenticatebooleanWhen true, will mount the authentication function as middleware before other routesfasle
permissionsRoutePermissionsA RoutePermissions object for use with the package's standard access control functionfasle
responseContentTypestringresponse content type - used to set headersfalse
errorHandlerOptionsErrorHandlerOptionsOptions that will be passed to the global error handler
redirectOnErrorstringOptional redirect when error is handled by the routefalse
showHelpboolean

or

(self: SuperRoute) => boolean
When set to true, will return an ascii output of the requested route's information when called with the OPTIONS http method.
Example:
curl --request OPTIONS https://localhost:8080/path/to/my/route
fasle
responseFormatstringresponse body type - used for automated testingfalse
responseReturnTypeSuccessResponseresponse format used for documentationfalse

Class: SuperRoute


Base Class for an express route super route Middleware order:

  1. Authentication function
  2. Access control
  3. Route Parameters validation
  4. Route Parameters validation
  5. Route Specific middleware defined in the middleware or versioned middleware arrays

Implements

Methods

handle

handle(middlewareArgs: IArguments, errorOrMessage: string | Error | RouteError, statusCode?: number, respondWith?: string, log?: boolean, redirect?: string | false, options?: { [key: string]: any; }): void

handles errors in the route's logic.

errors can be channeled to a custom error handler specific to the route by defining $$errorHandler by default, a RouteError object containing route and request data will be created and passed to next(err)

Parameters:
NameTypeDefault valueDescription
middlewareArgsIArguments-req, res, next from the express middleware function
errorOrMessagestringErrorRouteError
statusCodenumber500html response status code
respondWith?string-optional custom error message to send as response
logbooleanfalselog the error to the console if true
redirectstringfalse-
options?object-options object to pass to a custom error handler

Returns: void

checkPermissions

StaticcheckPermissions(userPermissions: string | string[], permissions: RoutePermissions, hierarchy: string[]): boolean

checks if the user has the permissions defined in the permissions object and according to the defined hierarchy For use with an access control function.

Parameters:
NameType
userPermissionsstring
permissionsRoutePermissions
hierarchystring[]

Returns: boolean


mount

mount(router: Router): void

mounts the route on a router instance or express app

Parameters:
NameType
routerRouter

Returns: void

Defined in: src/SuperRoute.ts:159


toMarkdown

toMarkdown(): string

Generates markdown documentation for hte route

Returns: string

Defined in: src/SuperRoute.ts:418


DefaultErrorHandler

StaticDefaultErrorHandler(err: RouteError
RouteErrorI, req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: NextFunction): void

a default error handler to mount as the last middleware of the app

Parameters:
NameType
errRouteError
RouteErrorI
reqRequest
resResponse
nextNextFunction

Returns: void

Class: BodyParameter

Defines a parameter expected to be present in the request's body

constructor

new BodyParameter(name: string, type?: ParameterType, description?: string, required?: boolean, additionalTests?: RequestParameterTestFunction[]): BodyParameter

Constructs a BodyParameter instance

Parameters:
NameTypeDefault valueDescription
namestring-property name
typeParameterType'any'the expected type of the parameter. if defined, will throw an error if the parameter's type doesn't match
descriptionstring''text that will be displayed in the rendered help output
requiredbooleantrueif true, will throw an error when the property is missing
additionalTestsRequestParameterTestFunction[][]an array of additional test functions and their description
nullablebooleanfalsewhen true, allows a required parameter to be null, ignoring it's type definition

Class: RouteParameter

Defines a parameter expected to be present in the request's route

constructor

new RouteParameter(name: string, description?: string, required?: boolean, additionalTests?: [RequestParameterTestFunction](#Interface: RequestParameterTestFunction)[]): RouteParameter

Constructs a BodyParameter instance

Parameters:
NameTypeDefault valueDescription
namestring-property name
descriptionstring''text that will be displayed in the rendered help output
requiredbooleantrueif true, will throw an error when the property is missing
additionalTestsRequestParameterTestFunction[][]an array of additional test functions and their description

Interface: RequestParameterTestFunction

A test function and test description for testing request parameters

new BodyParameter('age', 'number', 'user age', true, [
  {
    test: (value) => value > 18,
    description: 'User must be over 18'
  }
])
NameTypeDescription
testtest(value: any): booleana test function that receives the parameter value as an argument and returns a boolean
descriptionstringan optional description of the test that will be displayed in the error output in case the function returns false

Interface: AccessControlFunction

Access control function for SuperRoute settings When mounted, it will be called with the route's permissions and should return a RequestHandler

AccessControlFunction(permissions: RoutePermissions): RequestHandler

Parameters:
NameType
permissionsRoutePermissions

Interface: RoutePermissions

Access Control configuration for a route instance or extending class

  • equalOrGreaterThan - requester must have a permission level that is equal or greater than the given string as defined by the hierarchy array.

  • specific - requester must have all the given permissions

  • merge - when set to 'and' requester must satisfy both the specific an hierarchical rules.

Example:

{
  equalOrGreaterThan: 'admin';
  specific: ['specialPermission', 'awesomeDude'];
  merge: 'and'
}

will only grant access to admins that also have the specialPermission and awesomeDude permisions

Interface: ErrorHandlerOptions

Indexable

▪ [key: string]: any

Properties

log

Optional log: boolean


redirectOnError

Optional redirectOnError: string


Class: RouteError

A SuperRoute error with additional data

  • Error

    RouteError

constructor

+ new RouteError(message: string, statusCode: number, redirect?: null | string | false, log?: boolean): RouteError

Constructs a RouteError instance

Parameters:
NameTypeDefault valueDescription
messagestring-Error message
statusCodenumber-Http status code
redirectnull string falsefalsetells the error handler if it should redirect the request
logbooleanfalsetells the error handler to log the error - use it to override the default logging behaviour for specific errors when needed.

Returns: RouteError

Properties

logError

logError: boolean


message

Error message

message: string

Inherited from: Error.message


redirect

Tells the error handler if it should redirect the request

redirect: string | false


requestPath

The path of the request that invoked the route that where the error was thrown

Optional requestPath: string


response

Optional response to replace the error message when sent to the user • Optional response: string


route

The path of the route where the error was thrown

Optional route: string


statusCode

Response status code

statusCode: number


Methods

handle

Handles a RouteError with the data of the given route. Example:

new RouteError('SomeError', 400, null, true).handle(this, req, res, next);

handle(route: SuperRoute, req: Request, res: Response, next: NextFunction): void

Parameters:
NameType
routeany
reqRequest<ParamsDictionary, any, any, ParsedQs, Record<string, any>>
resResponse<any, Record<string, any>>
nextNextFunction

Returns: void

Defined in: src/RouteError.ts:66


respondWith

Attach a custom response to the RouteError object

respondWith(response: string): RouteError

Parameters:
NameType
responsestring

Returns: RouteError

Defined in: src/RouteError.ts:61


FromError

Generate a RouteError instance from a vanilla Error instance.

StaticFromError(err: Error, statusCode: number, redirect?: null | string | false, log?: boolean): RouteError

Parameters:
NameTypeDefault value
errError-
statusCodenumber-
redirectnull
string
false
false
logbooleanfalse

Returns: RouteError

Type aliases

ExpressHttpVerb

Available http verbs for super-route settings object

Ƭ ExpressHttpVerb: "get" | "post" | "put" | "head" | "delete" | "options" | "trace" | "copy" | "lock" | "mkcol" | "move" | "purge" | "propfind" | "proppatch" | "unlock" | "report" | "mkactivity" | "checkout" | "merge" | "m-search" | "notify" | "subscribe" | "unsubscribe" | "patch" | "search" | "connect"


ParameterType

Ƭ ParameterType: "string" | "number" | "boolean" | "object" | "array" | "parsableDateString" | "null" | "any"

Optional types for a body parameter

Defined in: src/RequestParameters.ts:93


SuccessResponse

response options

Ƭ SuccessResponse: "message" | "Array" | "object" | "file"

Keywords

FAQs

Package last updated on 09 Jul 2021

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc