Overview
StatusFlow is built with TypeScript and provides full type safety for error handling, responses, and middleware. This guide covers TypeScript-specific features, type imports, and best practices.
Type Safety Benefits
Type-Safe Exceptions Exception classes with typed constructors and properties
Typed Responses Strongly typed response interfaces and utility functions
Generic Support Generic types for custom response data
Autocomplete Full IDE autocomplete for all APIs
TypeScript Setup
Installation
StatusFlow includes TypeScript definitions out of the box:
npm install status-flow express
npm install --save-dev @types/express @types/node typescript
tsconfig.json Configuration
Recommended TypeScript configuration:
{
"compilerOptions" : {
"target" : "ES2020" ,
"module" : "commonjs" ,
"lib" : [ "ES2020" ],
"outDir" : "./dist" ,
"rootDir" : "./src" ,
"strict" : true ,
"esModuleInterop" : true ,
"skipLibCheck" : true ,
"forceConsistentCasingInFileNames" : true ,
"resolveJsonModule" : true ,
"moduleResolution" : "node" ,
"declaration" : true ,
"declarationMap" : true ,
"sourceMap" : true
},
"include" : [ "src/**/*" ],
"exclude" : [ "node_modules" , "dist" ]
}
Enable strict mode to get the full benefits of TypeScript’s type checking with StatusFlow.
Type Imports
StatusFlow exports all necessary types for type-safe development:
Core Types
import type {
StatusFlowOptions ,
StatusFlowLang ,
} from 'status-flow' ;
Exception Classes
import {
HttpException ,
BadRequestException ,
UnauthorizedException ,
ForbiddenException ,
NotFoundException ,
ConflictException ,
InternalServerErrorException ,
} from 'status-flow' ;
Response Types
import type { HttpResponse } from 'status-flow' ;
import { createSuccessResponse , createHttpResponse } from 'status-flow' ;
Middleware Types
import { statusFlowMiddleware , httpErrorMiddleware } from 'status-flow' ;
import type { Request , Response , NextFunction } from 'express' ;
Type-Safe Exception Handling
Using Exception Classes
Exception classes provide type safety for status codes and messages:
import { NotFoundException , BadRequestException } from 'status-flow' ;
import type { Request , Response } from 'express' ;
interface User {
id : string ;
name : string ;
email : string ;
}
app . get ( '/users/:id' , ( req : Request , res : Response ) => {
const userId = req . params . id ;
if ( ! userId ) {
// TypeScript knows this is a 400 error
throw new BadRequestException ( 'User ID is required' );
}
const user : User | undefined = database . findUser ( userId );
if ( ! user ) {
// TypeScript knows this is a 404 error
throw new NotFoundException ( 'User not found' , { userId });
}
// TypeScript ensures user is defined here
res . json ({ success: true , data: user });
});
Custom Exception Details with Types
Define interfaces for exception details:
import { NotFoundException , BadRequestException } from 'status-flow' ;
interface ValidationErrorDetail {
field : string ;
message : string ;
value : unknown ;
constraint : string ;
}
interface NotFoundDetail {
resourceType : string ;
resourceId : string | number ;
searchedAt : Date ;
}
app . post ( '/users' , ( req : Request , res : Response ) => {
const { email , age } = req . body ;
if ( ! email || typeof email !== 'string' ) {
const detail : ValidationErrorDetail = {
field: 'email' ,
message: 'Email must be a non-empty string' ,
value: email ,
constraint: 'required|string' ,
};
throw new BadRequestException ( 'Validation failed' , detail );
}
const existingUser = database . findByEmail ( email );
if ( existingUser ) {
const detail : NotFoundDetail = {
resourceType: 'user' ,
resourceId: existingUser . id ,
searchedAt: new Date (),
};
throw new ConflictException ( 'User already exists' , detail );
}
// Create user...
});
Type-Safe StatusFlow Function
Using StatusFlowOptions
The StatusFlow function accepts a typed options object:
import { StatusFlow } from 'status-flow' ;
import type { StatusFlowOptions , StatusFlowLang } from 'status-flow' ;
function createErrorResponse (
code : number ,
lang : StatusFlowLang ,
message ?: string ,
extraData ?: Record < string , unknown >
) {
const options : StatusFlowOptions = {
code ,
lang ,
overrideMessage: message ,
extra: extraData ,
};
return StatusFlow ( options );
}
app . get ( '/error' , ( req : Request , res : Response ) => {
const lang : StatusFlowLang = req . headers [ 'x-lang' ] === 'en' ? 'en' : 'es' ;
const response = createErrorResponse (
404 ,
lang ,
'Resource not found' ,
{ timestamp: Date . now () }
);
res . status ( 404 ). json ( response );
});
Type-Safe Language Handling
import type { StatusFlowLang } from 'status-flow' ;
function getLanguageFromRequest ( req : Request ) : StatusFlowLang {
const langHeader = req . headers [ 'x-lang' ];
// TypeScript ensures we return 'es' | 'en'
if ( langHeader === 'en' ) return 'en' ;
return 'es' ; // Default
}
app . get ( '/status' , ( req : Request , res : Response ) => {
const lang = getLanguageFromRequest ( req );
const response = StatusFlow ({ code: 200 , lang });
res . json ( response );
});
Generic Response Types
Typed Success Responses
Use generics for type-safe success responses:
import { createSuccessResponse } from 'status-flow' ;
import type { HttpResponse } from 'status-flow' ;
interface User {
id : string ;
name : string ;
email : string ;
createdAt : Date ;
}
interface Product {
id : number ;
name : string ;
price : number ;
inStock : boolean ;
}
app . get ( '/users/:id' , async ( req : Request , res : Response ) => {
const user : User = await database . getUser ( req . params . id );
// TypeScript knows the response includes User data
const response = createSuccessResponse < User >(
user ,
'User retrieved successfully'
);
res . json ( response );
});
app . get ( '/products' , async ( req : Request , res : Response ) => {
const products : Product [] = await database . getProducts ();
// TypeScript knows the response includes Product[] data
const response = createSuccessResponse < Product []>(
products ,
'Products retrieved successfully'
);
res . json ( response );
});
Custom Generic Response Wrapper
Create your own type-safe response wrapper:
import type { HttpResponse } from 'status-flow' ;
interface ApiResponse < T > extends HttpResponse {
data ?: T ;
timestamp : string ;
requestId : string ;
}
function createApiResponse < T >(
data : T ,
message : string ,
status = 200 ,
requestId : string
) : ApiResponse < T > {
return {
status ,
message ,
data ,
timestamp: new Date (). toISOString (),
requestId ,
};
}
interface PaginatedData < T > {
items : T [];
total : number ;
page : number ;
limit : number ;
}
app . get ( '/users' , async ( req : Request , res : Response ) => {
const page = parseInt ( req . query . page as string ) || 1 ;
const limit = parseInt ( req . query . limit as string ) || 10 ;
const { users , total } = await database . getUsersPaginated ( page , limit );
// TypeScript knows the exact shape of the response
const response = createApiResponse < PaginatedData < User >>(
{ items: users , total , page , limit },
'Users retrieved' ,
200 ,
req . id
);
res . json ( response );
});
Type-Safe Middleware
Typed Error Middleware
import { Request , Response , NextFunction } from 'express' ;
import { HttpException } from 'status-flow' ;
import type { HttpResponse } from 'status-flow' ;
function customErrorMiddleware (
err : unknown ,
req : Request ,
res : Response ,
next : NextFunction
) : void {
// Type guard for HttpException
if ( err instanceof HttpException ) {
const response : HttpResponse = {
status: err . status ,
message: err . message ,
details: err . details ,
};
res . status ( err . status ). json ( response );
return ;
}
// Handle other error types
const response : HttpResponse = {
status: 500 ,
message: 'Internal Server Error' ,
};
res . status ( 500 ). json ( response );
}
app . use ( customErrorMiddleware );
Type Guards
Create type guards for runtime type checking:
import { HttpException } from 'status-flow' ;
function isHttpException ( error : unknown ) : error is HttpException {
return error instanceof HttpException ;
}
function hasStatusCode ( error : unknown ) : error is { status : number } {
return (
typeof error === 'object' &&
error !== null &&
'status' in error &&
typeof ( error as any ). status === 'number'
);
}
app . use (( err : unknown , req : Request , res : Response , next : NextFunction ) => {
if ( isHttpException ( err )) {
// TypeScript knows err is HttpException
res . status ( err . status ). json ({
status: err . status ,
message: err . message ,
details: err . details ,
});
return ;
}
if ( hasStatusCode ( err )) {
// TypeScript knows err has status property
res . status ( err . status ). json ({ error: 'An error occurred' });
return ;
}
res . status ( 500 ). json ({ error: 'Internal Server Error' });
});
Typed Route Handlers
Custom Request Types
Extend Express Request with custom properties:
import { Request } from 'express' ;
interface AuthenticatedRequest extends Request {
user : {
id : string ;
email : string ;
role : 'admin' | 'user' ;
};
}
function requireAuth (
req : Request ,
res : Response ,
next : NextFunction
) : asserts req is AuthenticatedRequest {
const token = req . headers . authorization ?. replace ( 'Bearer ' , '' );
if ( ! token ) {
throw new UnauthorizedException ( 'Authentication required' );
}
const user = verifyToken ( token );
if ( ! user ) {
throw new UnauthorizedException ( 'Invalid token' );
}
( req as AuthenticatedRequest ). user = user ;
next ();
}
app . get ( '/profile' , requireAuth , ( req : Request , res : Response ) => {
// TypeScript knows req is AuthenticatedRequest after requireAuth
const authReq = req as AuthenticatedRequest ;
res . json ({
success: true ,
data: authReq . user ,
});
});
Typed Request Parameters
import { Request , Response } from 'express' ;
import { NotFoundException } from 'status-flow' ;
interface UserParams {
id : string ;
}
interface UserQuery {
include ?: 'posts' | 'comments' ;
}
interface CreateUserBody {
name : string ;
email : string ;
password : string ;
}
type UserRequest = Request < UserParams , {}, {}, UserQuery >;
type CreateUserRequest = Request <{}, {}, CreateUserBody >;
app . get ( '/users/:id' , ( req : UserRequest , res : Response ) => {
const { id } = req . params ; // TypeScript knows id is string
const { include } = req . query ; // TypeScript knows include is 'posts' | 'comments' | undefined
const user = database . findUser ( id );
if ( ! user ) {
throw new NotFoundException ( 'User not found' );
}
res . json ({ success: true , data: user });
});
app . post ( '/users' , ( req : CreateUserRequest , res : Response ) => {
const { name , email , password } = req . body ; // All properly typed
if ( ! name || ! email || ! password ) {
throw new BadRequestException ( 'Missing required fields' );
}
const user = database . createUser ({ name , email , password });
res . status ( 201 ). json ({ success: true , data: user });
});
Advanced TypeScript Patterns
Result Type Pattern
Implement a Result type for safer error handling:
import { HttpException } from 'status-flow' ;
type Result < T , E = HttpException > =
| { success : true ; data : T }
| { success : false ; error : E };
function findUser ( id : string ) : Result < User > {
const user = database . findUser ( id );
if ( ! user ) {
return {
success: false ,
error: new NotFoundException ( 'User not found' , { userId: id }),
};
}
return { success: true , data: user };
}
app . get ( '/users/:id' , ( req : Request , res : Response ) => {
const result = findUser ( req . params . id );
if ( ! result . success ) {
throw result . error ;
}
// TypeScript knows result.data is User
res . json ({ success: true , data: result . data });
});
Branded Types for IDs
type UserId = string & { readonly __brand : 'UserId' };
type ProductId = number & { readonly __brand : 'ProductId' };
function createUserId ( id : string ) : UserId {
return id as UserId ;
}
function createProductId ( id : number ) : ProductId {
return id as ProductId ;
}
function getUser ( id : UserId ) : User | undefined {
return database . findUser ( id );
}
app . get ( '/users/:id' , ( req : Request , res : Response ) => {
const userId = createUserId ( req . params . id );
const user = getUser ( userId );
if ( ! user ) {
throw new NotFoundException ( 'User not found' );
}
res . json ({ success: true , data: user });
});
Best Practices
Always use TypeScript’s strict mode for maximum type safety: {
"compilerOptions" : {
"strict" : true
}
}
Import types with the type keyword for better tree-shaking: import type { StatusFlowOptions , StatusFlowLang } from 'status-flow' ;
import { StatusFlow } from 'status-flow' ;
Define interfaces for complex data
Create interfaces for request bodies, params, and responses: interface CreateUserRequest {
name : string ;
email : string ;
password : string ;
}
interface UserResponse {
id : string ;
name : string ;
email : string ;
createdAt : Date ;
}
Use generics for reusable code
Create generic utility functions for common patterns: function findOrThrow < T >(
item : T | undefined ,
errorMessage : string
) : T {
if ( ! item ) {
throw new NotFoundException ( errorMessage );
}
return item ;
}
Use type guards for runtime type checking: function isValidEmail ( value : unknown ) : value is string {
return typeof value === 'string' && value . includes ( '@' );
}
Next Steps
API Reference Explore the complete TypeScript API documentation
Express Integration Learn more about Express integration patterns
Error Handling Advanced error handling patterns
Custom Responses Create type-safe custom response formats