Express utilities and decorators for building web applications with Inversify dependency injection container
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Comprehensive set of action result classes for structured HTTP responses. Implements the action result pattern for type-safe response handling with content negotiation support and automatic HTTP response generation.
Base interface that all action results implement for consistent response handling.
/**
* Interface for HTTP action results that can be executed to produce HTTP responses
*/
interface IHttpActionResult {
/** Executes the action result to produce an HTTP response message */
executeAsync(): Promise<HttpResponseMessage>;
}Core response message class containing status, headers, and content.
/**
* HTTP response message containing status code, headers, and content
*/
class HttpResponseMessage {
/** Response content */
content: HttpContent;
/** HTTP response headers */
headers: OutgoingHttpHeaders;
/** HTTP status code */
statusCode: number;
/**
* Creates a new HTTP response message
* @param statusCode - HTTP status code (defaults to 200)
*/
constructor(statusCode?: number);
}Action results for successful HTTP responses (2xx status codes).
/**
* Returns a 200 OK response without content
*/
class OkResult implements IHttpActionResult {
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a 200 OK response with negotiated content
*/
class OkNegotiatedContentResult<T> implements IHttpActionResult {
constructor(content: T);
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a 201 Created response with location and content
*/
class CreatedNegotiatedContentResult<T> implements IHttpActionResult {
constructor(location: string | URL, content: T);
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a JSON response with custom status code
*/
class JsonResult implements IHttpActionResult {
readonly json: unknown;
readonly statusCode: number;
constructor(json: unknown, statusCode: number);
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a streaming response
*/
class StreamResult implements IHttpActionResult {
constructor(readableStream: Readable, contentType: string, statusCode?: number);
executeAsync(): Promise<HttpResponseMessage>;
}Action results for error HTTP responses (4xx and 5xx status codes).
/**
* Returns a 400 Bad Request response
*/
class BadRequestResult implements IHttpActionResult {
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a 400 Bad Request response with error message
*/
class BadRequestErrorMessageResult implements IHttpActionResult {
constructor(message: string);
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a 404 Not Found response
*/
class NotFoundResult implements IHttpActionResult {
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a 409 Conflict response
*/
class ConflictResult implements IHttpActionResult {
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a 500 Internal Server Error response
*/
class InternalServerErrorResult implements IHttpActionResult {
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a 500 Internal Server Error response with exception details
*/
class ExceptionResult implements IHttpActionResult {
constructor(error: Error);
executeAsync(): Promise<HttpResponseMessage>;
}Action results for specialized HTTP responses and redirects.
/**
* Returns a redirect response (302 Found by default)
*/
class RedirectResult implements IHttpActionResult {
constructor(uri: string | URL);
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a response with custom status code
*/
class StatusCodeResult implements IHttpActionResult {
constructor(statusCode: number);
executeAsync(): Promise<HttpResponseMessage>;
}
/**
* Returns a response using a custom HTTP response message
*/
class ResponseMessageResult implements IHttpActionResult {
constructor(message: HttpResponseMessage);
executeAsync(): Promise<HttpResponseMessage>;
}Usage Examples:
import { controller, httpGet, httpPost, BaseHttpController } from "inversify-express-utils";
import {
JsonResult,
RedirectResult,
StreamResult,
BadRequestErrorMessageResult
} from "inversify-express-utils";
@controller("/files")
class FileController extends BaseHttpController {
@httpGet("/:id/download")
async downloadFile(@requestParam("id") id: string) {
const file = await this.fileService.getFile(id);
if (!file) {
return new NotFoundResult();
}
const stream = this.fileService.createReadStream(file.path);
return new StreamResult(stream, file.mimeType, 200);
}
@httpPost("/upload")
async uploadFile(@requestBody() fileData: any) {
if (!fileData || !fileData.name) {
return new BadRequestErrorMessageResult("File name is required");
}
try {
const uploadedFile = await this.fileService.upload(fileData);
const location = `/files/${uploadedFile.id}`;
return new CreatedNegotiatedContentResult(location, uploadedFile);
} catch (error) {
return new ExceptionResult(error);
}
}
@httpGet("/stats")
async getStats(@queryParam("format") format: string) {
const stats = await this.fileService.getStats();
if (format === "json") {
return new JsonResult(stats, 200);
}
return new OkNegotiatedContentResult(stats);
}
@httpGet("/:id/redirect-to-cdn")
async redirectToCdn(@requestParam("id") id: string) {
const file = await this.fileService.getFile(id);
if (!file) {
return new NotFoundResult();
}
const cdnUrl = `https://cdn.example.com/files/${file.hash}`;
return new RedirectResult(cdnUrl);
}
@httpPost("/custom-response")
async customResponse() {
// Create custom HTTP response message
const response = new HttpResponseMessage(202);
response.headers["X-Processing-Id"] = "12345";
response.headers["Retry-After"] = "30";
response.content = new JsonContent({
message: "File processing started",
status: "queued"
});
return new ResponseMessageResult(response);
}
}HTTP content classes used by action results for response content handling.
/**
* Abstract base class for HTTP content
*/
abstract class HttpContent {
/** Content headers */
readonly headers: OutgoingHttpHeaders;
/** Reads the content asynchronously */
abstract readAsync(): Promise<unknown>;
}
/**
* JSON content implementation
*/
class JsonContent extends HttpContent {
constructor(json: unknown);
readAsync(): Promise<string>;
}
/**
* String content implementation
*/
class StringContent extends HttpContent {
constructor(content: string);
readAsync(): Promise<string>;
}
/**
* Stream content implementation
*/
class StreamContent extends HttpContent {
constructor(stream: Readable, mediaType: string);
readAsync(): Promise<Readable>;
}Usage Examples:
import { HttpResponseMessage, JsonContent, StringContent } from "inversify-express-utils";
@controller("/content")
class ContentController extends BaseHttpController {
@httpGet("/custom-json")
async customJson() {
const response = new HttpResponseMessage(200);
response.content = new JsonContent({
data: "custom data",
timestamp: Date.now()
});
response.headers["Cache-Control"] = "no-cache";
return this.responseMessage(response);
}
@httpGet("/custom-text")
async customText() {
const response = new HttpResponseMessage(200);
response.content = new StringContent(
"Custom plain text response",
"text/plain; charset=utf-8"
);
return this.responseMessage(response);
}
@httpGet("/xml-data")
async xmlData() {
const xmlData = "<root><item>data</item></root>";
const response = new HttpResponseMessage(200);
response.content = new StringContent(xmlData, "application/xml");
return this.responseMessage(response);
}
}The library uses standard HTTP status codes from the http-status-codes package for consistency.
// Common status codes used by action results
const StatusCodes = {
OK: 200,
CREATED: 201,
ACCEPTED: 202,
NO_CONTENT: 204,
FOUND: 302,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
CONFLICT: 409,
INTERNAL_SERVER_ERROR: 500
};