{"id":39612849,"url":"https://github.com/karloscodes/cartridge","last_synced_at":"2026-03-08T15:02:46.198Z","repository":{"id":329732949,"uuid":"1033368044","full_name":"karloscodes/cartridge","owner":"karloscodes","description":"Ultra-clean Go web development with sublime developer experience","archived":false,"fork":false,"pushed_at":"2026-01-16T13:40:18.000Z","size":9543,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-17T04:07:53.112Z","etag":null,"topics":["go","golang","sqlite","web"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karloscodes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-08-06T17:49:50.000Z","updated_at":"2026-01-16T13:40:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/karloscodes/cartridge","commit_stats":null,"previous_names":["karloscodes/cartridge"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/karloscodes/cartridge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloscodes%2Fcartridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloscodes%2Fcartridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloscodes%2Fcartridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloscodes%2Fcartridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karloscodes","download_url":"https://codeload.github.com/karloscodes/cartridge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karloscodes%2Fcartridge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28534135,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"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":["go","golang","sqlite","web"],"created_at":"2026-01-18T08:12:10.711Z","updated_at":"2026-03-08T15:02:46.191Z","avatar_url":"https://github.com/karloscodes.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cartridge - Go Web Framework\n\nAn opinionated, batteries-included Go web framework built on [Fiber](https://gofiber.io) for server-side rendered applications.\n\n\u003e **Note**: This module is under active development and APIs may change.\n\n## Features\n\n- **SSR-first** - Server-side rendering with Go templates\n- **Multiple databases** - SQLite (with WAL) or PostgreSQL support\n- **Session management** - Secure cookie-based sessions with HMAC signing\n- **Background jobs** - Simple job dispatcher for async processing\n- **Structured logging** - JSON/text logging with log rotation\n- **Middleware** - Rate limiting, concurrency control, security headers\n\n## Quick Start\n\n```bash\ngo get github.com/karloscodes/cartridge\n```\n\n### Using NewSSRApp (Recommended for SQLite)\n\n`NewSSRApp` is the high-level factory for SSR applications with SQLite:\n\n```go\npackage main\n\nimport (\n    \"time\"\n    \"github.com/karloscodes/cartridge\"\n    \"myapp/web\"\n)\n\nfunc main() {\n    app, err := cartridge.NewSSRApp(\"myapp\",\n        cartridge.WithAssets(web.Templates, web.Static),\n        cartridge.WithSession(\"/login\"),\n        cartridge.WithRoutes(func(s *cartridge.Server) {\n            s.Get(\"/\", homeHandler)\n            s.Get(\"/users\", usersHandler)\n        }),\n    )\n    if err != nil {\n        panic(err)\n    }\n\n    if err := app.MigrateDatabase(myMigrator); err != nil {\n        panic(err)\n    }\n\n    if err := app.Run(); err != nil {\n        panic(err)\n    }\n}\n\nfunc homeHandler(ctx *cartridge.Context) error {\n    return ctx.Render(\"home\", fiber.Map{\"title\": \"Welcome\"})\n}\n```\n\n### Using NewInertiaApp (For Inertia.js SPAs)\n\n`NewInertiaApp` is for Inertia.js applications (React/Vue SPA with server-side routing). It handles Inertia dev mode, embedded assets, cross-origin APIs, and background workers:\n\n```go\npackage main\n\nimport (\n    \"github.com/karloscodes/cartridge\"\n    \"myapp/web\"\n)\n\nfunc main() {\n    app, err := cartridge.NewInertiaApp(\n        cartridge.InertiaWithConfig(cfg),\n        cartridge.InertiaWithStaticAssets(web.Assets()),\n        cartridge.InertiaWithRoutes(mountRoutes),\n        cartridge.InertiaWithWorker(jobsManager),\n        cartridge.InertiaWithSession(\"/login\"),\n        cartridge.InertiaWithCrossOriginAPI(),  // For analytics/public APIs\n    )\n    if err != nil {\n        panic(err)\n    }\n\n    if err := app.Run(); err != nil {\n        panic(err)\n    }\n}\n```\n\n**Key differences from NewSSRApp:**\n- Uses `inertia.SetDevMode(true)` in development (re-reads Vite manifest)\n- `InertiaWithCrossOriginAPI()` configures SecFetchSite for cross-origin requests\n- `InertiaWithWorker()` for custom BackgroundWorker implementations\n- No template engine (Inertia renders React/Vue components)\n\n### Using NewApplication (For Custom Setups)\n\n`NewApplication` is the lower-level constructor for full control over dependencies. Use this when you need PostgreSQL, a custom database manager, or non-SSR applications:\n\n```go\npackage main\n\nimport (\n    \"log/slog\"\n    \"github.com/karloscodes/cartridge\"\n    \"github.com/karloscodes/cartridge/database\"\n    \"github.com/karloscodes/cartridge/postgres\"\n)\n\nfunc main() {\n    // Create your own dependencies\n    logger := slog.Default()\n\n    // Use PostgreSQL\n    dbManager := database.NewManager(\n        postgres.NewDriver(),\n        \u0026database.Config{\n            DSN:          \"host=localhost user=app dbname=myapp\",\n            MaxOpenConns: 25,\n            MaxIdleConns: 5,\n            Postgres: database.PostgresOptions{\n                SSLMode:  \"disable\",\n                Timezone: \"UTC\",\n            },\n        },\n        logger,\n    )\n\n    // Create application with custom dependencies\n    app, err := cartridge.NewApplication(cartridge.ApplicationOptions{\n        Config:    myConfig,    // implements cartridge.Config interface\n        Logger:    logger,\n        DBManager: dbManager,   // implements cartridge.DBManager interface\n        RouteMountFunc: func(s *cartridge.Server) {\n            s.Get(\"/\", homeHandler)\n            s.Post(\"/api/items\", createItemHandler)\n        },\n    })\n    if err != nil {\n        panic(err)\n    }\n\n    if err := app.Run(); err != nil {\n        panic(err)\n    }\n}\n```\n\n## Embedded Assets\n\nBoth `NewSSRApp` and `NewInertiaApp` support embedded assets for single-binary deployment:\n\n```go\n// web/embed.go\npackage web\n\nimport (\n    \"embed\"\n    \"io/fs\"\n)\n\n//go:embed dist/assets\nvar assetsFS embed.FS\n\n// Assets returns embedded static assets (JS, CSS, images)\nfunc Assets() fs.FS {\n    sub, _ := fs.Sub(assetsFS, \"dist/assets\")\n    return sub\n}\n\n//go:embed templates\nvar templatesFS embed.FS\n\n// Templates returns embedded HTML templates (SSR only)\nfunc Templates() fs.FS {\n    return templatesFS\n}\n```\n\n**Behavior:**\n- **Production**: Assets served from embedded `fs.FS` (no external files needed)\n- **Development**: Assets served from disk for hot-reload with Vite\n\n## Database Support\n\nCartridge supports multiple databases through a pluggable driver interface.\n\n### SQLite (Default)\n\nSQLite is the default for `NewSSRApp`. It uses WAL mode and immediate transactions for optimal concurrency:\n\n```go\nimport \"github.com/karloscodes/cartridge/sqlite\"\n\ndbManager := sqlite.NewManager(sqlite.Config{\n    Path:         \"storage/app.db\",\n    MaxOpenConns: 1,              // SQLite works best with 1 connection\n    MaxIdleConns: 1,\n    BusyTimeout:  5000,           // ms\n    EnableWAL:    true,           // Write-Ahead Logging (default: true)\n    TxImmediate:  true,           // Immediate transaction locks (default: true)\n    Logger:       logger,\n})\n```\n\n### PostgreSQL\n\nFor PostgreSQL, use the generic database manager with the PostgreSQL driver:\n\n```go\nimport (\n    \"github.com/karloscodes/cartridge/database\"\n    \"github.com/karloscodes/cartridge/postgres\"\n)\n\ndbManager := database.NewManager(\n    postgres.NewDriver(),\n    \u0026database.Config{\n        DSN:          \"host=localhost port=5432 user=app password=secret dbname=myapp\",\n        MaxOpenConns: 25,\n        MaxIdleConns: 5,\n        Postgres: database.PostgresOptions{\n            SSLMode:    \"prefer\",    // disable, prefer, require\n            Timezone:   \"UTC\",\n            SearchPath: \"public\",    // optional schema\n        },\n    },\n    logger,\n)\n```\n\n### Custom Database Drivers\n\nImplement the `database.Driver` interface for other databases:\n\n```go\ntype Driver interface {\n    Name() string\n    Open(dsn string) gorm.Dialector\n    ConfigureDSN(dsn string, cfg *Config) string\n    AfterConnect(db *gorm.DB, cfg *Config, logger *slog.Logger) error\n    Close(db *gorm.DB, logger *slog.Logger) error\n    SupportsCheckpoint() bool\n    Checkpoint(db *gorm.DB, mode string) error\n}\n```\n\n## Configuration\n\nCartridge reads configuration from environment variables with the app name as prefix:\n\n```bash\nMYAPP_ENV=production          # development, production, test\nMYAPP_PORT=8080\nMYAPP_SESSION_SECRET=xxx      # Required in production\nMYAPP_LOG_LEVEL=info\nMYAPP_DATA_DIR=storage\n```\n\n## App Options\n\n### NewSSRApp Options\n\n```go\napp, err := cartridge.NewSSRApp(\"myapp\",\n    cartridge.WithConfig(cfg),              // Custom configuration\n    cartridge.WithAssets(tmpl, static),     // Embedded templates and static files\n    cartridge.WithTemplateFuncs(myFuncs),   // Custom template functions\n    cartridge.WithErrorHandler(handler),    // Custom error handler\n    cartridge.WithSession(\"/login\"),        // Enable session management\n    cartridge.WithJobs(2*time.Minute, p1),  // Background job processors\n    cartridge.WithRoutes(mountRoutes),      // Route mounting\n)\n```\n\n### NewInertiaApp Options\n\n```go\napp, err := cartridge.NewInertiaApp(\n    cartridge.InertiaWithConfig(cfg),           // Config (required, implements FactoryConfig)\n    cartridge.InertiaWithStaticAssets(fs),      // Embedded assets (production only)\n    cartridge.InertiaWithDBManager(dbMgr),      // Custom DB manager (optional)\n    cartridge.InertiaWithRoutes(mountRoutes),   // Route mounting\n    cartridge.InertiaWithWorker(worker),        // Custom BackgroundWorker\n    cartridge.InertiaWithJobs(interval, p1),    // Job processors with interval\n    cartridge.InertiaWithSession(\"/login\"),     // Enable session management\n    cartridge.InertiaWithCrossOriginAPI(),      // Allow cross-origin requests\n    cartridge.InertiaWithPageTitle(\"My App\"),   // HTML page title\n    cartridge.InertiaWithCatchAllRedirect(\"/\"), // SPA fallback redirect\n)\n```\n\n## Database Migrations\n\n```go\n// Create a migrator with your models\nmigrator := cartridge.NewAutoMigrator(\n    \u0026User{},\n    \u0026Post{},\n    \u0026Comment{},\n)\n\n// Run migrations (connects, migrates, checkpoints WAL for SQLite)\nif err := app.MigrateDatabase(migrator); err != nil {\n    panic(err)\n}\n```\n\n## Session Management\n\n```go\n// In your login handler\nfunc loginHandler(ctx *cartridge.Context) error {\n    // Validate credentials...\n\n    session := ctx.Ctx.Locals(\"session\").(*cartridge.SessionManager)\n    if err := session.SetSession(ctx.Ctx, userID); err != nil {\n        return err\n    }\n    return ctx.Redirect(\"/dashboard\")\n}\n\n// Protected routes use session middleware\nauthConfig := \u0026cartridge.RouteConfig{\n    CustomMiddleware: []fiber.Handler{session.Middleware()},\n}\ns.Get(\"/dashboard\", dashboardHandler, authConfig)\n```\n\n## Background Jobs\n\nJobs run on a fixed interval and process batches of work:\n\n```go\n// Implement the Processor interface\ntype EmailProcessor struct{}\n\nfunc (p *EmailProcessor) ProcessBatch(ctx *cartridge.JobContext) error {\n    ctx.Logger.Info(\"processing pending emails\")\n\n    var pending []Email\n    if err := ctx.DB.Where(\"sent_at IS NULL\").Find(\u0026pending).Error; err != nil {\n        return err\n    }\n\n    for _, email := range pending {\n        // Send email...\n        ctx.DB.Model(\u0026email).Update(\"sent_at\", time.Now())\n    }\n    return nil\n}\n\n// Register processors with interval\napp, _ := cartridge.NewSSRApp(\"myapp\",\n    cartridge.WithJobs(2*time.Minute, \u0026EmailProcessor{}, \u0026WebhookProcessor{}),\n)\n```\n\n## Interfaces\n\nCartridge uses interfaces for dependency injection, making it easy to swap implementations:\n\n```go\n// Config abstracts runtime configuration\ntype Config interface {\n    IsDevelopment() bool\n    IsProduction() bool\n    IsTest() bool\n    GetPort() string\n    GetPublicDirectory() string\n    GetAssetsPrefix() string\n}\n\n// DBManager abstracts database connection management\ntype DBManager interface {\n    GetConnection() *gorm.DB\n    Connect() (*gorm.DB, error)\n}\n```\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarloscodes%2Fcartridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarloscodes%2Fcartridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarloscodes%2Fcartridge/lists"}