| íëŠĐ | ëīėĐ |
|---|---|
| Invoke | /serverpod:exception |
| Aliases | /backend:exception, /api:error |
| Tools | Read, Edit, Write, Glob, Grep |
| Model | sonnet |
| Skills | serverpod |
Serverpod Exception Agent#
Specialized agent for generating Serverpod backend exception, validation, and constants classes
Role#
Implements the exception handling system for the Serverpod backend.
- sealed class-based exception definitions
- Validator class patterns
- Constants and ErrorMessages separation
- ExceptionHandler.safeExecute() wrapping
Activation Conditions#
- Activated on
/serverpod:exceptioncommand invocation - Called in Step 1.5 of
/feature:createorchestration
Parameters#
| Parameter | Required | Description |
|---|---|---|
feature_name | â | Feature module name (snake_case) |
entity_name | â | Entity name (PascalCase) |
include_validator | â | Generate Validator (default: true) |
include_constants | â | Generate Constants (default: true) |
Generated Files#
backend/kobic_server/lib/src/feature/{feature_name}/
âââ exception/
â âââ exception.dart # Export file
â âââ {feature_name}_exception.dart # Exception classes
â âââ {feature_name}_error_messages.dart # Error messages
âââ validation/
â âââ validation.dart # Export file
â âââ {feature_name}_validator.dart # Validation classes
âââ constant/
â âââ constant.dart # Export file
â âââ {feature_name}_constants.dart # Constants classes
âââ helper/
âââ helper.dart # Export file
âââ exception_handler.dart # Exception handler
Import Order (Required)#
// 1. Dart standard libraries
import 'dart:core';
// 2. Common constants
import 'package:kobic_server/src/common/constants/http_status_constants.dart';
// 3. Feature internal constants/messages
import '../constant/constant.dart';
Core Patterns#
1. sealed class Exception Definition#
/// Base class for {Feature}-related exceptions
sealed class {Feature}Exceptions implements Exception {
const {Feature}Exceptions(
this.message,
this.statusCode, [
this.stackTrace,
]);
final String message;
final int statusCode;
final StackTrace? stackTrace;
}
/// {Feature} resource not found
final class {Feature}NotFoundException extends {Feature}Exceptions {
{Feature}NotFoundException(int resourceId)
: super(
{Feature}ErrorMessages.notFound('{feature}', resourceId),
{Feature}Constants.httpNotFound,
);
}
/// {Feature} validation failure
final class {Feature}ValidationException extends {Feature}Exceptions {
{Feature}ValidationException(String reason)
: super(
reason,
{Feature}Constants.httpBadRequest,
);
}
/// {Feature} parameter validation failure
final class Invalid{Feature}ParameterException extends {Feature}Exceptions {
Invalid{Feature}ParameterException(String parameter, String reason)
: super(
{Feature}ErrorMessages.invalidParameter(parameter, reason),
{Feature}Constants.httpBadRequest,
);
}
/// {Feature} database operation failure
final class {Feature}DatabaseOperationException extends {Feature}Exceptions {
{Feature}DatabaseOperationException(
String operation,
String errorMessage, [
StackTrace? stackTrace,
]) : super(
{Feature}ErrorMessages.databaseOperationFailed(operation, errorMessage),
{Feature}Constants.httpInternalServerError,
stackTrace,
);
}
2. ErrorMessages Class#
/// {Feature} error message definitions
class {Feature}ErrorMessages {
const {Feature}ErrorMessages._();
/// Resource not found
static String notFound(String resource, int id) =>
'$resource(ID: $id) not found.';
/// Parameter validation failure
static String invalidParameter(String param, String reason) =>
'$param: $reason';
/// Database operation failure
static String databaseOperationFailed(String operation, String error) =>
'Database operation failed ($operation): $error';
/// Required parameter missing
static const String idRequired = 'ID is required.';
static const String idTooSmall = 'ID must be 1 or greater.';
/// Paging related
static const String limitTooSmall = 'limit must be at least 1.';
static const String limitTooLarge = 'limit must be at most 100.';
static const String offsetNegative = 'offset must be 0 or greater.';
}
3. Constants Class#
/// {Feature} related constants
class {Feature}Constants {
const {Feature}Constants._();
// ============ HTTP Status Codes ============
static const int httpOk = HttpStatusConstants.ok; // 200
static const int httpCreated = HttpStatusConstants.created; // 201
static const int httpBadRequest = HttpStatusConstants.badRequest; // 400
static const int httpUnauthorized = HttpStatusConstants.unauthorized; // 401
static const int httpForbidden = HttpStatusConstants.forbidden; // 403
static const int httpNotFound = HttpStatusConstants.notFound; // 404
static const int httpInternalServerError = HttpStatusConstants.internalServerError; // 500
// ============ Paging Constants ============
static const int defaultPageLimit = 20;
static const int maxPageLimit = 100;
static const int minPageLimit = 1;
static const int defaultOffset = 0;
// ============ ID Related ============
static const int minValidId = 1;
// ============ Field Names (for validation) ============
static const String fieldId = 'id';
static const String fieldLimit = 'limit';
static const String fieldOffset = 'offset';
// ============ DB Operation Names ============
static const String dbOperationInsert = '{feature} insert';
static const String dbOperationUpdate = '{feature} update';
static const String dbOperationDelete = '{feature} delete';
static const String dbOperationSelect = '{feature} select';
}
4. Validator Class#
/// {Feature} input validation
class {Feature}Validator {
const {Feature}Validator._();
/// Validate ID (optional)
static void validateId(int? id) {
if (id != null && id < {Feature}Constants.minValidId) {
throw Invalid{Feature}ParameterException(
{Feature}Constants.fieldId,
{Feature}ErrorMessages.idTooSmall,
);
}
}
/// Validate ID (required)
static void validateRequiredId(int? id) {
if (id == null) {
throw {Feature}ValidationException({Feature}ErrorMessages.idRequired);
}
validateId(id);
}
/// Validate paging parameters
static void validatePagingParams(int limit, int offset) {
if (limit < {Feature}Constants.minPageLimit) {
throw Invalid{Feature}ParameterException(
{Feature}Constants.fieldLimit,
{Feature}ErrorMessages.limitTooSmall,
);
}
if (limit > {Feature}Constants.maxPageLimit) {
throw Invalid{Feature}ParameterException(
{Feature}Constants.fieldLimit,
{Feature}ErrorMessages.limitTooLarge,
);
}
if (offset < 0) {
throw Invalid{Feature}ParameterException(
{Feature}Constants.fieldOffset,
{Feature}ErrorMessages.offsetNegative,
);
}
}
/// Null check for object
static void validateNotNull<T>(T? value, String fieldName) {
if (value == null) {
throw Invalid{Feature}ParameterException(
fieldName,
'$fieldName is required.',
);
}
}
}
5. ExceptionHandler#
/// Exception handling wrapper
class ExceptionHandler {
const ExceptionHandler._();
/// Safe execution wrapper
static Future<T> safeExecute<T>(
Future<T> Function() operation, {
required String operationName,
}) async {
try {
return await operation();
} on {Feature}Exceptions {
rethrow;
} on Exception catch (error, stackTrace) {
throw {Feature}DatabaseOperationException(
operationName,
error.toString(),
stackTrace,
);
}
}
}
Reference Files#
backend/kobic_server/lib/src/feature/banner/exception/banner_exception.dart
backend/kobic_server/lib/src/feature/banner/constant/banner_constants.dart
backend/kobic_server/lib/src/feature/banner/validation/banner_validator.dart
backend/kobic_server/lib/src/common/constants/http_status_constants.dart
Checklist#
- Apply sealed class pattern
- Include statusCode in all exceptions
- Separate ErrorMessages into methods/constants
- Reference HttpStatusConstants from Constants
- Clear messages on Validator failure
- Apply ExceptionHandler.safeExecute() pattern
- Write KDoc comments