Dependency Injection + Itty Router = dtty.
Web framework for Cloudflare workers inspired by NestJS.
To use Dtty, simply create a new instance and let it handle all incoming requests.
// index.ts
const app = new Dtty();
export default {
async fetch(
request: Request,
env?: Record<string, any>,
ctx?: ExecutionContext,
): Promise<Response> {
return app.handle(request, env, ctx);
},
};
Controllers are registered through the Dtty.registerControllers
method. This handles the mapping of routes and application of controller level and method level middleware.
@Controller("/route")
class RouteController {
@Get("/")
handleGet() {}
@Post("/")
handlePost() {}
@Put("/")
handlePut() {}
@Delete("/")
handleDelete() {}
}
/* ----- */
app.registerControllers(
// Array of controller classes
RouteController,
);
Route parameters can be injected into controller methods with the @Param()
decorator which takes an optional string to inject an individual parameter as opposed to all the parameters.
Optionally, the @Param()
decorator takes a second parameter that to parse and validate the parameter. For example, if an endpoint expects an id
parameter of type number
(e.g /request/1234
), the included IntegerTransformer
will automatically validate that the parameter is an integer and will transform the string into an integer before supplying it to the controller handler.
- IntegerTransformer
- UuidTransformer
@Get('/route/:id')
getRouteById(@Param("id") id: string) {}
@Get('/route/:id')
getRouteByNumberId(@Param("id", IntegerTransformer) id: number) {}
Much like route parameters, query parameters can be injected into the controller with the @Query()
decorator. The decorator takes a configuration object to inject either an individual parameter or all the parameters with optional transformation and validation.
When working with esbuild
, some of the metadata that typescript emits and class-transformer
relies upon is missing. Therefore, all class properties should be decorated with the @Type
decorator to manually specify the type of the field.
@Get('/named')
handleNamedParam(@Query({paramName: "test"}) value: string) {
// maps the parameter named `test` out without transformation
}
@Get('/unnamed')
handleUnnamed(@Query() values: Record<string, unknown>) {
// maps all the query parameters out without transformation or validation
}
@Get('/named/transformed')
handleNamedAndTransformed(@Query({paramName: "test", transformer: IntegerTransformer}) value: number) {
// maps the parameter named `test` out and transforms into an integer
}
@Get('/unnamed/validated')
handleUnnamedAndValidated(@Query({paramsType: QueryDto}) values: QueryDto) {
// maps all the query parameters out and transforms and validates against a class definition
}
The request body can be both validated and injected into controller methods via the @Body()
decorator. This decorator optionally takes a class constructor to perform transformation and validation via class-transformer and class-validator respectively.
@Put('/route/:id')
updateRouteById(@Param('id') id: string, @Body(UpdateRouteDto) body: UpdateRouteDto) {}
Exceptions thrown by an endpoint are caught at three levels:
- Method
- Controller
- Global
Dtty provides an interface called ExceptionHandler
to define how classes can be used to catch and handle exceptions. The HandleException
decorator optionally takes a parameter to filter exceptions by type to enable finer grained control over exception handling logic. For a given exception:
- Evaluate any method level exception handlers for the specific exception type
- Evaluate any un-filtered method level exception handlers
- Evaluate any controller level exception handlers for the specific exception type
- Evaluate any un-filtered controller level exception handlers
- Evaluate any global level exception handlers for the specific exception type
- Evaluate any un-filtered global level exception handlers
If no exception handlers are found, the application will return with code 500
.
@Controller()
@ApplyHandlers(ControllerExceptionHandler)
export class IndexController {
@Get("")
@ApplyHandlers(MethodExceptionHandler, GenericHandler)
getIndex() {}
}
- Global middleware
- Controller middleware
- Route middleware
- Global body transformer
- Global body validator
- Route param transformer / validator
- Global exception handler
- Controller exception handler
- Route exception handler
- URL query param mapper
- URL query param transformer / validator
- Optimizations for tree shaking
- Custom DI library
- UUID transformer
- Async constructors