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 🙏

© 2024 – Pkg Stats / Ryan Hefner

node-tiny

v1.1.18

Published

A simple node service framework

Downloads

1,178

Readme

Tiny

Introduce

  • 简体中文

  • Tiny is a simple server-side framework based on Node+Typescript. Its core code is very small and provides many interesting classes and decorators to help you save time configuring routes, validating parameters, setting up Jwt, writing API documentation, and other additional features.

  • Tiny was born out of the problem of asynchronous code confusion that is prone to occur during Node development. Unlike other frameworks, it restricts the use of asynchronous middleware. It is usually recommended to only use the @Middleware decorator on the controller method for setting. Its advantage is that it will not make the asynchronous code too confusing, but its disadvantage is that it is not flexible enough.

  • It is usually recommended to use async only when it comes to @Middleware, @Jwt, and tiny.run execution. Tiny has built-in corresponding error handling. If async is used elsewhere, the errors need to be handled by yourself.

Environment

NODE Version NPM Version

Directory

Install

  • Before installing, download and install Node.js. Node.js V18.0.0 or later is required.

Creating a Tiny Application

  • To create a Tiny-based project, you can use the project template provided by Tiny. This template sets up a simple project structure for your reference.
npm create node-tiny <project-name>

Installing Tiny in an existing project

npm install --save node-tiny

View current API information

  • If you use the Tiny template to build your project, you can visit the /doc.html address to view the simplest API information.

Use

First-time usage example

  • File index.ts
import Tiny from 'node-tiny';
import { Manager } from '@/controller/manager';

const { CreateApp, Router } = Tiny;

const tiny = new CreateApp();
const router = new Router();

router.config({ prefix: '/api' });
router.register(new Manager());

tiny.listen(4000);
  • File @/controller/manager.ts
import Tiny from 'node-tiny';
const { Controller, Summary, Dto, StatusCode, Get } = Tiny;

export class Manager extends Controller {
  @Get()
  @Summary('This is a summary')
  public async index(context) {
    context.send(StatusCode.success, new Dto({ code: StatusCode.success, result: 'hello word', msg: 'success' }));
  }
}

API Description

CreateApp

  • Use Tiny.CreateApp to create a Tiny application
const tiny = new CreateApp();
  • Use tiny.run to set the program running content, receiving a request context Context parameter, which is usually configured before tiny.listen
tiny.run = async (context) => {
  router.work(context);
}
  • Use tiny.error to monitor program execution errors, use context.error to trigger the execution of tiny.error
tiny.error = (err) => {
  // ToDo
}
  • Use tiny.errorCode and tiny.errorMsg to configure the error message, the default is 500 and Internal Server Error
tiny.errorCode = 500
tiny.errorMsg = 'Internal Server Error'

Context

  • Context stores the context information of the entire request, as well as other customized information. The following is the calling process inside tiny.listen
  function listen(...args): Server {
    const server = http.createServer(async (req: IncomingMessage, res: ServerResponse) => {
      try {
        const context = new Context(req, res);
        this.run(context);
      } catch (e: FunctionError) {
        // ...
      }
    });
    return server.listen(...args);
  }
  • context.req:IncomingMessage, Request information

  • context.res:ServerResponse, Response information

  • context.query:ContextQuery, Request query parameters and path parameters, which also means that the two parameters cannot have the same name. This has been implemented in router.work and can be used directly

type ContextQuery = object | null | undefined;
  • context.body:ContextBody, the parameters saved in the body, Tiny does not have a built-in body parsing, you can use a third-party library to parse it in the tiny.run method
type ContextBody = object | string | null | undefined;
  • context.params:ContextParams, the parameters verified by @Tiny.Param.in will be automatically filled in and can be used directly
type ContextParams = object | null | undefined;
  • context.payload:ContextPayload, the information verified by Tiny.Jwt will be automatically filled in after using the @Protected decorator and can be used directly
type ContextPayload = object | string | null | undefined;
  • context.payload:ContextFiles, expandable file information, not set inside Tiny, after using the @Protected decorator, it will be automatically filled in and can be used directly
type ContextFiles = any[] | null | undefined;
  • context.extend:ContextExtend, other information that can be expanded
type ContextExtend = object;
  • context.cookie:CookieManager, cookie management tool, can be used directly
interface CookieManager {
  // Get a cookie by name
  get(name: string): string | undefined;
  // Setting a cookie
  set(name: string, value: string, options: CookieOptions = {}): void;
  // Deleting a cookie
  delete(name: string, options: CookieOptions = {}): void
}
  • context.error:Function, Errors during execution can be thrown to tiny.error for processing

  • context.send<T = Dto>(code: number, data: T, type?: DataType), send request information

context.send(StatusCode.success, new Dto({ code: StatusCode.success, result: 'hello word', msg: 'success' }));
  • context.setQuery(query: ContextQuery), set the address parameter information, which has been implemented in router.work and can be used directly

  • context.setBody(body: ContextBody), set body parameter information, Tiny does not have built-in body parsing, use a third-party library for parsing in the tiny.run method

tiny.run = async (context) => {
  await bodyHandler(context); // Third-party body parsing library
  router.work(context);
};
  • context.setParams(params: ContextParams), set the validation parameter information, after using the Params.in decorator, it will be automatically filled in and can be used directly
class Manager extends Controller {
  @Post()
  @Type()
  @Params.in(HomeIndexInput, ParamsSource.body)
  getParams(context) {
    context.send(StatusCode.success, new Dto({ code: StatusCode.success, result: context.params, msg: 'success' }));
  }
}
  • context.setPayload(payload: ContextPayload), set the Jwt verification information, after using the @Protected decorator, it will be automatically filled in and can be used directly
class Manager extends Controller {
  @Post()
  @Type()
  @Protected()
  jwtVerify(context) {
    context.send(StatusCode.success, new Dto({code: StatusCode.success, result: context.payload, msg: 'success'}));
  }
}
  • context.setFiles(files: ContextFiles), set file information

  • context.setExtend<T>(name: string, value: T), set extended information

Controller

Controller

  • All controllers should inherit from the Controller class
import Tiny from 'node-tiny';
const { Controller, Summary, Dto, StatusCode, Get } = Tiny;

export class Manager extends Controller {
  @Get()
  @Summary('This is a summary')
  public async index(context) {
    context.send(StatusCode.success, new Dto({ code: StatusCode.success, result: 'hello word', msg: 'success' }));
  }
}

Get

  • Use the @Get() decorator to declare a Get method
import Tiny from 'node-tiny';
export class Manager extends Controller {
  @Tiny.Get()
  public async index(context) {
    // ...
  }
}

Delete

  • Use the @Delete() decorator to declare a Delete method
import Tiny from 'node-tiny';
export class Manager extends Controller {
  @Tiny.Delete()
  public async index(context) {
    // ...
  }
}

Post

  • Use the @Post() decorator to declare a Post method
import Tiny from 'node-tiny';
export class Manager extends Controller {
  @Tiny.Post()
  public async index(context) {
    // ...
  }
}

Put

  • Use the @Put() decorator to declare a Put method
import Tiny from 'node-tiny';
export class Manager extends Controller {
  @Tiny.Put()
  public async index(context) {
    // ...
  }
}

Patch

  • Use the @Patch() decorator to declare a Patch method
import Tiny from 'node-tiny';
export class Manager extends Controller {
  @Tiny.Patch()
  public async index(context) {
    // ...
  }
}

Type

  • Use @Type(requestType?: DataType, responseType?: DataType) to declare the request/response content type, which defaults to the DataType.json type
import Tiny from 'node-tiny';

export class Manager extends Controller {
  @Tiny.Patch()
  @Tiny.Type(DataType.formData, DataType.formData)
  public async index(context) {
    // ...
  }
}

Middleware

  • Use @Middleware(handler: ContextAsyncHandler) to set middleware for the controller
import Tiny from 'node-tiny';
function execMiddleware(context, next) {
  if(context.extend.a) {
    context.send(StatusCode.success, new Dto({ code: StatusCode.success, result: 'middleware', msg: 'success' }));
  } else {
    next();
  }
}

export class Manager extends Controller {
  @Tiny.Patch()
  @Tiny.Middleware(execMiddleware)
  public async index(context) {
    // ...
  }
}

Mapping

  • Use the @Mapping(path: string) decorator to reset the routing address
import Tiny from 'node-tiny';
export class Manager extends Controller {
  @Tiny.Patch()
  @Mapping('/manager/test/:id')
  public async index(context) {
    // ...
  }
}

Summary

  • Use the @Summary(summary?: string, describe?: string) decorator to set the description document for the method
import Tiny from 'node-tiny';
export class Manager extends Controller {
  @Tiny.Patch()
  @Summary('Test Method')
  public async index(context) {
    // ...
  }
}

Router

  • Use Router to create a router instance
const router = new Router()
  • router.options:ConnectOptions is the routing configuration item
type ConnectOptions = {
  prefix?: string;
  format?: boolean;
};
  • router.apiJSON:RouterApiJson[] is the api configuration JSON information, which can be used to generate API documentation
type RouterApiJson = {
  module: string;
  describe?: string;
  func: string;
  path: string;
  method: string;
  requestType?: string;
  responseType?: string;
  summary?: string;
  paramsModel?: object;
  resultModel?: object;
};
  • router.routes:RoutesList is the configured route list
type RouteItem = { path: string; method: MethodType; handler: Function };
type RouteValue = {
  REG: RouteItem[];
  [path: string]: RouteItem | RouteItem[];
};
type RoutesList = {
  GET: RouteValue;
  POST: RouteValue;
  PUT: RouteValue;
  PATCH: RouteValue;
  DELETE: RouteValue;
};
  • router.config(options: ConnectOptions)Set routing configuration items

  • router.notFound(context: ContextBase)The route is not found, triggering a 404 error

  • router.work(context: ContextBase)Routing starts

  • router.register(controller: Controller)Registering Routes

router.register(new Manager());

Model

Model

  • Use model<Model>.fill(map: object) to fill the data model
class LoginInput extends Model {
  @Declare()
  name!: string;

  @Declare()
  password!: string;
}

const input = new LoginInput();
const result: ModelResult = input.fill({...});
if(result.valid) {
  // ...
}
  • Use model<Model>.getConfigCache to get the current model configuration
new LoginInput().getConfigCache();
ModelResult
  • Use new ModelResult(valid: boolean, message?: string, value?: any) to set the model validation result

Declare

  • Use the @Declare(description?: string) decorator to declare parameters. Note: Model parameters must use at least Declare
class LoginInput extends Model {
  @Declare()
  name!: string;
}

Required

  • Use the @Required(message?: string) decorator to set the property to be required
class LoginInput extends Model {
  @Declare()
  @Required('The name cannot be empty')
  name!: string;
}

TypeCheck

  • Use the @TypeCheck(type: ParamsType | T, message?: string) decorator to set type checking
class LoginInput extends Model {
  @Declare()
  @TypeCheck(ParamsType.string, 'The name can only be a string')
  name!: string;
}

ArrayCheck

  • Use the @ArrayCheck(type: ParamsType | T, message?: string, maxLength?: number, maxLengthMessage?: string) decorator to set array type checking. The precondition is to set TypeCheck to ParamsType.array
class LoginInput extends Model {
  @Declare()
  @TypeCheck(ParamsType.array, 'Lists can only be arrays')
  @ArrayCheck(ParamsType.string, 'The array content can only be strings')
  list!: string[];
}

StringLength

  • Use the @StringLength(range: number[], message?: string) decorator to set the string length check. The prerequisite is to set TypeCheck to ParamsType.string
class LoginInput extends Model {
  @Declare()
  @TypeCheck(ParamsType.string, 'The name can only be a string')
  @StringLength([1,50], 'The name length can only be 1-50')
  name!: string;
}

TypeCustom

  • Use the @TypeCustom<T>(valid: (value: T) => ModelResult) decorator to set custom validation content and return a ModelResult
class LoginInput extends Model {
  @Declare()
  @TypeCustom((value) => {
    if(value) {
      // ...
    }
    return new Tiny.ModelResult(true,'', value);
  })
  name!: string;
}

Params

Params

  • Use the @Params.in<T extends Model>(params: { new (): T }, type: ParamsSource, validate: boolean = true) decorator to set parameter validation
import Tiny from 'node-tiny';
const { Controller, Post, Params } = Tiny;

class LoginInput extends Model {
  @Declare()
  name!: string;

  @Declare()
  password!: string;
}

export class Manager extends Controller {
  @Post()
  @Params.in(LoginInput, ParamsSource.body)
  public async index(context) {
    console.log(context.params.name)
    console.log(context.params.password)
    // ...
  }
}
  • Use the @Params.out<T extends Model>(result: { new (): T }) decorator to set the output parameter type
import Tiny from 'node-tiny';
const { Controller, Post, Params, Dto, StatusCode } = Tiny;

class LoginOut extends Model {
  @Declare()
  id!: string;

  @Declare()
  name!: string;
}

export class Manager extends Controller {
  @Post()
  @Params.out(LoginOut)
  public async index(context) {
    // ...
    const info = new LoginOut();
    info.fill({...});
    // ...
  }
}

Jwt

Jwt

  • Configure Jwt.sign = <Payload = object>(context: ContextBase, payload: Payload) => string | null | undefined;Generate token
Jwt.sign = (context, payload) => {
  context.cookie.set('token', JSON.stringify(payload));
  return JSON.stringify(payload);
};
  • Configure Jwt.verify = (context: ContextBase, next: () => any) => any; to verify the token
Jwt.verify = (context, next) => {
  const token = context.cookie.get('token');
  if (token) {
    context.setPayload(getJSON(token));
    next();
  } else {
    Jwt.refuse(context);
  }
}
  • Configure Jwt.refuse = (context: ContextBase) => any; to handle verification failure. The default is as follows
Jwt.refuse = (context, next) => {
  context.send(StatusCode.success, new Dto({ code: StatusCode.authError, msg: 'No permission to access temporarily', result: null }));
}

Protected

  • Use the @Protected() decorator, the configured method will be Jwt verified
import Tiny  from 'node-tiny';
const { Controller, Post, Protected } = Tiny;

export class Manager extends Controller {
  @Post()
  @Protected()
  public async index(context) {
    console.log(context.payload)
    // ...
  }
}

Dto

Dto

  • Use new Dto({ code: number | string, result?: any, msg?: string }) to set the Response return code

Values

MethodType

  • Method Type
export enum MethodType {
  head = 'HEAD',
  options = 'OPTIONS',
  get = 'GET',
  delete = 'DELETE',
  post = 'POST',
  put = 'PUT',
  patch = 'PATCH'
}

DataType

  • The data structure type of body
export enum DataType {
  json = 'application/json',
  text = 'text/plain',
  html = 'text/html',
  xml = 'text/xml',
  formUrlencoded = 'application/x-www-form-urlencoded',
  formData = 'multipart/form-data',
  other = 'other'
}

ParamsSource

  • Parameter source
export enum ParamsSource {
  query = 'query',
  body = 'body'
}

ParamsType

  • Parameter Data Type
export enum ParamsType {
  number = 'number',
  boolean = 'boolean',
  string = 'string',
  array = 'array'
}

StatusCode

  • Response Status Code
export const StatusCode = {
  success: 200,
  paramsError: 400,
  authError: 401,
  notFound: 404,
  timeout: 408,
  serveError: 500
};

Other