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

express_annotations

v0.1.1

Published

Annotations system for espress routes

Downloads

14

Readme

express-annotations

Annotation for express with typescript

NPM

installations

npm install --save express_annotations

how to use it

With the AbstractExpressServer

You only need to extend the AbstractExpressServer


import {AbstractExpressServer}  from "express_annotations/dist/AbstractExpressServer";
import * as bodyParser from "body-parser";

export class Server extends AbstractExpressServer {
	
  // This method is called from the abstract class
  // to allow you to add custom use as bodyParser
  protected doPrepareApp(): void {
    this.app.use(bodyParser.json());
    this.app.use(bodyParser.urlencoded({ extended: false }));
  }

  // when the express app is ready this handler is called
  protected listenHandler () : void {
    console.log ("Express ready");
  }	
  
  // called if an error happend when initializing app
  protected initError(error: any): void {
    console.log("Error happen !", error);
  }
}

Then in a bin.ts file


import {Server} from "./Server";

let server = new Server(3000);

// the method loadController is to loadControllers
// it accepts regexp and return a native promise
// Be cause the loadController method is asynchrone, the method listen will be called
// only when all the controllers are loaded
server.prepareApp().loadController("./controllers/*.js").listen();

Then you need to configure your controller.

With annotation

If you don't want to use the Abstract Class what you need to do is to create a class then add the @ExpressApp annotation on your express app.


import * as express from "express";

export class Server {
  @ExpressApp
  app = express ();
}

Routing

For routing you only need to add an annotation on your routing class and implements IRoute :

import * as express from "express";
import {IRoute, Router} from "express_annotations/dist/ExpressAnnotations";

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;
}

By this way you have access to the controller so if you want to manipulate some rules using the express router, you still can.

====
When using the AbstractController the method loadController will try to make a new on every function exported in the module (Class are converted as function)

handling call

GET / POST / PATCH / PUT / DELETE

All the method are accessible by annotation inside your Routing class.
All method work with promise so you need to return a promise with your result.
You can either return a couple of header / body, an object or a status

GET

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  // json:true means you want express return as json
  @GET({path:"/json", json:true})
  private handleGet () {
    return Promise.resolve({hello:"world !"});
  }

  // status:true means you want express return as response.sendStatus (status)
  @GET({path:"/status", status:true})
  private handleStatus () {
    return Promise.resolve(200);
  }

  // no status and no json mean you send your result as it is : response.send ("Hello world")
  @GET({path:"/plain"})
  private handleStatus () {
    return Promise.resolve("Hello world");
  }

  // By this way you can send custom header
  @GET({path:"/customHeader", json:true})
  private handleStatus () {
    return Promise.resolve({header:{"custom-header":"hello header !"}, body:{result:"Hello world with custom headers"});
  }
}

POST

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  @POST ({path="/", json:true})
  private handlePost (@body body : any) {
    return Promise.resolve ({sendBody:body});
  }
}

PATCH

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  @PATCH ({path="/", json:true})
  private handlePatch (@body body : any) {
    return Promise.resolve ({method:"patch", sendBody:body});
  }
}

PUT

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  @PUT ({path="/", json:true})
  private handlePut (@body body : any) {
    return Promise.resolve ({method:"put", sendBody:body});
  }
}

DELETE

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  @DELETE ({path="/", json:true})
  private handleDelete () {
    return Promise.resolve ({method:"delete"});
  }
}

Parameters

To get headers, path params or queries you only need to annotate your method arguments

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  @GET ({path="/:id", json:true})
  private handleFindById (@param("id") id : string) {
    return Promise.resolve ({method:"findById", id:id});
  }

  @GET ({path="/", json:true})
  private handleFindByQueryId (@query("id") id : string) {
    return Promise.resolve ({method:"findByQueryId", id:id});
  }

  @GET ({path="/byHeader", json:true})
  private handleFindByHeaderId (@EHeader("id") id : string) {
    return Promise.resolve ({method:"findByHeaderId", id:id});
  }

  // if no name is given all headers will be returned
  @GET ({path="/byHeader2", json:true})
  private handleFindByCustomProp (@EHeader() headers : any) {
    return Promise.resolve ({method:"handleFindByCustomProp", id:headers["id"]});
  }

}

If you override the express request and need a property inside the request you can also need the @custom annotation

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  @GET ({path="/byHeader", json:true})
  private handleFindByCustomProp (@custom("myCustomProp") myCustomProp : any) {
    return Promise.resolve ({method:"handleFindByCustomProp", myProp:myCustomProp});
  }

}

Finaly if you want to have the orignal express Request and Response

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  // the noResponse: true is to avoid to send response twice
  @GET ({path="/byOldSchool", noResponse:true})
  private handleOldSchool (@ERequest() request : Request, @EResponse() response : Response) {
    response.json ({oldSchool:true, response:"Hello world !"});
    return Promise.resolve ();
  }
}

Automatic Path, Params and Query

If you don't specify name or path, annotations system will automatically handle them by the method or arguments name

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  // the generated path will be "<host>:<port>/example/automaticPath/:name"
  // For example http://localhost:3000/example/automaticPath/foo?start=25
  // will result to a call with name=foo and start = 25
  @GET ({json:true})
  private automaticPath (@param name:string, @query start : number) {
    return Promise.resolve ({method:"automaticPath", name:name, start: start});
  }
}

Error Handling

Route Error Handling

You can ser error handler for a local router with the @ErrorHandler annotation :

@Router({route: "/example"})
export class Test implements IRoute {
  // the router is automatically set by the annotation
  router : express.Router;

  ...

  @ErrorHandler
  private errorHandler (error : any, req : Request, res : Response, next : NextFunction) {
  	console.log ("Error handling from route 'example'");
  	res.status(500);
  	res.json(error);
  }
}

Global Error Handling

Because the error method in express have to be set as the last use method, we only manage to add it in the helper AbstractExpressServer.

If you are using this helper you can leave the default method provided or override the method errorHandler

This is the default behaviour :

protected errorHandler (error : any, req : Request, res : Response, next : NextFunction) {
    let status = error.status || 500;
    let err = error.error || error;
    res.status(status);
    if (typeof err == "object")
        res.json(err);
    else {
        res.send(err);
    }
}