Routing

Plumier generate routes directly from controllers. By default it will looks into the ./controller directory. Except other directory or controller classes specified on the configuration.

The main idea of Plumier route generator is generate route based on: <directories>/<controller name>/<method name>. Further more decorator(s) @route can be used to customize the generation result

Name Convention#

If no @route decorator provided, route will be generated by convention using controller name, method name and parameter names. By default route by convention will have GET http method.

export class AnimalController {
list(offset:number, limit:number)
}
GET /animal/list?offset=<number>&limit=<number>

Directory Convention#

Route generated based on controller directory hierarchy. For example if the controller hierarchy like below:

info

Directory convention can be disabled by using ControllerFacility and set directoryAsPath to false

+ controller
- home-controller.ts
+ api
+ v1
- animal-controller.ts
+ v2
- animal-controller.ts
//controller/home-controller.ts
export class HomeController {
@route.get("/")
index(){
return "My Cool Animal API"
}
}
//controller/api/v1/animal-controller.ts
export class AnimalController {
@route.get("")
get(){
return { name: "Mimi" }
}
}
//controller/api/v2/animal-controller.ts
export class AnimalController {
@route.get(":id")
get(id:string){
return { name: "Mimi" }
}
@route.get("")
all(){
return [{ name: "Mimi" }]
}
}

Route generated:

GET /
GET /api/v1/animal
GET /api/v2/animal/:id
GET /api/v2/animal

Note that AnimalController will have their own root route based on their directory.

Http Verb Override#

Http verb override will only override the http verb of the route, route will be constructed using controller name (omit controller word) and action name

export class AnimalController {
@route.put()
modify(id:number, model:AnimalDto)
@route.post()
save(model:AnimalDto){}
}
POST /animal/save
PUT /animal/modify?id=<number>

Absolute Route Override#

Absolute route override (route start with /) will ignore all the controller and action name including the directory convention name, instead it will used provided route.

export class AnimalController {
@route.get("/beast/:id")
get(id:number){}
@route.get("/beast/list")
list(last:number, limit:number)
}
GET /beast/:id
GET /beast/list?last=<number>&limit=<number>

Relative Route Override#

Relative route override will only rename the name of the action and keep using controller name.

export class AnimalController {
@route.get(":id")
get(id:number){}
@route.get("list")
list(last:number, limit:number)
}
GET /animal/:id
GET /animal/list?last=<number>&limit=<number>

Ignore Action Name#

You can provided empty string on the route parameter to ignore action name

export class AnimalController {
@route.get("")
get(id:number){}
}
GET /animal?id=<number>

Route Parameter Mapping#

Its possible to use different parameter name on route and the action parameter name like below

export class AnimalController {
@route.get(":id", { name: "id" })
get(name:number){}
}

Above code means that the id route parameter will be bound to the name action parameter. Using this configuration will get rid the route analysis error.

Example Restful Api#

Sum up of above rules you can construct clean Restful API routes like below:

export class AnimalController {
@route.get(":id")
get(id:number){}
@route.get("")
getAll(){}
@route.post("")
save(animal:any)
@route.put(":id")
modify(id:number, animal:any)
@route.delete(":id")
delete(id:number){}
}
GET /animal/:id
GET /animal
POST /animal
PUT /animal/:id
DELETE /animal/:id

Root Route#

Root route only override the controller name

@route.root("/beast")
export class AnimalController {
get(id:number){}
list(last:number, limit:number)
}
GET /beast/get?id=<number>
GET /beast/list?last=<number>&limit=<number>
info

Relative/Absolute rule also applied into the Root route. When placed inside directory convention, an absolute Root route will skipped the directory name convention.
And logically Relative/Absolute rule, doesn't affected Root route name.

Parameterized Root Route#

Root route can be parameterized and provided backing parameter on all of the action, except absolute route

@route.root("/beast/:beastId")
export class AnimalController {
get(beastId:number, id:number){}
//absolute route doesn't need to provided backing parameter for beastId
@route.get("/list")
list(last:number, limit:number)
}
GET /beast/<beastId>/get?id=<number>
GET /list?last=<number>&limit=<number>

Root Route Parameter Mapping#

Same as the @route decorator, the @route.root also provide parameter mapping which work the same

@route.root("/beast/:beastId", { name: "beastId" })
export class AnimalController {
get(name:number, id:number){}
}

Example Nested Restful API#

By using rules above you can configure nested restful api like below:

@route.root("category/:type/animal")
export class AnimalController {
@route.get(":id")
get(type:string, id:number){}
@route.get("")
getAll(type:string){}
@route.post("")
save(type:string, animal:any)
@route.put(":id")
modify(type:string, id:number, animal:any)
@route.delete(":id")
delete(type:string, id:number){}
}
GET category/:type/animal/:id
GET category/:type/animal
POST category/:type/animal
PUT category/:type/animal/:id
DELETE category/:type/animal/:id

Multiple Route Decorator#

Multiple routes can be applied to an action, this functionalities needed for example when hosting an SPA with url rewrite

info

A more convenient way to serve SPA url rewrite is using @route.historyApiFallback() see here for more info

export class HomeController {
@route.get("/")
@route.get("/home")
@route.get("/about-us")
@route.get("/cart")
index(id:number){
return response.file("<file path>")
}
}
GET /
GET /home
GET /about-us
GET /cart

Multiple Root Route#

Multiple root routes can also be applied into a controller and resulting create multiple routes for each methods.

@route.root("/home")
@route.root("/dashboard")
export class HomeController {
index(id:number){
return response.file("<file path>")
}
}
GET /home/index
GET /dashboard/index
info

Absolute route when combined with multiple root route will cause conflict, consider to avoid them.

Ignore Route Generation#

By default Route Generation System will generate all methods inside controller into route. In some case you need to create an helper method inside controller but you don't want the method generated into route.

export class HomeController {
@route.ignore()
helper(){
}
@route.get()
index(){
helper()
return "My Cool Animal API"
}
}

Above code showing that helper method will not be generated because it is marked with @route.ignore()

Ignore Controller Completely#

@route.ignore() can be applied on controller and make all routes inside controller ignored completely. This feature important on CRUD Route generation, where controller automatically generated and you don't have control to the controller.

@route.ignore()
export class HomeController {
@route.get()
index(){
return "My Cool Animal API"
}
}

Above code will cause all routes inside HomeController ignored completely.

Ignore Specific Method#

If you have a controller with inheritance, you can ignore super class routes by specifying method name like below

export class ControllerBase {
@route.get()
get(){ }
@route.post()
save(){ }
@route.put()
replace(){ }
}
@route.ignore({ applyTo: ["save", "replace"]})
export class UsersController extends ControllerBase{}

Above code showing that we ignore the save and replace method which cause only GET /users/get will be generated.