Skip to content

Web

This section will introduce the backend part of unioc. Although it's called @unioc/web, it currently mainly provides adaptation and support for backend environments such as Node/Bun/Deno. Its main responsibility is to provide adapters for various different Web request-related frameworks and protocols, such as Restful, WebSocket, etc.

Restful

@unioc/web internally provides a class scanner (RestfulScanner), which implements the IRestfulScanner interface and is associated with four subclasses, which will be introduced one by one below.

IRestfulScanner

A scanner that provides methods to traverse and manage scanned classes, as well as other convenient methods such as mergePath (used to merge the paths of the @RestController and method annotations like @Get, @Post decorators), etc.

Type Definition

ts
export interface IRestfulScanner {
  /**
   * ### Merge the controller path and method path.
   *
   * 🔍 Merge the controller path and method path.
   *
   * @param controllerPath - The controller path.
   * @param methodPath - The method path.
   */
  
mergePath
(
controllerPath
: string,
methodPath
: string): string
/** * ### Add a new controller wrapper. * * 📉 Add a new controller wrapper. */
add
(
controller
: IRestControllerWrapper): this
/** * ### Find all the controller wrappers. * * 🚰 Find all the controller wrappers. * * @param force - Whether to force the search. */
findAll
(
force
?: boolean): IRestControllerWrapper[]
/** * ### Find all the method operators. * * 🚰 Find all the method operators. */
findAllMethodOperator
(): IRestMethodOperator[]
/** * ### Resolve all the controller wrappers. * * 👌 Resolve all the controller wrappers. */
resolveAll
():
Promise
<this>
/** * ### Search for the method operator by full path. * * 🔍 Search for the method operator by full path. * * @param fullPath - The full path. Must start with `/`. */
search
(
fullPath
: `/${string}`): IRestMethodOperator[]
/** * ### Resolve the node handler. * * 🐢 Resolve the node handler for the current scanner. */
resolveNodeHandler
?():
Awaitable
<IRestfulNode.
Handler
>
/** * ### Resolve the connect handler. * * 🔗 Resolve the connect handler for the current scanner. */
resolveConnectHandler
?():
Awaitable
<IRestfulConnect.
Handler
>
/** * ### Resolve the context handler. * * ⛰️ Resolve the context handler for the current scanner. */
resolveContextHandler
?():
Awaitable
<IRestfulContext.
Handler
>
/** * ### Resolve the web standard handler. * * 🌐 Resolve the web standard handler for the current scanner. */
resolveWebStandardHandler
?():
Awaitable
<IRestfulWebStandard.
Handler
>
}

IRestControllerWrapper

A controller wrapper, each wrapper has a corresponding IClassWrapper from the core package to navigate to the IoC container. At this layer, you can also get the decorator metadata at the @RestController level. For example, with the following pseudo-code:

ts
@RestController('/api')
@RestController('/api2')
class RestController {}

Both defined prefix routes can be fully read through methods such as getFullControllerOptions at this layer.

Type Definition

ts
export interface IRestControllerWrapper {
  /**
   * ### Get the restful scanner.
   *
   * 🔍 Get the restful scanner.
   */
  
getRestfulScanner
(): IRestfulScanner
/** * ### Get the class wrapper. * * 🔍 Get the class wrapper. */
getClassWrapper
():
IClassWrapper
/** * ### Get the current controller options. * * ⚙️ Get the current controller options. */
getFullControllerOptions
(): RestController.
Options
[]
/** * ### Get the current method options. * * ⚙️ Get the current method options. */
getFullMethodOptions
(): IHttpMethodMetadata[]
/** * ### Find all the controller operators. * * 🚰 Find all the controller operators. * * @param force - Whether to force the search. */
findAll
(
force
?: boolean): IRestControllerOperator[]
/** * ### Add a new controller operator. * * 📉 Add a new controller operator. */
add
(
operator
: IRestControllerOperator): this
}

IRestControllerOperator

This layer contains the individual @RestController information obtained after traversing IRestControllerWrapper. For example, in the example above:

ts
@RestController('/api')
@RestController('/api2')
class AppController {}

At this layer, the paths /api and /api2 will be read one by one, and convenient methods are provided to access them.

Type Definition

ts
export interface IRestControllerOperator {
  /**
   * ### Get the current controller wrapper.
   *
   * 🔍 Get the current controller wrapper.
   */
  
getControllerWrapper
(): IRestControllerWrapper
/** * ### Get the current controller path. * * 📃 Get the current controller path. */
getControllerPath
(): string
/** * ### Get the current controller options. * * ⚙️ Get the current controller options. */
getFullControllerOptions
(): RestController.
Options
/** * ### Find all the method wrappers. * * 🚰 Find all the method wrappers. * * @param force - Whether to force the search. */
findAll
(
force
?: boolean): IRestMethodWrapper[]
/** * ### Add a new method wrapper. * * 📉 Add a new method wrapper. */
add
(
operator
: IRestMethodWrapper): this
}

IRestMethodWrapper

Method wrapper that can get the current method's propertyKey, etc., and provides some convenient methods to get the current method's reflection information, such as getReturnType, getParamTypes, etc. In addition, it also provides an execute method to execute the current method.

Type Definition

ts
export interface IRestMethodWrapper {
  /**
   * ### Get the current controller operator.
   *
   * 🔍 Get the current controller operator.
   */
  
getControllerOperator
(): IRestControllerOperator
/** * ### Get the current property key. * * 🌾 Get the current property key. */
getPropertyKey
():
PropertyKey
/** * ### Execute the current method. * * 📖 Execute the current method. */
execute
<
T
,
TE
>(
args
: unknown[],
extraOptions
:
Record
<string, unknown>):
Promise
<
IResult
<
T
,
TE
>>
/** * ### Get the current method's return type. * * 😚 Shortcut to get the current method's return type by IMetadataScanner. */
getReturnType
():
IClass
| undefined
/** * ### Get the current method's param types. * * 😚 Shortcut to get the current method's param types by IMetadataScanner. */
getParamTypes
():
IClass
[]
/** * ### Find all the method operators. * * 🚰 Find all the method operators. * * @param force - Whether to force the search. */
findAll
(
force
?: boolean): IRestMethodOperator[]
/** * ### Add a new method operator. * * 📉 Add a new method operator. */
add
(
operator
: IRestMethodOperator): this
}

IRestMethodOperator

Method operator, which obtains the single route information for the current method. It can obtain the httpMethod, path, etc. set by the current annotation in the current method, and provides some convenient methods such as getFullPath (which helps you automatically call IRestfulScanner.mergePath to merge the paths of @RestController and method annotations).

Type Definition

ts
export interface IRestMethodOperator {
  /**
   * ### Get the current method wrapper.
   *
   * 🔍 Get the current method wrapper.
   */
  
getMethodWrapper
(): IRestMethodWrapper
/** * ### Get the current http method. * * 🌍 Get the current http method.Get the current http method. */
getHttpMethod
():
IHttpMethod
/** * ### Get the current full path. * * 🧮 Computed to full path using `mergePath`. * It will merge the {@linkcode IRestControllerOperator.getControllerPath} and {@linkcode getPath}. */
getFullPath
(): string
/** * ### Get the current path. * * 📃 Get the current method path. */
getPath
(): string
/** * ### Get the current method options. * * ⚙️ Get the full current method's options. */
getFullMethodOptions
(): IHttpMethodMetadata
}

Why Do We Need Such a Complex Design?

Although the design above seems complex, it enables any other framework to support unioc's Restful functionality through the @unioc/web adapter.

We have made our best effort to keep things as simple and clear as possible, making it easy for almost anyone to quickly create decorators or even implement their own Web adapter framework. When better performing Web frameworks emerge, they can be quickly adapted, maximizing usability and extensibility 🚀

For the IoC Framework Layer

You can implement adaptation for any Web IoC framework by inheriting these base implementation classes, such as @unioc/adapter-nestjs:

ts
import type { IRestfulScanner } from 'unioc/web'
import { RestfulScanner } from 'unioc/web'

export class NestJsRestfulScanner extends RestfulScanner implements IRestfulScanner {
  // Just override the methods that need to be overridden
}

For the Web Framework Layer

Through a unified interface, all controllers and methods in the entire container can be easily traversed, such as @unioc/web-express. The code is just a little bit, but it has achieved adaptation for the express framework, because most of the work is done by @unioc/web.

Contributors

Changelog