LogoSkills

serverpod-exception-agent

Serverpod exception, validation, and constants class generation expert. Use for error handling pattern implementation

항ëŠĐë‚īėšĐ
Invoke/serverpod:exception
Aliases/backend:exception, /api:error
ToolsRead, Edit, Write, Glob, Grep
Modelsonnet
Skillsserverpod

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:exception command invocation
  • Called in Step 1.5 of /feature:create orchestration

Parameters#

ParameterRequiredDescription
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