{"id":29747489,"url":"https://github.com/jkaninda/okapi","last_synced_at":"2026-03-15T16:22:17.850Z","repository":{"id":296140878,"uuid":"992240090","full_name":"jkaninda/okapi","owner":"jkaninda","description":"Okapi – A lightweight, expressive, and minimalist Go web framework with built-in OpenAPI 3, Swagger UI, and powerful middleware support.","archived":false,"fork":false,"pushed_at":"2026-02-09T21:46:55.000Z","size":1684,"stargazers_count":81,"open_issues_count":1,"forks_count":14,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-09T23:56:05.129Z","etag":null,"topics":["api-rest","go","go-api","go-okapi","go-openapi-ui","go-server","go-swagger","golang","golang-server","okapi-api","restful-api"],"latest_commit_sha":null,"homepage":"http://okapi.jkaninda.dev/","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/jkaninda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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-05-28T20:54:20.000Z","updated_at":"2026-02-09T21:44:15.000Z","dependencies_parsed_at":"2025-06-12T21:35:15.872Z","dependency_job_id":"be7facad-177b-416c-90ce-0c3d60f522c7","html_url":"https://github.com/jkaninda/okapi","commit_stats":null,"previous_names":["jkaninda/okapi"],"tags_count":41,"template":false,"template_full_name":null,"purl":"pkg:github/jkaninda/okapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkaninda%2Fokapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkaninda%2Fokapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkaninda%2Fokapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkaninda%2Fokapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jkaninda","download_url":"https://codeload.github.com/jkaninda/okapi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkaninda%2Fokapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29438984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T05:24:35.651Z","status":"ssl_error","status_checked_at":"2026-02-14T05:24:34.830Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["api-rest","go","go-api","go-okapi","go-openapi-ui","go-server","go-swagger","golang","golang-server","okapi-api","restful-api"],"created_at":"2025-07-26T08:39:54.735Z","updated_at":"2026-02-14T07:03:44.685Z","avatar_url":"https://github.com/jkaninda.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Okapi\n\nA modern, minimalist HTTP web framework for Go inspired by FastAPI's elegant design philosophy.\n\n[![Tests](https://github.com/jkaninda/okapi/actions/workflows/tests.yml/badge.svg)](https://github.com/jkaninda/okapi/actions/workflows/tests.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/jkaninda/okapi)](https://goreportcard.com/report/github.com/jkaninda/okapi)\n[![Go Reference](https://pkg.go.dev/badge/github.com/jkaninda/okapi.svg)](https://pkg.go.dev/github.com/jkaninda/okapi)\n[![codecov](https://codecov.io/gh/jkaninda/okapi/branch/main/graph/badge.svg?token=JHTW49M1LF)](https://codecov.io/gh/jkaninda/okapi)\n[![GitHub Release](https://img.shields.io/github/v/release/jkaninda/okapi)](https://github.com/jkaninda/okapi/releases)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/jkaninda/okapi/main/logo.png\" width=\"150\" alt=\"Okapi logo\"\u003e\n\u003c/p\u003e\n\nNamed after the okapi, a rare and graceful mammal native to the rainforests of northeastern Democratic Republic of the Congo—Okapi blends simplicity and strength in a unique, powerful package.\n\n## Features\n\n- **Intuitive API Design** – Clean, declarative syntax for routes and middleware\n- **Automatic Request Binding** – Parse JSON, XML, forms, query params, headers, and path variables into structs\n- **Built-in Validation** – Struct tag-based validation with comprehensive error messages\n- **Auto-Generated OpenAPI Docs** – Swagger UI and ReDoc automatically synced with your code\n- **Runtime Documentation Control** – Enable/disable OpenAPI docs at runtime without redeployment\n- **Authentication Ready** – Native JWT, Basic Auth, and extensible middleware support\n- **Standard Library Compatible** – Works seamlessly with Go's `net/http`\n- **Dynamic Route Management** – Enable/disable routes at runtime without code changes\n- **Production Ready** – CORS, TLS, graceful shutdown, and comprehensive middleware\n\n## Installation\n\n```bash\nmkdir myapi \u0026\u0026 cd myapi\ngo mod init myapi\ngo get github.com/jkaninda/okapi@latest\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport \"github.com/jkaninda/okapi\"\n\nfunc main() {\n    o := okapi.Default()\n    \n    o.Get(\"/\", func(c *okapi.Context) error {\n        return c.OK(okapi.M{\n            \"message\": \"Hello from Okapi!\",\n        })\n    })\n\n\terr := o.Start()\n\tif err != nil {\n\t\tpanic(err) \n\t}\n}\n```\n\nRun with `go run main.go` and visit:\n- **API**: http://localhost:8080\n- **Docs**: http://localhost:8080/docs\n\n---\n\n## Request Binding \u0026 Validation\n\nOkapi provides multiple approaches to bind and validate incoming requests, from simple binding to fully typed handler signatures.\n\n### Validation Tags\n\nDefine validation rules directly on your structs:\n\n```go\ntype Book struct {\n    Name   string `json:\"name\" minLength:\"4\" maxLength:\"50\" required:\"true\" pattern:\"^[A-Za-z]+$\"`\n    Price  int    `json:\"price\" required:\"true\" min:\"5\" max:\"100\"`\n    Year   int    `json:\"year\" deprecated:\"true\"`\n    Status string `json:\"status\" enum:\"available,out_of_stock,discontinued\" default:\"available\"`\n}\n```\n\n### Method 1: Binding with `c.Bind()`\n\nThe simplest approach to bind and validate within your handler:\n\n```go\no.Post(\"/books\", func(c *okapi.Context) error {\n    var book Book\n    if err := c.Bind(\u0026book); err != nil {\n        return c.ErrorBadRequest(err)\n    }\n    return c.Created(book)\n})\n```\n\n### Method 2: Typed Input with `okapi.Handle()`\n\nAutomatic input binding with a typed handler signature:\n\n```go\no.Post(\"/books\", okapi.Handle(func(c *okapi.Context, book *Book) error {\n    book.ID = generateID()\n    return c.Created(book)\n}),\n    okapi.DocRequestBody(\u0026Book{}),\n    okapi.DocResponse(\u0026Book{}),\n)\n```\n\n### Method 3: Shorthand with `okapi.H()`\n\nA concise version for simple input validation:\n\n```go\ntype BookDetailInput struct {\n    ID int `path:\"id\"`\n}\n\no.Get(\"/books/{id:int}\", okapi.H(func(c *okapi.Context, input *BookDetailInput) error {\n    book := findBookByID(input.ID)\n    if book == nil {\n        return c.AbortNotFound(\"Book not found\")\n    }\n    return c.OK(book)\n}))\n```\n\n### Method 4: Input \u0026 Output with `okapi.HandleIO()`\n\nDefine both input and output structs separately for complex operations:\n\n```go\ntype BookEditInput struct {\n    ID   int  `path:\"id\" required:\"true\"`\n    Body Book `json:\"body\"`\n}\n\ntype BookOutput struct {\n    Status int\n    Body   Book\n}\n\no.Put(\"/books/{id:int}\", okapi.HandleIO(func(c *okapi.Context, input *BookEditInput) (*BookOutput, error) {\n    book := updateBook(input.ID, input.Body)\n    if book == nil {\n        return nil, c.AbortNotFound(\"Book not found\")\n    }\n    return \u0026BookOutput{Body: *book}, nil\n})).WithIO(\u0026BookEditInput{}, \u0026BookOutput{})\n```\n\n### Method 5: Output Only with `okapi.HandleO()`\n\nWhen you only need a structured output without specific input validation:\n\n```go\ntype BooksResponse struct {\n    Body []Book `json:\"books\"`\n}\n\no.Get(\"/books\", okapi.HandleO(func(c *okapi.Context) (*BooksResponse, error) {\n    return \u0026BooksResponse{Body: getAllBooks()}, nil\n})).WithOutput(\u0026BooksResponse{})\n```\n\n---\n\n## Advanced Request/Response Patterns\n\nSeparate payload from metadata using the `Body` field pattern:\n\n```go\ntype BookRequest struct {\n    Body   Book   `json:\"body\"`              // Request payload\n    ID     int    `param:\"id\" query:\"id\"`    // Path or query parameter\n    APIKey string `header:\"X-API-Key\" required:\"true\"` // Header\n}\n\ntype BookResponse struct {\n    Status    int    // HTTP status code\n    Body      Book   // Response payload\n    RequestID string `header:\"X-Request-ID\"` // Response header\n}\n\n\no.Post(\"/books\", func(c *okapi.Context) error {\n    var req BookRequest\n    if err := c.Bind(\u0026req); err != nil {\n        return c.ErrorBadRequest(err)\n    }\n    \n    res := \u0026BookResponse{\n        Status:    201,\n        RequestID: uuid.New().String(),\n        Body:      req.Body,\n    }\n    return c.Respond(res) // Automatically sets status, headers, and body\n},\n    okapi.Request(\u0026BookRequest{}),\n    okapi.Response(BookResponse{}),\n)\n```\n\n---\n\n## Route Groups \u0026 Middleware\n\n```go\napi := o.Group(\"/api\")\n\n// Versioned API groups\nv1 := api.Group(\"/v1\", authMiddleware).Deprecated()\nv2 := api.Group(\"/v2\")\n\nv1.Get(\"/books\", getBooks)\nv2.Get(\"/books\", v2GetBooks)\n\n// Disable routes at runtime\nv2.Get(\"/experimental\", experimentalHandler).Disable()\n\n// Apply middleware to individual routes\nv2.Get(\"/books/{id}\", getBookByID).Use(cacheMiddleware)\n\n// Protected admin routes\nadmin := api.Group(\"/admin\", adminMiddleware)\nadmin.Get(\"/dashboard\", getDashboard)\n```\n\n---\n\n## Declarative Route Definition\n\nIdeal for controller or service-based architectures:\n\n```go\ntype BookService struct{}\n\nfunc (s *BookService) Routes() []okapi.RouteDefinition {\n    apiGroup := \u0026okapi.Group{Prefix: \"/api\"}\n    \n    return []okapi.RouteDefinition{\n        {\n            Method:      http.MethodGet,\n            Path:        \"/books\",\n            Handler:     s.List,\n            Group:       apiGroup,\n            Summary:     \"List all books\",\n            Response:    \u0026BooksResponse{},\n        },\n        {\n            Method:      http.MethodPost,\n            Path:        \"/books\",\n            Handler:     s.Create,\n            Group:       apiGroup,\n            Middlewares: []okapi.Middleware{authMiddleware},\n            Security:    bearerAuthSecurity,\n            Options: []okapi.RouteOption{\n                okapi.DocSummary(\"Create a book\"),\n                okapi.DocRequestBody(\u0026Book{}),\n                okapi.DocResponse(\u0026Book{}),\n            },\n        },\n    }\n}\n\n// Register routes\napp := okapi.Default()\nbookService := \u0026BookService{}\napp.Register(bookService.Routes()...)\n```\n\n---\n\n## Authentication\n\n### JWT Authentication\n\n```go\njwtAuth := okapi.JWTAuth{\n    SigningSecret:    []byte(\"your-secret-key\"),\n    ClaimsExpression: \"Equals(`email_verified`, `true`)\",\n    TokenLookup:      \"header:Authorization\",\n    ContextKey:       \"user\",\n}\n\nprotected := o.Group(\"/api\", jwtAuth.Middleware).WithBearerAuth()\nprotected.Get(\"/profile\", getProfile)\n```\n\n### Basic Authentication\n\n```go\nbasicAuth := okapi.BasicAuth{\n    Username: \"admin\",\n    Password: \"secure-password\",\n}\n\nadmin := o.Group(\"/admin\", basicAuth.Middleware)\nadmin.Get(\"/dashboard\", getDashboard)\n```\n\n---\n\n## Template Rendering\n\n```go\nfunc main() {\n    tmpl, _ := okapi.NewTemplateFromDirectory(\"views\", \".html\")\n    o := okapi.Default().WithRenderer(tmpl)\n    \n    o.Get(\"/\", func(c *okapi.Context) error {\n        return c.Render(http.StatusOK, \"home\", okapi.M{\n            \"title\":   \"Welcome\",\n            \"message\": \"Hello, World!\",\n        })\n    })\n    \n    o.Start()\n}\n```\n\n### Embedded Templates\n\n```go\n//go:embed views/*\nvar Views embed.FS\n\nfunc main() {\n    app := okapi.New()\n    app.WithRendererFromFS(Views, \"views/*.html\")\n    app.StaticFS(\"/assets\", http.FS(must(fs.Sub(Views, \"views/assets\"))))\n    app.Start()\n}\n```\n\n---\n\n## Testing\n\n```go\nimport \"github.com/jkaninda/okapi/okapitest\"\n\nfunc TestGetBooks(t *testing.T) {\n    server := okapi.NewTestServer(t)\n    server.Get(\"/books\", GetBooksHandler)\n    \n    okapitest.GET(t, server.BaseURL+\"/books\").\n        ExpectStatusOK().\n        ExpectBodyContains(\"Go Programming\").\n        ExpectHeader(\"Content-Type\", \"application/json\")\n}\n```\n\n---\n\n## CLI Integration\n\n```go\nimport \"github.com/jkaninda/okapi/okapicli\"\n\nfunc main() {\n    o := okapi.Default()\n    \n    cli := okapicli.New(o, \"My API\").\n        String(\"config\", \"c\", \"config.yaml\", \"Config file\").\n        Int(\"port\", \"p\", 8000, \"Server port\").\n        Bool(\"debug\", \"d\", false, \"Debug mode\")\n    \n    cli.Parse()\n    o.WithPort(cli.GetInt(\"port\"))\n    \n    // ... register routes ...\n    \n    cli.Run()\n}\n```\n\n---\n\n---\n\n## OpenAPI Documentation\n\nOkapi automatically generates interactive API documentation with multiple approaches to document your routes.\n\n### Enabling Documentation\n\n**With `okapi.Default()`** – Documentation is enabled by default at `/docs` and `/redoc`.\n\n**With `okapi.New()`** – Documentation is disabled by default. Enable it conditionally:\n\n```go\no := okapi.New()\n\nif os.Getenv(\"ENABLE_DOCS\") == \"true\" {\n    o.WithOpenAPIDocs()\n}\n```\n\n### Documenting Routes\n\n#### Composable Functions\n\nSimple and readable for most routes:\n\n```go\no.Get(\"/books\", getBooksHandler,\n    okapi.DocSummary(\"List all available books\"),\n    okapi.DocTags(\"Books\"),\n    okapi.DocQueryParam(\"author\", \"string\", \"Filter by author name\", false),\n    okapi.DocQueryParam(\"limit\", \"int\", \"Maximum results to return\", false),\n    okapi.DocResponseHeader(\"X-Client-Id\", \"string\", \"Client ID\"),\n    okapi.DocResponse([]Book{}),\n    okapi.DocResponse(400, ErrorResponse{}),\n)\n```\n\n#### Fluent Builder\n\nFor complex or dynamic documentation needs:\n\n```go\no.Post(\"/books\", createBookHandler,\n    okapi.Doc().\n        Summary(\"Add a new book to the inventory\").\n        Tags(\"Books\").\n        BearerAuth().\n        ResponseHeader(\"X-Client-Id\", \"string\", \"Client ID\").\n        RequestBody(BookRequest{}).\n        Response(201, Book{}).\n        Response(400, ErrorResponse{}).\n        Build(),\n)\n```\n\n#### Struct-Based with Body Field\n\nDefine request/response metadata directly in structs:\n\n```go\ntype BookRequest struct {\n    Body struct {\n        Name  string `json:\"name\" minLength:\"4\" maxLength:\"50\" required:\"true\"`\n        Price int    `json:\"price\" required:\"true\"`\n    } `json:\"body\"`\n    ID     int    `param:\"id\" query:\"id\"`\n    APIKey string `header:\"X-API-Key\" required:\"true\"`\n}\n\no.Post(\"/books\", createBookHandler,\n    okapi.Request(\u0026BookRequest{}),\n    okapi.Response(\u0026BookResponse{}),\n)\n```\n\n#### Fluent Route Methods\n\nChain documentation directly on route definitions:\n\n```go\no.Post(\"/books\", handler).WithIO(\u0026BookRequest{}, \u0026BookResponse{})  // Both request \u0026 response\no.Post(\"/books\", handler).WithInput(\u0026BookRequest{})                 // Request only\no.Get(\"/books\", handler).WithOutput(\u0026BooksResponse{})               // Response only\n```\n\nSee the full guide at **[okapi.jkaninda.dev/features/openapi](https://okapi.jkaninda.dev/features/openapi.html)**\n\n### Generated Documentation\n\n|                               Swagger UI (`/docs`)                               |                             ReDoc (`/redoc`)                              |\n|:--------------------------------------------------------------------------------:|:-------------------------------------------------------------------------:|\n| ![Swagger UI](https://raw.githubusercontent.com/jkaninda/okapi/main/swagger.png) | ![ReDoc](https://raw.githubusercontent.com/jkaninda/okapi/main/redoc.png) |\n\n---\n\n## Documentation\n\nFull documentation available at **[okapi.jkaninda.dev](https://okapi.jkaninda.dev)**\n\nTopics covered: Routing, Request Binding, Validation, Responses, Middleware, Authentication, OpenAPI, Testing, TLS, CORS, Graceful Shutdown, and more.\n\n---\n\n## Related Projects\n\nBuilding microservices? \nCheck out **[Goma Gateway](https://github.com/jkaninda/goma-gateway)** a high-performance API Gateway with authentication, rate limiting, load balancing, and support for REST, GraphQL, gRPC, TCP, and UDP.\n\n## Okapi vs Huma\n\nBoth **[Okapi](https://github.com/jkaninda/okapi)** and **[Huma](https://github.com/danielgtaylor/huma)** aim to improve developer experience in Go APIs with strong typing and OpenAPI integration. The key difference is **philosophy**: Okapi is a *batteries-included web framework*, while Huma is an *API layer designed to sit on top of existing routers*.\n\n| Feature / Aspect             | **Okapi**                                                              | **Huma**                                                       |\n|------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------|\n| **Positioning**              | Full web framework                                                     | API framework built on top of existing routers                 |\n| **Router**                   | Built-in high-performance router                                       | Uses external routers (Chi, httprouter, Fiber, etc.)           |\n| **OpenAPI Generation**       | Native, framework-level (Swagger UI \u0026 Redoc included)                  | Native, schema-first API design                                |\n| **Request Binding**          | Unified binder for JSON, XML, forms, query, headers, path params       | Struct tags + resolver pattern for headers, query, path params |\n| **Validation**               | Tag-based (min, max, enum, required, default, pattern, etc.)           | Included                                                       |\n| **Response Modeling**        | Output structs with `Body` pattern; headers \u0026 status via struct fields | Strongly typed response models with similar patterns           |\n| **Middleware**               | Built-in + custom middleware, groups, per-route middleware             | Router middleware + Huma-specific middleware and transformers  |\n| **Authentication**           | Built-in JWT, Basic Auth, security schemes for OpenAPI                 | Security schemes via OpenAPI; middleware via router            |\n| **Dynamic Route Management** | Enable/disable routes \u0026 groups at runtime                              | Not a core feature                                             |\n| **Templating / HTML**        | Built-in rendering (HTML templates, static files)                      | API-focused; not intended for HTML apps                        |\n| **CLI Integration**          | Built-in CLI support (flags, env config)                               | Included                                                       |\n| **Testing Utilities**        | Built-in test server and fluent HTTP assertions                        | Relies on standard Go testing tools                            |\n| **Learning Curve**           | Very approachable for Go web developers                                | Slightly steeper (requires OpenAPI-first mental model)         |\n| **Use Case Fit**             | Full web apps, APIs, gateways, microservices                           | Pure API services, schema-first API design                     |\n| **Philosophy**               | \"FastAPI-like DX for Go, batteries included\"                           | \"OpenAPI-first typed APIs on top of your router of choice\"     |\n\n\n### Quick Comparison\n\n**Okapi** — define a route with built-in validation and OpenAPI metadata:\n\n```go\napp:=okapi.Default()\napp.Register(okapi.RouteDefinition{\n     Method:      http.MethodPost,\n     Path:        \"/users\",\n     Handler:     createUser,\n     OperationId: \"create-user\",\n     Summary:     \"Create a new user\", \n     Tags: []string{\"users\"},\n     Request: \u0026UserRequest{},\n     Response:    \u0026User{},\n})\n```\n\n**Huma** — similar concept, different style:\n\n```go\nhuma.Register(api, huma.Operation{\n    OperationID: \"create-user\",\n    Method:      http.MethodPost,\n    Path:        \"/users\",\n    Summary:     \"Create a new user\",\n    Tags:        []string{\"Users\"},\n}, createUser)\n```\n\nBoth approaches generate OpenAPI documentation automatically.\n\n---\n\n### When to Choose Which?\n\n#### Choose Okapi if you want:\n\n- A **batteries-included web framework** with routing, middleware, auth, OpenAPI, templates, and CLI in one cohesive package\n- **FastAPI-like developer experience** that feels idiomatic in Go\n- **Dynamic route control** — enable or disable routes and groups at runtime\n- To build APIs **and** serve HTML pages or static assets from the same application\n\n#### Choose Huma if you want:\n\n- A **schema-first, OpenAPI-driven API layer** where the spec drives your implementation\n- To **keep using your existing router** (Chi, Fiber, Echo, etc.) without adopting a new framework\n- **Strict typed request/response contracts** as your primary design model\n- A **minimal, API-only stack** without broader web framework concerns\n\n---\n\n### Community \u0026 Maturity\n\n- **Huma**: More established with a larger community and extensive production usage\n- **Okapi**: Newer and rapidly evolving, with a smaller but growing community\n\nBoth are actively maintained. Choose based on your architectural preferences and project needs rather than stability concerns alone.\n\n\u003e **Note**: If you're already using Huma with Chi or another router and it's working well for you, there's no urgent reason to switch. Okapi is ideal for new projects or when you want a more integrated, batteries-included framework experience.\n\n\n---\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n---\n\n## Support\n\n- **Documentation:** [okapi.jkaninda.dev](https://okapi.jkaninda.dev)\n- **Issues:** [GitHub Issues](https://github.com/jkaninda/okapi/issues)\n- **Discussions:** [GitHub Discussions](https://github.com/jkaninda/okapi/discussions)\n- **LinkedIn:** [Jonas Kaninda](https://www.linkedin.com/in/jkaninda/)\n---\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**Made with ❤️ for the Go community**\n\n⭐ **Star us on GitHub** — it motivates us to keep improving!\n\nCopyright © 2025 Jonas Kaninda\n\n\u003c/div\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkaninda%2Fokapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjkaninda%2Fokapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkaninda%2Fokapi/lists"}