@scythe/core

Core module for scythe framework

Usage no npm install needed!

<script type="module">
  import scytheCore from 'https://cdn.skypack.dev/@scythe/core';
</script>

README

Common information

This micro-framework created to help implement NodeJS backend applications with TypeScript without pain. The main idea is to provide several abstractions and tools to increase development speed and use popular used technologies as built-in features powered by TypeScript and also add ability for extension without losing flexibility. We use ExpressJS as core technology.

Table of contents

Installation

npm i @scythe/core or yarn add @scythe/core

Usage

To create application you can use ScytheApplicationBuilder (see below for more information).

import { ScytheApplicationBuilder } from '@scythe/core';

export const testApp = new ScytheApplicationBuilder().build();

This module will export instance of ScytheApplication (see below for more information) all you need to do is to call start method.

Let's make it more complex and create endpoint to get sample string. Logic part that contain routing, docs and middleware definitions called controller. Controller is abstract class with TypeScript decorators for storing metadata upon which routing, OpenAPI documentation, middlewares logic etc are generated.

import { controller, get, summary, description, response } from '@scythe/core';

@controller('/test', 'Test')
export abstract class TestController {
  @get()
  @summary('Test')
  @description('Test description')
  @response(200, 'string')
  public testGet() {
    return 'sample string';
  }
}

And update main application file to

import { ScytheApplicationBuilder } from '@scythe/core';
import { TestController } from './TestController';

export const testApp = new ScytheApplicationBuilder().addController(TestController).build();

This will create express server on 3030 port with 2 endpoints:

  1. GET /test - which will return sample string
  2. GET /swagger - which will render swagger docks by metadata from decorators

To add more endpoints just define more methods with decorators (to see all available decorators keep reading).

Built-in features

.env

All .env variables will be available throught proces.env[VARIABLE_NAME].
This particular module use only one variable is:

PORT = 3030 //defines your server port

Scythe Application Builder

This tool helps you to build Scythe Application (you can also use ScytheApplication class itself). Base example was provided in usage section.

Available ScytheApplicationBuilder methods:

.setConfig(config: IConfig): ScytheApplicationBuilder

Sets config for the application. Available fields:

  • withoutServer?: boolean - if sets to true, application will be started without express server
  • validateRequests?: boolean - sets validation for requests according to open API schema (see more in OpenAPI validation section)
  • validateResponse?: boolean - sets validation for responses according to open API schema (see more in OpenAPI validation section)
  • hydrateOpenAPIModels?: boolean - enables hydration by OpenAPI docks
  • openAPIInfo?: InfoObject - info object for OpenAPI docs
  • openAPIDocksEndpoint?: string- endpoint to open api docs UI generated from metadata

Default config is:

validateResponse: true
validateRequests: true

.setLogPath(path: string): ScytheApplicationBuilder

Sets path to application log file. Default path is ./logs/application.log

.addRouter(url: PathParams, router: IBuiltRouter): ScytheApplicationBuilder

Adds router created by ScytheRouterBuilder, which will be mounted on url passed in params

.addModule(module): ScytheApplicationBuilder

Adds custom module to your application (more about modules see in https://www.npmjs.com/package/@scythe/types

.addAdditionalMiddleware(middleware: RequestHandler): ScytheApplicationBuilder

Adds additional middleware into middlewares poll. Accept ordinary express middleware as param

.addJob(schedule: string, job: TJobHandler): ScytheApplicationBuilder

Schedule a job to be executed by timetable. Params:

  • schedule: string - CRON schedule string
  • job: Function - function to be executed

.addController(Controller: any, basePath?: string): ScytheApplicationBuilder

Adds controller to current application to be mounted on basePath.
Controller is abstract class decorated with @controller decorator (see more in decorators section)

.build(): ScytheApplication

Builds ScytheApplication based on provided info

Scythe Application

Base application class which contains almost all necessary logic.

Available ScytheApplication methods

constructor(config: IConfig, logPath: string, additionalMiddlewares: RequestHandler[] = [])

Inits application with provided params:

config
  • withoutServer?: boolean - if sets to true, application will be started without express server
  • validateRequests?: boolean - sets validation for requests according to open API schema (see more in OpenAPI validation section)
  • validateResponse?: boolean - sets validation for responses according to open API schema (see more in OpenAPI validation section)
  • hydrateOpenAPIModels?: boolean - enables hydration by OpenAPI docks
  • openAPIInfo?: InfoObject - info object for OpenAPI docs
  • openAPIDocksEndpoint?: string - endpoint to open api docs UI generated from metadata
logPath

Path to application log file

additionalMiddlewares

Array of express middlewares to be applied

.addRouter(url: PathParams, router: IBuiltRouter): void

Adds router created by ScytheRouterBuilder, which will be mounted on url passed in params

.addModule(module): void

Adds custom module to your application (more about modules see in https://www.npmjs.com/package/@scythe/types

.start(): void

Starts an application running all modules and creating express servers if needs to

.addJob(schedule: string, job: TJobHandler): void

Schedule a job to be executed by timetable. Params:

  • schedule: string - CRON schedule string
  • job: Function - function to be executed

.registerController(Controller: any, basePath?: string): void

Adds controller to current application to be mounted on basePath.
Controller is abstract class decorated with @controller or @webSocketController decorator (see more in decorators section)

.stop(): void

Stops application destroying connections and stopping jobs and modules

.getOpenAPIDocs(): OpenAPIObject

Return generated open API docs

Routing

Decorators

This framework provides amount of decorators to create controller to simplify working with routing/docs/validation ets.

Available decorators:

@controller(path: string, tag?: string): ClassDecorator

Sets decorated class as controller. All methods inside will be mounted on path from params. If tag param is passed, all endpoints will have this open API tag by default

@baseSecurity(securitySchema: string): ClassDecorator

Sets base security schema for controller. Accept name of open API defined security schema

@commonMiddlewares(...middlewares: RequestHandler[]): ClassDecorator

Sets common middlewares to be executed before each method logic in controller

@method(method: string, path: string = '/'): MethodDecorator

Binds http method to class method. If class method decorated with it, it will be treated as endpoint by passed path

@get(path: string = '/'): MethodDecorator

Alias for get method

@put(path: string = '/'): MethodDecorator

Alias for put method

@del(path: string = '/'): MethodDecorator

Alias for delete method

@post(path: string = '/'): MethodDecorator

Alias for post method

@patch(path: string = '/'): MethodDecorator

Alias for patch method

@middlewares(...middlewares: RequestHandler[]): MethodDecorator

Sets middlewares to be executed before method logic in controller

@tag(tag: string): MethodDecorator

Sets open API tag

@summary(summary: string): MethodDecorator

Sets Open API summary

@description(description: string): MethodDecorator

Sets Open API description

@response(responseCode: number, type: string | INewable, isArray: boolean = false): MethodDecorator

Sets Open API response by path as Open API schema reference with selected response status code

@defaultResponses(...responses: EDefaultResponse[]): MethodDecorator

Sets predefined responses. Can be:

  • EDefaultResponse.NotFound
  • EDefaultResponse.NoContent
  • EDefaultResponse.Unauthorized
  • EDefaultResponse.Forbidden
  • EDefaultResponse.ValidationError

@parameters(...parameters: ParameterObject[]): MethodDecorator

Sets Open API parameters

@headerParameter(name: string, schema: string, required: boolean = true): MethodDecorator

Sets Open API header parameter by its schema reference name

@security(name: string): MethodDecorator

Sets Open API security schema by its name

@deprecated(): MethodDecorator

Mark endpoint as deprecated

Scythe Router Builder

Builder for express routing implementation, it provides extended version of express router by adding methods to write open API docs.
Should be used only if controllers do not cover your case.
Usage:

import { ScytheRouterBuilder } from '@scythe/core';

const builder = new ScytheRouterBuilder();
builder
  .useNamespace('/yourPath')
  .useOpenAPIDocs({
    get: {
      tags: ['Your Tags'],
      summary: 'Summary',
      description: 'Descrition',
      consumes: ['application/json'],
      produces: ['application/json'],
      parameters: [{ name: 'filter', description: 'filter', required: false, type: 'string', in: 'query' }],
      responses: {
        '200': {
          description: 'description',
          type: 'array',
          items: {
            $ref: '#/definitions/swagerSchemaName'
          }
        }
      }
    }
  })
  .buildNamespace()
  .get(yourMiddlewaresGoesHere);

export const awesomeRouter = builder.buildRouter();

Available methods:

.useNamespace(namespace: string): ScytheRouterBuilder

Define route namespace (ex. /users, /users/:id)

.useOpenAPIDocs(specs: PathItemObject): ScytheRouterBuilder

Sets OpenAPI docs for your namespace

.setOpenAPIComponents(schema)

Sets OpenAPI definitions for your router

.buildNamespace()

Builds namespace and return express router route instance, where you can add any request methods

.buildRouter()

Builds router to be passed into ScytheApplication or ScytheApplicationBuilder

Open API

This framework provides full set of types of Open API v3, so it'll be easy for you to implement all definitions.

Open API Components Builder

This builder helps you create Open API definition to pass into controller or router builder

Available methods:

.useOpenAPISchemas(schemas: TOpenAPISchemas): OpenAPIComponentsBuilder

Sets Open API schemas according to Open API v3

.useOpenAPIResponses(responses: TOpenAPIResponses): OpenAPIComponentsBuilder

Sets Open API responses according to Open API v3

.useOpenAPIParameters(parameters: TOpenAPIParameters): OpenAPIComponentsBuilder

Sets Open API parameters according to Open API v3

.useOpenAPIExamples(examples: TOpenAPIExamples): OpenAPIComponentsBuilder

Sets Open API examples according to Open API v3

.useOpenAPIRequestBodies(requestBodies: TOpenAPIRequestBodies): OpenAPIComponentsBuilder

Sets Open API request bodies according to Open API v3

.useOpenAPIHeaders(headers: TOpenAPIHeaders): OpenAPIComponentsBuilder

Sets Open API headers according to Open API v3

.useOpenAPISecuritySchemas(securitySchemas: TOpenAPISecuritySchemas): OpenAPIComponentsBuilder

Sets Open API security schemas according to Open API v3

.useOpenAPILinks(links: TOpenAPILinks): OpenAPIComponentsBuilder

Sets Open API links according to Open API v3

.useOpenAPICallbacks(callbacks: TOpenAPICallbacks): OpenAPIComponentsBuilder

Sets Open API callbacks according to Open API v3

.buildComponents()

Builds all components to be passed into @registerComponents decorator or into router builder

Open API Helpers

This part provides amount of function helpers to ease Open API components writing

Available helpers:

getQueryParam(name: string, schema: SchemaObject | ReferenceObject, required: boolean = true)

Generates Open API query param component

Example:
{
 name: 'test', in: 'query', schema: { $ref: '#/components/schemas/test' }, required: true}

createRefContent(schemaName: string, isArray: boolean = false)

Creates Open API json content based on schema.

Example:
{
 content: { 'application/json': { schema: { $ref: '#/components/schemas/test' } } }}

createSchemaContent(schema: SchemaObject)

Same as createRefContent only take Open API schema object

getRefObject(name: string, type: EComponentTypes = EComponentTypes.Schemas)

Generates Open API ref object by passed type. Available types:

  • EComponentTypes.Schemas
  • EComponentTypes.RequestBodies
  • EComponentTypes.Responses
  • EComponentTypes.Parameters
  • EComponentTypes.Examples
  • EComponentTypes.Headers
  • EComponentTypes.SecuritySchemas
  • EComponentTypes.Links
  • EComponentTypes.Callbacks
Example:
{
 $ref: '#/componens/headers/TestHeader'}

createValidationError(dataPath: string, message: string)

Return object representation of Open API validation error object (same object returns by Open API Validator)

Example:
{
 error: { message: 'Validation error', details: [{ dataPath: '.body.test', message: 'should be string' }] }}

Open API Validator

If pass

validateResponse: true
validateRequests: true

into your application, all your responses/requests will be validated according to your Open API definitions.