Component loading system and error handling utilities for application bootstrapping, file loading, and exception management in Egg.js applications.
Component loaders responsible for loading and initializing various application components during startup.
/**
* Base application loader class
*/
class EggApplicationLoader {
/** Load application components in correct order */
load(): Promise<void>;
/** Load configuration files */
loadConfig(): Promise<void>;
/** File loader utility */
FileLoader: typeof FileLoader;
/** All loaded plugins information */
allPlugins: Record<string, any>;
/** Application information */
appInfo: EggAppInfo;
/** Configuration metadata */
configMeta: any;
/** Plugin loading order */
orderPlugins: string[];
}
/**
* Application worker loader for loading app worker components
*/
class AppWorkerLoader extends EggApplicationLoader {
/** Load controllers, services, middleware, etc. for app worker */
}
/**
* Agent worker loader for loading agent worker components
*/
class AgentWorkerLoader extends EggApplicationLoader {
/** Load components specific to agent worker */
}Usage Examples:
// The loaders are used internally by Egg.js during application startup
// You typically don't use them directly, but can access their results
// In application ready callback
export default class AppBootHook {
async didReady() {
const { app } = this;
// Access loader information
console.log('Loaded plugins:', app.loader.orderPlugins);
console.log('App info:', app.loader.appInfo);
// Access loaded components through app
console.log('Available controllers:', Object.keys(app.controller || {}));
console.log('Available services:', Object.keys(app.service || {}));
}
}
// Custom loader usage (advanced)
export default class CustomBootHook {
async configWillLoad() {
const { app } = this;
// Access file loader for custom loading
const customLoader = new app.loader.FileLoader({
directory: path.join(app.baseDir, 'app/custom'),
target: app.custom,
inject: app
});
customLoader.load();
}
}Built-in error classes for handling specific error conditions in Egg.js applications.
/**
* Error thrown when cookie size exceeds browser limits
*/
class CookieLimitExceedError extends Error {
constructor(name: string, value: string);
/** Cookie name that exceeded limit */
name: string;
/** Cookie value that caused the error */
value: string;
}
/**
* Error for unhandled promise rejections with additional context
*/
class MessageUnhandledRejectionError extends Error {
constructor(message: string);
/** Error message */
message: string;
}Usage Examples:
import { CookieLimitExceedError } from 'egg';
// Error handling in middleware
export default function errorHandler() {
return async (ctx: Context, next: Next) => {
try {
await next();
} catch (err) {
if (err instanceof CookieLimitExceedError) {
ctx.logger.warn('Cookie limit exceeded: %s = %s', err.name, err.value);
ctx.status = 400;
ctx.body = {
error: 'Cookie too large',
cookieName: err.name
};
} else {
// Handle other errors
ctx.status = err.status || 500;
ctx.body = { error: err.message };
}
}
};
}
// Listening for cookie limit events
export default class Application extends EggApplication {
constructor(options) {
super(options);
// Built-in cookie limit handling
this.on('cookieLimitExceed', ({ name, value, ctx }) => {
const err = new CookieLimitExceedError(name, value);
ctx.coreLogger.error(err);
// Custom handling
ctx.throw(400, 'Cookie size exceeds browser limits');
});
}
}The underlying file loading system used by loaders to discover and load application components.
/**
* File loader options for custom component loading
*/
interface FileLoaderOptions {
/** Directory to load files from */
directory: string | string[];
/** Target object to assign loaded modules */
target: any;
/** Where to inject the target ('app' or 'ctx') */
inject?: 'app' | 'ctx';
/** File matching pattern */
match?: string | string[] | RegExp;
/** File ignore pattern */
ignore?: string | string[];
/** Loader function for processing files */
initializer?: (obj: any, options: any) => any;
/** Call loaded modules as functions */
call?: boolean;
/** Override existing properties */
override?: boolean;
/** Enable caseStyle conversion */
caseStyle?: 'camel' | 'pascal' | 'lower' | 'upper';
/** Custom loading filter */
filter?: (obj: any) => boolean;
}
interface CustomLoaderConfig extends Omit<FileLoaderOptions, 'inject' | 'target'> {
/** Injection target */
inject?: 'ctx' | 'app';
/** Load from plugins/framework */
loadunit?: boolean;
}Usage Examples:
// Custom loader configuration in config
config.customLoader = {
// Load validators into app.validator
validator: {
directory: path.join(appInfo.baseDir, 'app/validator'),
inject: 'app',
caseStyle: 'camel',
filter(validator) {
return typeof validator === 'function';
}
},
// Load utilities into ctx.util
util: {
directory: path.join(appInfo.baseDir, 'app/util'),
inject: 'ctx',
call: true // Call loaded modules as functions
}
};
// Using custom loaded components
export default class UserController extends Controller {
async create() {
const { ctx, app } = this;
const userData = ctx.request.body;
// Use custom validator
const errors = app.validator.user(userData);
if (errors.length > 0) {
ctx.throw(422, 'Validation failed', { errors });
}
// Use custom utility
const hashedPassword = ctx.util.hashPassword(userData.password);
const user = await ctx.service.user.create({
...userData,
password: hashedPassword
});
ctx.body = { user };
}
}Understanding the component loading lifecycle and timing.
/**
* Loader lifecycle hooks (used in app.js)
*/
interface LoaderLifecycle {
/** Called before configuration loading */
configWillLoad?(): void;
/** Called after configuration loaded */
configDidLoad?(): void;
/** Called before all components ready */
didLoad?(): void;
/** Called when application is ready */
willReady?(): void;
/** Called after application ready */
didReady?(): void;
/** Called before server started */
serverDidReady?(): void;
/** Called before closing */
beforeClose?(): void;
}Usage Examples:
// app.js - Application lifecycle hooks
export default class AppBootHook {
constructor(app) {
this.app = app;
}
configWillLoad() {
// Modify configuration before loading
this.app.config.custom = { loaded: false };
}
async configDidLoad() {
// Access final configuration
console.log('Final config:', this.app.config.name);
}
async didLoad() {
// All components loaded, can access services/controllers
console.log('Available services:', Object.keys(this.app.service));
}
async willReady() {
// Perform final setup before ready
await this.app.redis.ping();
}
async didReady() {
// Application is ready
this.app.logger.info('Application started successfully');
}
async serverDidReady() {
// HTTP server is ready
const { server } = this.app;
this.app.logger.info('Server listening on port %d', server.address().port);
}
async beforeClose() {
// Cleanup before shutdown
await this.app.mysql.end();
}
}Common error handling patterns and utilities.
/**
* Application-level error handling
*/
interface EggApplicationCore {
/** Emit error event */
emit(event: 'error', err: Error, ctx?: Context): boolean;
/** Add error event listener */
on(event: 'error', listener: (err: Error, ctx?: Context) => void): this;
}Usage Examples:
// Global error handler in app.js
export default class AppBootHook {
constructor(app) {
this.app = app;
// Global error handling
app.on('error', (err, ctx) => {
// Log error with context
if (ctx) {
ctx.logger.error('Request error:', err);
// Custom error reporting
this.reportError(err, {
url: ctx.url,
method: ctx.method,
userAgent: ctx.get('user-agent'),
ip: ctx.ip
});
} else {
app.logger.error('Application error:', err);
}
});
}
reportError(err, context) {
// Send to error reporting service
// Implementation depends on your error reporting solution
}
}
// Error handling middleware
export default function onerror() {
return async (ctx, next) => {
try {
await next();
} catch (err) {
// Set error properties
err.status = err.status || 500;
err.headers = err.headers || {};
// Emit error event
ctx.app.emit('error', err, ctx);
// Response handling
if (ctx.acceptJSON) {
ctx.body = {
error: err.message,
status: err.status
};
} else {
ctx.body = `<h1>${err.status} ${err.message}</h1>`;
}
ctx.status = err.status;
}
};
}interface EggAppInfo {
/** Package.json content */
pkg: any;
/** Application name */
name: string;
/** Base directory */
baseDir: string;
/** Environment */
env: EggEnvType;
/** Scope name */
scope: string;
/** Home directory */
HOME: string;
/** Root directory */
root: string;
}
interface FileLoader {
/** Full file path symbol */
FULLPATH: symbol;
/** Load files from directory */
load(): void;
}