Development Guide
This guide covers how to build features inside CoreAPI. All development happens in one of two places — a domain package or a shared package — and everything is wired together in main.go.
Top-level Structure
CoreAPI/
├── domain/ ← feature domains (your work goes here)
├── pkg/ ← shared packages used across domains
├── public/ ← static files served by Fiber
├── internal/ ← platform-level db models (do not modify)
├── main.go ← wires everything together
└── setup.go ← wires the ticketing stack (johorzoo domains)
main.go and setup.go are the only files that call db.GetDB() or db.GetZooDB(). Domains never reach out for a database connection themselves — it is always passed in.
Domains
Each feature lives in its own package under domain/. A domain owns its models, handlers, routes, and config. Domains do not import each other.
domain/
├── media/ ← reference domain — new pattern ✅
├── distribution/ ← reference domain — new pattern ✅
├── playback/ ← new pattern ✅
├── advertising/ ← being migrated
├── catalogue/ ← being migrated
├── spatial/ ← flat (old pattern)
├── provisioning/ ← flat (old pattern)
└── ...
When building something new, follow media or distribution. Do not copy from flat domains like spatial or provisioning.
Domain folder structure
domain/your-domain/
├── models.go — GORM structs, TableName()
├── config.go — env vars, read once via ConfigFromEnv()
├── validation.go — input validation helpers (optional)
├── helper.go — domain utilities (optional)
├── handlers/
│ ├── handler.go — Handler struct + NewHandler()
│ └── handler_xxx.go — HTTP methods on Handler
└── routes/
├── wire.go — Setup() — the only function main.go calls
└── routes.go — registerRoutes() — internal
Shared Packages
Shared logic that is used across multiple domains lives in pkg/.
| Package | Purpose |
|---|---|
pkg/db |
Database connections — GetDB(), GetZooDB(), RegisterModels() |
pkg/middleware |
Protected(jwtService), HasRole(), HasAnyRole() |
pkg/response |
Standard response envelope — OK, Error, Paginated |
pkg/seed |
Dev seed data — one file per domain, called from Run() |
pkg/storage |
SeaweedFS/S3 client |
pkg/StreamerClient |
HTTP client for Castis Streamer nodes |
pkg/jwt |
JWT service used by middleware |
Key Rules
You develop inside a domain or pkg. Nothing else.
main.go wires your domain. You call your domain's Setup() from main.go and pass the database in. Your domain never fetches it.
Your domain's models.go drives the migration. Register your models in main.go via db.RegisterModels() before db.Init(). db.RunMigrationMain() picks them up on startup.
Seed data goes in pkg/seed. Add a file for your domain, add the seed call inside Run().
Middleware goes in routes/routes.go. Not in handlers. jwtService is passed through wire.go into registerRoutes.
How To
| Question | Page |
|---|---|
| How does AutoMigrate create my tables on startup? | Migrations |
| How do I seed initial data for my domain? | Seeding |
| How do I connect a new domain to CoreAPI? | Domain Setup |
| How do I init the database and assign it to my domain? | Database |
| How do I use middleware in my domain? | Middleware |
| What are the recommended patterns? | Patterns |