{"id":31554609,"url":"https://github.com/dumbdev/katal","last_synced_at":"2025-10-07T08:13:09.359Z","repository":{"id":317464828,"uuid":"1067548406","full_name":"dumbdev/katal","owner":"dumbdev","description":"A Laravel-inspired web framework for Bun with TypeScript. Build modern web applications with decorators, controllers, middleware, validation, and authentication.","archived":false,"fork":false,"pushed_at":"2025-10-01T03:40:01.000Z","size":55,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-01T05:32:45.945Z","etag":null,"topics":["bun","framework","laravel","middleware","nodejs","typescript","web"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dumbdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-01T02:45:13.000Z","updated_at":"2025-10-01T03:47:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"d3aaec8a-26e1-4893-93b9-2e8386a4db1c","html_url":"https://github.com/dumbdev/katal","commit_stats":null,"previous_names":["dumbdev/katal","dumbdev/contro"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/dumbdev/katal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dumbdev%2Fkatal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dumbdev%2Fkatal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dumbdev%2Fkatal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dumbdev%2Fkatal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dumbdev","download_url":"https://codeload.github.com/dumbdev/katal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dumbdev%2Fkatal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278547818,"owners_count":26004773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","framework","laravel","middleware","nodejs","typescript","web"],"created_at":"2025-10-04T21:11:12.359Z","updated_at":"2025-10-06T01:51:31.448Z","avatar_url":"https://github.com/dumbdev.png","language":"TypeScript","readme":"# Katal\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![npm version](https://badge.fury.io/js/katal.svg)](https://www.npmjs.com/package/katal)\n[![Bun](https://img.shields.io/badge/Bun-💖-white)](https://bun.sh/)\n\n\u003e A simple yet powerful web framework for Bun with TypeScript, inspired by Laravel and Express\n\n## 🚀 Features\n\n- 🚀 **Built for Bun** - Leverage Bun's native performance and TypeScript support\n- 🎯 **Controller-based** - One controller per route for better organization\n- 🔒 **JWT Authentication** - Out-of-the-box JWT support\n- 🛡️ **Middleware System** - Global and route-specific middleware support\n- ✅ **Request Validation** - Built-in validation with custom rules\n- 📦 **Dependency Injection** - Built-in IoC container\n- ⚡ **Performance** - Optimized for Bun's runtime\n- 🌐 **CORS** - Built-in CORS middleware\n- 🚦 **Rate Limiting** - Protect your APIs from abuse\n- 🔧 **Extensible** - Create custom base controllers and middleware\n- 📝 **Type-Safe** - Full TypeScript support\n- 🧪 **Testing** - Built with testability in mind\n\n## Installation\n\n```bash\nbun install katal\n```\n\n## 🚀 Quick Start\n\n### Installation\n\n```bash\n# Create a new project\nmkdir my-katal-app\ncd my-katal-app\nbun init -y\nbun add katal\n```\n\n### Basic Example\n\nCreate a simple API server with a few endpoints:\n\n```typescript\n// app.ts\nimport { Application, Controller } from \"katal\";\nimport type { RequestContext } from \"katal\";\n\nconst app = new Application({ port: 3000 });\nconst router = app.getRouter();\n\n// Health check endpoint\nclass HealthController extends Controller {\n    async handle() {\n        return this.success({\n            status: \"ok\",\n            timestamp: new Date().toISOString(),\n            uptime: process.uptime()\n        });\n    }\n}\n\n// Get all users\nclass GetUsersController extends Controller {\n    async handle() {\n        return this.success([\n            { id: 1, name: \"John Doe\", email: \"john@example.com\" },\n            { id: 2, name: \"Jane Smith\", email: \"jane@example.com\" },\n        ]);\n    }\n}\n\n// Create a new user\nclass CreateUserController extends Controller {\n    async handle(context: RequestContext) {\n        // In a real app, you would save to a database here\n        const user = {\n            id: Math.random().toString(36).substr(2, 9),\n            ...context.body,\n            createdAt: new Date().toISOString()\n        };\n        \n        return this.success(user, \"User created successfully\", 201);\n    }\n}\n\n// Register routes\nrouter.get(\"/health\", HealthController);\nrouter.get(\"/users\", GetUsersController);\nrouter.post(\"/users\", CreateUserController, {\n    validation: {\n        name: { \n            required: true, \n            type: \"string\", \n            minLength: 2,\n            maxLength: 100 \n        },\n        email: { \n            required: true, \n            type: \"email\" \n        },\n        age: {\n            type: \"number\",\n            min: 13,\n            max: 120\n        }\n    },\n    // Optional: Add middleware to this specific route\n    middleware: [\"log-request\"]\n});\n\n// Add global middleware (applied to all routes)\nimport { createCorsMiddleware } from \"katal\";\napp.use(createCorsMiddleware({ \n    origin: \"*\",\n    methods: [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"],\n    allowedHeaders: [\"Content-Type\", \"Authorization\"]\n}));\n\n// Start the server\napp.listen(() =\u003e {\n    console.log(`Server running on http://localhost:${app.port}`);    \n    console.log(`Environment: ${app.environment}`);\n});\n```\n\nRun your application:\n\n```bash\nbun run app.ts\n```\n\nTest the API:\n\n```bash\n# Health check\ncurl http://localhost:3000/health\n\n# Get all users\ncurl http://localhost:3000/users\n\n# Create a new user\ncurl -X POST http://localhost:3000/users \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Alice\",\"email\":\"alice@example.com\",\"age\":28}'\n```\n\n## 📚 Documentation\n\n### 🎛️ Controllers\n\nControllers are the heart of your Katal application. Each controller handles exactly one route and extends the base `Controller` class.\n\n#### Basic Controller Example\n\n```typescript\nimport { Controller } from 'katal';\nimport type { RequestContext } from 'katal';\n\nclass GetUserController extends Controller {\n    async handle(context: RequestContext) {\n        const { id } = context.params;\n        \n        // Access request data\n        const { query } = context;\n        \n        // Return success response\n        return this.success({\n            id,\n            name: 'John Doe',\n            email: 'john@example.com'\n        });\n    }\n}\n```\n\n#### Response Helpers\n\nKatal provides several response helpers in the base `Controller` class:\n\n```typescript\n// Success response with data (returns 200 OK)\nreturn this.success(data);\n\n// Success with custom message\nreturn this.success(data, 'Operation successful');\n\n// Error response with custom status code\nreturn this.error('User not found', 404);\n\n// Validation error with error details\nreturn this.validationError([{ field: 'email', message: 'Invalid email' }]);\n\n// Redirect to another URL\nreturn this.redirect('https://example.com');\n\n// Custom JSON response with status code\nreturn this.json({ custom: 'response' }, 201);\n\n// Plain text response\nreturn this.text('Hello, world!');\n\n// HTML response\nreturn this.html('\u003ch1\u003eHello, world!\u003c/h1\u003e');\n```\n\n#### Lifecycle Hooks\n\nControllers support these lifecycle hooks:\n\n```typescript\nclass ExampleController extends Controller {\n    // Called before handle()\n    async beforeHandle(): Promise\u003cResponse | null\u003e {\n        // Return null to continue\n        // Or return a Response to short-circuit\n        if (!this.isAuthenticated()) {\n            return this.error('Unauthorized', 401);\n        }\n        return null;\n    }\n\n    // Main handler\n    async handle(context: RequestContext) {\n        // Your route logic here\n        return this.success({ data: 'example' });\n    }\n\n    // Called after handle()\n    async afterHandle(response: Response): Promise\u003cResponse\u003e {\n        // Modify response if needed\n        response.headers.set('X-Custom-Header', 'value');\n        return response;\n    }\n}\n```\n\n### 🛡️ Middleware\n\nMiddleware in Katal allows you to process requests and responses. You can create middleware with `before` and `after` hooks that run around your route handlers.\n\n#### Middleware Interface\n\n```typescript\ninterface Middleware {\n    before?: (context: MiddlewareContext) =\u003e Promise\u003cResponse | void\u003e | Response | void;\n    after?: (context: MiddlewareContext) =\u003e Promise\u003cResponse\u003e | Response;\n}\n\ninterface MiddlewareContext {\n    request: Request;\n    response?: Response;\n    [key: string]: any; // For custom properties\n}\n```\n\n#### Built-in Middleware\n\nKatal comes with several useful middleware:\n\n```typescript\nimport { \n    createCorsMiddleware, \n    createRateLimitMiddleware,\n    createAuthMiddleware,\n    Auth\n} from 'katal';\n\n// CORS middleware\napp.use(createCorsMiddleware({\n    origin: '*',\n    methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],\n    allowedHeaders: ['Content-Type', 'Authorization'],\n    credentials: true\n}));\n\n// Rate limiting (100 requests per 15 minutes per IP)\napp.use(createRateLimitMiddleware({\n    windowMs: 15 * 60 * 1000,\n    maxRequests: 100\n}));\n\n// JWT Authentication\nconst auth = new Auth({\n    secret: 'your-secret-key-change-in-production',\n    expiresIn: '24h'\n});\n\n// Register auth service and middleware\napp.singleton('auth', () =\u003e auth);\n\n// Register auth middleware using the new registerMiddleware method\napp.registerMiddleware('auth', createAuthMiddleware(auth));\n```\n\n#### Custom Middleware Examples\n\n1. **Logging Middleware**:\n\n```typescript\nconst loggingMiddleware: Middleware = {\n    async before(context) {\n        const url = new URL(context.request.url);\n        console.log(`📝 [${new Date().toISOString()}] ${context.request.method} ${url.pathname}`);\n        (context.request as any)._startTime = Date.now();\n        return null; // Continue to next middleware/route\n    },\n    \n    async after(context) {\n        const duration = Date.now() - (context.request as any)._startTime;\n        console.log(`✅ [${new Date().toISOString()}] ${context.request.method} ${context.request.url} - ${context.response?.status} (${duration}ms)`);\n        return context.response!;\n    }\n};\n\n// Register globally\napp.use(loggingMiddleware);\n```\n\n2. **Request ID Middleware**:\n\n```typescript\nconst requestIdMiddleware: Middleware = {\n    async before(context) {\n        const requestId = Math.random().toString(36).substr(2, 9);\n        (context.request as any)._requestId = requestId;\n        return null;\n    },\n    \n    async after(context) {\n        const requestId = (context.request as any)._requestId;\n        const response = context.response!;\n        \n        // Add request ID to response headers\n        const headers = new Headers(response.headers);\n        headers.set('X-Request-ID', requestId);\n        \n        return new Response(response.body, {\n            status: response.status,\n            statusText: response.statusText,\n            headers\n        });\n    }\n};\n\n// Register globally\napp.use(requestIdMiddleware);\n```\n\n3. **Route-Specific Middleware**:\n\n```typescript\n// Create a named middleware\nconst adminAuthMiddleware: Middleware = {\n    async before(context) {\n        const authHeader = context.request.headers.get('Authorization');\n        if (!authHeader || !authHeader.includes('admin-token')) {\n            return new Response(\n                JSON.stringify({ error: 'Admin access required' }), \n                { status: 403, headers: { 'Content-Type': 'application/json' } }\n            );\n        }\n        return null;\n    }\n};\n\n// Register the named middleware\napp.singleton('adminAuth', () =\u003e adminAuthMiddleware);\n\n// Use in routes\nrouter.get('/admin/dashboard', AdminDashboardController, {\n    middleware: ['adminAuth']\n});\n```\n\n#### Middleware Execution Order\n\n1. Global middleware (in registration order)\n   - `before` hooks run first-to-last\n   - `after` hooks run last-to-first\n2. Route-specific middleware (in the order specified in route options)\n   - `before` hooks run after global middleware\n   - `after` hooks run before global middleware's `after` hooks\n3. Controller's `beforeHandle` method\n4. Controller's `handle` method\n5. Controller's `afterHandle` method\n```\n\n### 🔌 Error Handling\n\nKatal provides built-in error handling with the following features:\n\n1. **Global Error Handling**:\n   - All uncaught errors are caught and returned as JSON responses\n   - 500 status code for unexpected errors\n   - 404 for non-existent routes\n   - 422 for validation errors\n\n2. **Controller Error Helpers**:\n   ```typescript\n   // Return a 400 Bad Request error\n   return this.error('Invalid input', 400);\n   \n   // Return a validation error (422 Unprocessable Entity)\n   const errors = [\n       { field: 'email', message: 'Invalid email format' },\n       { field: 'password', message: 'Password too short' }\n   ];\n   return this.validationError(errors);\n   \n   // Return a 404 Not Found\n   return this.error('User not found', 404);\n   ```\n\n3. **Custom Error Handling**:\n   For custom error handling, you can extend the base Controller class:\n   \n   ```typescript\n   class BaseController extends Controller {\n       protected handleError(error: Error): Response {\n           // Log the error\n           console.error('Controller error:', error);\n           \n           // Handle specific error types\n           if (error instanceof DatabaseError) {\n               return this.error('Database error', 503);\n           }\n           \n           // Default error handling\n           return this.error('Something went wrong', 500);\n       }\n   }\n   ```\n\n4. **Validation Errors**:\n   When using the built-in validation, validation errors are automatically handled:\n   \n   ```typescript\n   // In your controller\n   const { valid, errors } = Validator.validate(data, validationSchema);\n   if (!valid) {\n       return this.validationError(errors);\n   }\n   ```\n   \n   This will return a response like:\n   ```json\n   {\n       \"success\": false,\n       \"message\": \"Validation failed\",\n       \"errors\": [\n           { \"field\": \"email\", \"message\": \"Invalid email format\" },\n           { \"field\": \"password\", \"message\": \"Password too short\" }\n       ]\n   }\n   ```\n```\n\n### 🏗️ Custom Base Controllers\n\nKatal allows you to create custom base controllers to encapsulate common functionality and reduce code duplication. Here are some practical examples:\n\n#### 1. Admin Controller\n\n```typescript\nabstract class AdminController extends Controller {\n    protected override async beforeHandle(): Promise\u003cResponse | null\u003e {\n        const authHeader = this.context.request.headers.get(\"Authorization\");\n        if (!authHeader || !authHeader.includes(\"admin-token\")) {\n            return this.error(\"Admin access required\", 403);\n        }\n        return null;\n    }\n\n    protected getAdminUser() {\n        return {\n            id: \"admin-1\",\n            name: \"Admin User\",\n            role: \"admin\",\n        };\n    }\n}\n\n// Usage\nclass AdminDashboardController extends AdminController {\n    async handle() {\n        const admin = this.getAdminUser();\n        return this.success({ admin, stats: { totalUsers: 1000 } });\n    }\n}\n```\n\n#### 2. API Controller with Standardized Responses\n\n```typescript\nabstract class ApiController extends Controller {\n    protected apiVersion = \"v1\";\n\n    protected override async afterHandle(response: any): Promise\u003cany\u003e {\n        if (response instanceof Response) return response;\n        \n        return this.json({\n            version: this.apiVersion,\n            timestamp: new Date().toISOString(),\n            data: response,\n        });\n    }\n}\n\n// Usage\nclass StatsController extends ApiController {\n    async handle() {\n        // Returns: { version: \"v1\", timestamp: \"...\", data: { ... } }\n        return { requests: 1000, users: 250 };\n    }\n}\n```\n\n#### 3. Authenticated Controller\n\n```typescript\nabstract class AuthenticatedController extends Controller {\n    protected user: any;\n\n    protected override async beforeHandle(): Promise\u003cResponse | null\u003e {\n        const authHeader = this.context.request.headers.get(\"Authorization\");\n        if (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n            return this.error(\"Authentication required\", 401);\n        }\n        \n        // In a real app, verify JWT token\n        this.user = await this.verifyToken(authHeader.split(\" \")[1]);\n        return null;\n    }\n\n    protected getCurrentUser() {\n        return this.user;\n    }\n}\n```\n\n#### 4. Cached Controller\n\n```typescript\nabstract class CachedController extends Controller {\n    private static cache = new Map\u003cstring, { data: any; timestamp: number }\u003e();\n    protected cacheDuration = 60000; // 1 minute\n\n    protected getCacheKey(): string {\n        return new URL(this.context.request.url).pathname;\n    }\n\n    protected override async beforeHandle(): Promise\u003cResponse | null\u003e {\n        const key = this.getCacheKey();\n        const cached = CachedController.cache.get(key);\n\n        if (cached \u0026\u0026 Date.now() - cached.timestamp \u003c this.cacheDuration) {\n            return this.success(cached.data);\n        }\n        return null;\n    }\n\n    protected override async afterHandle(response: any): Promise\u003cany\u003e {\n        if (response instanceof Response \u0026\u0026 response.status === 200) {\n            const key = this.getCacheKey();\n            const data = await response.clone().json();\n            CachedController.cache.set(key, {\n                data: data.data,\n                timestamp: Date.now(),\n            });\n        }\n        return response;\n    }\n}\n\n// Usage\nclass ProductsController extends CachedController {\n    async handle() {\n        // This will be cached for 1 minute\n        return this.success([\n            { id: 1, name: \"Product 1\" },\n            { id: 2, name: \"Product 2\" },\n        ]);\n    }\n}\n```\n\n#### 5. Logging Controller\n\n```typescript\nabstract class LoggingController extends Controller {\n    protected override async beforeHandle(): Promise\u003cResponse | null\u003e {\n        console.log(\n            `📝 [${new Date().toISOString()}] ${this.context.request.method} ${new URL(this.context.request.url).pathname}`\n        );\n        return null;\n    }\n\n    protected override async afterHandle(response: any): Promise\u003cany\u003e {\n        console.log(`✅ Request completed with status: ${response.status}`);\n        return response;\n    }\n}\n\n// Usage\nclass HealthController extends LoggingController {\n    async handle() {\n        return this.success({ status: \"ok\" });\n    }\n}\n```\n\n### 🌐 Routing\n\nKatal provides a flexible routing system that works alongside controllers. Routes are defined separately from controllers for better organization.\n\n#### Basic Routing\n\n```typescript\nimport { Application } from 'katal';\n\nconst app = new Application({ port: 3000 });\nconst router = app.getRouter();\n\n// Basic GET route\nrouter.get('/hello', HelloController);\n\n// Route with URL parameters\nrouter.get('/users/:id', GetUserController);\n\n// Route with query parameters\n// Example: /search?q=term\u0026page=1\nrouter.get('/search', SearchController);\n\n// All HTTP methods are supported\nrouter.post('/users', CreateUserController);\nrouter.put('/users/:id', UpdateUserController);\nrouter.delete('/users/:id', DeleteUserController);\nrouter.patch('/users/:id', PatchUserController);\n```\n\n#### Route Groups\n\nGroup related routes with common middleware or path prefixes:\n\n```typescript\n// Group with path prefix\nrouter.group('/api/v1', (router) =\u003e {\n    router.get('/users', GetUsersController);\n    router.post('/users', CreateUserController);\n    \n    // Nested groups\n    router.group('/admin', (router) =\u003e {\n        router.get('/dashboard', AdminDashboardController);\n        router.get('/stats', AdminStatsController);\n    }, {\n        middleware: ['auth', 'admin']\n    });\n});\n```\n\n### ✅ Validation\n\nKatal includes a powerful validation system that's easy to use and extend.\n\n#### Basic Validation\n\nIn Katal, request validation is handled using the `validateRequest` method in your controller. Here's how to implement validation:\n\n```typescript\nimport { Controller } from 'katal';\nimport type { RequestContext } from 'katal';\n\nclass RegisterController extends Controller {\n    // Define your validation schema as a class property\n    private readonly registerSchema = {\n        username: {\n            required: true,\n            type: 'string',\n            minLength: 3,\n            maxLength: 30,\n            pattern: /^[a-zA-Z0-9_]+$/  // Only alphanumeric and underscores\n        },\n        email: {\n            required: true,\n            type: 'email',  // Built-in email type\n            maxLength: 255\n        },\n        age: {\n            type: 'number',\n            min: 18,\n            max: 120\n        },\n        isAdmin: {\n            type: 'boolean'\n        },\n        website: {\n            type: 'url',  // Built-in URL type\n            required: false\n        },\n        role: {\n            type: 'string',\n            enum: ['user', 'editor', 'admin']\n        },\n        preferences: {\n            type: 'object',\n            required: true,\n            schema: {\n                theme: {\n                    type: 'string',\n                    enum: ['light', 'dark', 'system']\n                },\n                notifications: {\n                    type: 'boolean',\n                    required: true\n                },\n                language: {\n                    type: 'string',\n                    enum: ['en', 'es', 'fr', 'de']\n                }\n            }\n        },\n        tags: {\n            type: 'array',\n            min: 1,\n            max: 10,\n            items: 'string'\n        },\n        customField: {\n            type: 'string',\n            custom: (value: string) =\u003e {\n                // Custom validation function\n                // Return true if valid, false or error message if invalid\n                if (value.length % 2 === 0) {\n                    return true;\n                }\n                return 'Value must have an even number of characters';\n            }\n        }\n    };\n\n    async handle() {\n        // Validate the request\n        const validationResponse = this.validateRequest(this.context, this.registerSchema);\n        if (validationResponse) {\n            return validationResponse;\n        }\n\n        // If validation passes, continue with registration logic\n        const { username, email, password } = this.context.body;\n        \n        // Your registration logic here...\n        \n        return this.success({ userId: 123 }, 'Registration successful');\n    }\n}\n\n// In your routes file:\nrouter.post('/register', RegisterController);\n```\n\n#### Custom Validators\n\nYou can create custom validation rules using the `custom` validator function. The function should return `true` if the value is valid, or a string error message if invalid.\n\n```typescript\nimport { Controller } from 'katal';\n\nclass UserController extends Controller {\n    private readonly updatePasswordSchema = {\n        currentPassword: { \n            required: true, \n            type: 'string' \n        },\n        newPassword: {\n            required: true,\n            type: 'string',\n            minLength: 8,\n            custom: (value: string) =\u003e {\n                if (typeof value !== 'string') return 'Password must be a string';\n                if (value.length \u003c 8) return 'Password must be at least 8 characters';\n                if (!/[A-Z]/.test(value)) return 'Password must contain at least one uppercase letter';\n                if (!/[a-z]/.test(value)) return 'Password must contain at least one lowercase letter';\n                if (!/\\d/.test(value)) return 'Password must contain at least one number';\n                return true; // Validation passed\n            }\n        },\n        confirmPassword: {\n            required: true,\n            type: 'string',\n            custom: (value: string, data: any) =\u003e {\n                if (value !== data.newPassword) {\n                    return 'Passwords do not match';\n                }\n                return true;\n            }\n        }\n    };\n\n    async updatePassword() {\n        const validation = this.validateRequest(this.context, this.updatePasswordSchema);\n        if (validation) return validation;\n        \n        // Continue with password update logic\n        const { currentPassword, newPassword } = this.context.body;\n        // ...\n        \n        return this.success({ message: 'Password updated successfully' });\n    }\n}\n```\n\n#### Built-in Validators\n\nKatal provides several built-in validators through the `type` property:\n\n- `'string'` - Validates string values\n- `'number'` - Validates numeric values\n- `'boolean'` - Validates boolean values\n- `'email'` - Validates email format\n- `'url'` - Validates URL format\n- `'array'` - Validates arrays (use `items` to validate array elements)\n- `'object'` - Validates objects (use `schema` to validate object properties)\n\nExample using built-in validators:\n\n```typescript\nconst schema = {\n    username: {\n        type: 'string',\n        minLength: 3,\n        maxLength: 30,\n        pattern: /^[a-z0-9_]+$/\n    },\n    email: {\n        type: 'email',\n        required: true\n    },\n    age: {\n        type: 'number',\n        min: 18,\n        max: 120\n    },\n    roles: {\n        type: 'array',\n        items: {\n            type: 'string',\n            enum: ['user', 'editor', 'admin']\n        },\n        min: 1\n    },\n    metadata: {\n        type: 'object',\n        schema: {\n            lastLogin: { type: 'string' },\n            preferences: { type: 'object' }\n        }\n    }\n};\n```\n\n### 🔐 Authentication \u0026 Authorization\n\nKatal provides built-in JWT authentication through the `Auth` class. Here's how to implement it in your application.\n\n#### Setup Authentication\n\n```typescript\nimport { Auth, createAuthMiddleware } from 'katal';\n\n// Initialize auth with your secret key\nconst auth = new Auth({\n    secret: process.env.JWT_SECRET || 'your-secret-key',\n    expiresIn: '24h' // Token expiration\n});\n\n// Register auth service in the container\napp.singleton('auth', () =\u003e auth);\n\n// Register auth middleware\nconst middlewareManager = (app as any).middlewareManager;\nmiddlewareManager.register('auth', createAuthMiddleware(auth));\n```\n\n#### User Registration\n\n```typescript\nclass RegisterController extends Controller {\n    async handle(context: RequestContext) {\n        const { email, password, name } = context.body;\n        const auth = app.resolve\u003cAuth\u003e('auth');\n\n        // Hash the password\n        const hashedPassword = await auth.hashPassword(password);\n\n        // In a real app, save to database\n        const user = {\n            id: Math.random().toString(36).substr(2, 9),\n            email,\n            name,\n            password: hashedPassword,\n        };\n\n        // Generate JWT token\n        const token = await auth.generateToken(user);\n\n        return this.success({\n            user: { \n                id: user.id, \n                email: user.email, \n                name: user.name \n            },\n            token,\n        });\n    }\n}\n```\n\n#### User Login\n\n```typescript\nclass LoginController extends Controller {\n    async handle(context: RequestContext) {\n        const { email, password } = context.body;\n        const auth = app.resolve\u003cAuth\u003e('auth');\n\n        // In a real app, fetch user from database\n        const user = {\n            id: \"123\",\n            email: \"demo@example.com\",\n            name: \"Demo User\",\n            password: await auth.hashPassword(\"password123\"),\n        };\n\n        // Verify password\n        const isValid = await auth.verifyPassword(password, user.password);\n\n        if (!isValid) {\n            return this.error(\"Invalid credentials\", 401);\n        }\n\n        // Generate token\n        const token = await auth.generateToken(user);\n\n        return this.success({\n            user: { \n                id: user.id, \n                email: user.email, \n                name: user.name \n            },\n            token,\n        });\n    }\n}\n```\n\n#### Protecting Routes\n\n```typescript\n// Public route\nrouter.get('/public', PublicController);\n\n// Protected route - requires authentication\nrouter.get('/profile', ProfileController, {\n    middleware: ['auth']\n});\n\n// Protected route with role check\nclass AdminDashboardController extends Controller {\n    async handle() {\n        // Access the authenticated user from context\n        const user = this.context.user;\n        \n        // Check user role\n        if (user.role !== 'admin') {\n            return this.error('Admin access required', 403);\n        }\n        \n        return this.success({\n            message: 'Welcome to the admin dashboard',\n            stats: { /* ... */ }\n        });\n    }\n}\n\n// Register admin route\nrouter.get('/admin/dashboard', AdminDashboardController, {\n    middleware: ['auth']\n});\n```\n\n#### Auth Methods\n\nThe `Auth` class provides these methods:\n\n```typescript\n// Hash a password\nconst hashed = await auth.hashPassword('mypassword');\n\n// Verify a password\nconst isValid = await auth.verifyPassword('mypassword', hashed);\n\n// Generate JWT token\nconst token = await auth.generateToken({ id: '123', role: 'user' });\n\n// Verify token (done automatically by middleware)\nconst payload = await auth.verifyToken(token);\n```\n\n#### Token Usage\n\nInclude the token in the `Authorization` header:\n\n```http\nGET /protected-route\nAuthorization: Bearer your-jwt-token-here\n```\n\nOr as a query parameter:\n```\nGET /protected-route?token=your-jwt-token-here\n```\n\n#### Token Refresh\n\nFor implementing token refresh, create a refresh token endpoint:\n\n```typescript\nclass RefreshTokenController extends Controller {\n    async handle() {\n        const auth = app.resolve\u003cAuth\u003e('auth');\n        const user = this.context.user; // From current token\n        \n        // Generate new token with extended expiration\n        const newToken = await auth.generateToken({\n            id: user.id,\n            role: user.role\n        });\n        \n        return this.success({ token: newToken });\n    }\n}\n\n// Register refresh token route\nrouter.post('/auth/refresh', RefreshTokenController, {\n    middleware: ['auth'] // Requires a valid (but possibly expired) token\n});\n```\n    }\n}\n\n// Login\nclass LoginController extends Controller {\n    async handle({ body }) {\n        const { email, password } = body;\n        \n        // Find user by email\n        const user = await User.findOne({ email });\n        if (!user) {\n            return this.error('Invalid credentials', 401);\n        }\n        \n        // Verify password\n        const isValid = await Bun.password.verify(password, user.password);\n        if (!isValid) {\n            return this.error('Invalid credentials', 401);\n        }\n        \n        // Generate JWT token\n        const token = auth.signToken({ userId: user.id });\n        \n        // Set auth cookie\n        this.setCookie('auth_token', token, {\n            httpOnly: true,\n            secure: process.env.NODE_ENV === 'production',\n            sameSite: 'lax',\n            maxAge: 60 * 60 * 24 * 7 // 7 days\n        });\n        \n        return this.success({\n            user: {\n                id: user.id,\n                username: user.username,\n                email: user.email\n            }\n        }, 'Login successful');\n    }\n}\n```\n\n### 📝 Logging\n\nKatal provides a powerful and flexible logging system through the `LoggingIntegration` class, allowing you to log messages at different severity levels and route them to various destinations.\n\n#### Log Levels\n\nKatal supports four log levels:\n\n```typescript\nenum LogLevel {\n    DEBUG = \"debug\",  // Detailed debug information\n    INFO = \"info\",    // General application flow\n    WARN = \"warn\",    // Warnings that don't prevent execution\n    ERROR = \"error\"   // Errors that need attention\n}\n```\n\n#### Basic Usage\n\n```typescript\nimport { LoggingIntegration, LogLevel } from 'katal';\n\n// Create a logger instance\nconst logger = new LoggingIntegration()\n    .setMinLevel(LogLevel.INFO)  // Set minimum log level\n    .toConsole();                // Log to console\n\n// Log messages\nawait logger.debug('Debug message', { some: 'context' });\nawait logger.info('User logged in', { userId: 123, ip: '192.168.1.1' });\nawait logger.warn('API rate limit approaching', { endpoint: '/api/users' });\nawait logger.error('Database connection failed', new Error('Connection timeout'));\n```\n\n#### Log Destinations\n\nKatal comes with built-in destinations and allows custom ones:\n\n```typescript\n// Log to console with colors (default)\nlogger.toConsole();\n\n// Log to a file\nlogger.toFile('logs/app.log');\n\n// Custom destination example (e.g., to a remote service)\nconst remoteLogging = {\n    async write(entry) {\n        await fetch('https://logs.example.com/api', {\n            method: 'POST',\n            headers: { 'Content-Type': 'application/json' },\n            body: JSON.stringify(entry)\n        });\n    }\n};\n\nlogger.addDestination(remoteLogging);\n```\n\n#### Request Logging Middleware\n\nHere's how to create a request logging middleware:\n\n```typescript\nimport { LoggingIntegration } from 'katal';\n\nconst logger = new LoggingIntegration()\n    .setMinLevel(LogLevel.INFO)\n    .toConsole()\n    .toFile('logs/requests.log');\n\nconst requestLogger = {\n    async before(context) {\n        // Store start time for duration calculation\n        (context.request as any)._startTime = Date.now();\n        \n        // Log request start\n        await logger.info('Request started', {\n            method: context.request.method,\n            url: context.request.url,\n            ip: context.request.headers.get('x-forwarded-for') || \n                context.request.headers.get('x-real-ip') ||\n                context.request.headers.get('cf-connecting-ip') ||\n                'unknown'\n        });\n        \n        return null; // Continue to next middleware\n    },\n    \n    async after(context) {\n        const { request, response } = context;\n        const duration = Date.now() - (request as any)._startTime;\n        \n        // Log request completion\n        await logger.info('Request completed', {\n            method: request.method,\n            url: request.url,\n            status: response?.status,\n            duration: `${duration}ms`,\n            'response-size': response?.headers.get('content-length') || 'unknown'\n        });\n        \n        return response;\n    }\n};\n\n// Register middleware\napp.use(requestLogger);\n```\n\n#### Error Logging\n\nFor comprehensive error handling and logging:\n\n```typescript\n// Global error handler\napp.onError = async (error: Error, context: any) =\u003e {\n    await logger.error('Unhandled error', error, {\n        url: context?.request?.url,\n        method: context?.request?.method,\n        params: context?.params,\n        query: context?.query,\n        body: context?.body\n    });\n    \n    return new Response(\n        JSON.stringify({ \n            error: 'Internal Server Error',\n            requestId: context?.requestId \n        }), \n        { status: 500, headers: { 'Content-Type': 'application/json' } }\n    );\n};\n\n// In a controller\nclass UserController extends Controller {\n    async handle() {\n        try {\n            // Your code here\n        } catch (error) {\n            // Log the error with context\n            await logger.error('Failed to process user', error, {\n                userId: this.context.params.id,\n                action: 'updateProfile'\n            });\n            \n            // Return error response\n            return this.error('Failed to process request', 500);\n        }\n    }\n}\n```\n\n### 🚀 Deployment\n\nDeploying your Katal application is straightforward. Here's how to deploy to various platforms:\n\n#### 1. Local Development\n\n```bash\n# Install dependencies\nbun install\n\n# Start development server with hot reloading\nbun run dev\n```\n\n#### 2. Production Build\n\n```bash\n# Install production dependencies (with --production flag)\nbun install --production\n\n# Build your application (if needed)\nbun run build\n\n# Start production server\nbun start\n```\n\n#### 3. Environment Variables\n\nCreate a `.env` file in your project root:\n\n```env\nNODE_ENV=production\nPORT=3000\nJWT_SECRET=your-secure-secret\nDATABASE_URL=your-database-url\n```\n\n#### 4. Process Manager (PM2)\n\nInstall PM2 globally:\n\n```bash\nbun add -g pm2\n```\n\nCreate an ecosystem file `ecosystem.config.js`:\n\n```javascript\nmodule.exports = {\n    apps: [{\n        name: 'my-katal-app',\n        script: 'app.js',\n        instances: 'max',\n        exec_mode: 'cluster',\n        env: {\n            NODE_ENV: 'production',\n            PORT: 3000\n        }\n    }]\n};\n```\n\nStart your application:\n\n```bash\npm2 start ecosystem.config.js\n```\n\n#### 5. Docker Deployment\n\nCreate a `Dockerfile`:\n\n```dockerfile\n# Use the official Bun image\nFROM oven/bun:latest\n\n# Set working directory\nWORKDIR /app\n\n# Copy package.json and bun.lockb\nCOPY package.json bun.lockb ./\n\n# Install dependencies\nRUN bun install --production\n\n# Copy the rest of the application\nCOPY . .\n\n# Build the application (if needed)\n# RUN bun run build\n\n# Expose the port the app runs on\nEXPOSE 3000\n\n# Start the application\nCMD [\"bun\", \"start\"]\n```\n\nBuild and run the Docker container:\n\n```bash\n# Build the image\ndocker build -t my-katal-app .\n\n# Run the container\ndocker run -p 3000:3000 --env-file .env my-katal-app\n```\n\n#### 6. Deployment to Platforms\n\nKatal can be deployed to any platform that supports Node.js applications:\n\n- **Vercel**: Use the [Bun runtime](https://vercel.com/docs/runtimes#official-runtimes/node-js/bun-runtime)\n- **Railway**: Supports Bun out of the box\n- **Render.com**: Use a custom build command with Bun\n- **AWS Lambda**: Use the [AWS Lambda runtime for Bun](https://github.com/oven-sh/bun#aws-lambda)\n\n### 📦 Advanced Topics\n\n#### Dependency Injection\n\nKatal includes a simple yet powerful dependency injection container:\n\n```typescript\n// Register a service\napp.singleton('database', () =\u003e new Database(process.env.DATABASE_URL));\n\n// In a controller\nclass UserController extends Controller {\n    private database = this.app.make('database');\n    \n    async handle() {\n        const users = await this.database.query('SELECT * FROM users');\n        return this.success(users);\n    }\n}\n```\n\n### 📚 Examples\n\nCheck out the `examples/` directory for complete examples:\n\n- `examples/simple/` - Basic API example\n- `examples/custom-base-controllers/` - Extending base controllers\n- `examples/middleware-hooks/` - Middleware and hooks examples\n\n### 🤝 Contributing\n\nContributions are welcome! Please read our [contributing guide](CONTRIBUTING.md) to get started.\n\n### 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n### 🙏 Acknowledgments\n\n- Built with ❤️ and [Bun](https://bun.sh/)\n- Inspired by Laravel, Express, and other amazing frameworks\n- Thanks to all [contributors](https://github.com/dumbdev/katal/graphs/contributors) who helped shape this project\n\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdumbdev%2Fkatal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdumbdev%2Fkatal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdumbdev%2Fkatal/lists"}