
Security News
Crates.io Users Targeted by Phishing Emails
The Rust Security Response WG is warning of phishing emails from rustfoundation.dev targeting crates.io users.
github.com/jkaninda/okapi
Okapi is a modern, minimalist HTTP web framework for Go, inspired by FastAPI's elegance. Designed for simplicity, performance, and developer happiness, it helps you build fast, scalable, and well-documented APIs with minimal boilerplate.
The framework is named after the okapi (/oʊˈkɑːpiː/), a rare and graceful mammal native to the rainforests of the northeastern Democratic Republic of the Congo. Just like its namesake, which resembles a blend of giraffe and zebra. Okapi blends simplicity and strength in a unique, powerful package.
✔ Intuitive & Expressive API – Clean, declarative syntax for effortless route and middleware definition.
✔ Automatic Request Binding – Seamlessly parse JSON, XML, form data, query params, headers, and path variables into structs.
✔ Built-in Auth & Security – Native support for JWT, Basic Auth, and custom middleware.
✔ Standard Library Compatibility - Integrates seamlessly with Go’s net/http standard library.
✔ Blazing Fast Routing – Optimized HTTP router with low overhead for high-performance applications.
✔ First-Class Documentation – OpenAPI 3.0 & Swagger UI integrated out of the box—auto-generate API docs with minimal effort.
✔ Dynamic Route Management – Easily enable or disable individual routes or groups, with automatic Swagger sync and no code commenting.
✔ Modern Tooling
✔ Developer Experience
Built for speed, simplicity, and real-world use—whether you're prototyping or running in production.
Ideal for:
Whether you're building your next startup, internal tools, or side projects—Okapi scales with you.
mkdir myapi && cd myapi
go mod init myapi
go get github.com/jkaninda/okapi@latest
Create a file named main.go
:
package main
import (
"github.com/jkaninda/okapi"
)
func main() {
o := okapi.Default()
o.Get("/", func(c okapi.Context) error {
return c.OK(okapi.M{"message": "Hello from Okapi Web Framework!","License":"MIT"})
})
// Start the server
if err := o.Start(); err != nil {
panic(err)
}
}
Run your server:
go run main.go
Visit http://localhost:8080
to see the response.
{
"License": "MIT",
"message": "Hello from Okapi Web Framework!"
}
package main
import (
"github.com/jkaninda/okapi"
"net/http"
)
type Response struct {
Success bool `json:"success"`
Message string `json:"message"`
Data Book `json:"data"`
}
type Book struct {
Name string `json:"name" form:"name" max:"50" required:"true" description:"Book name"`
Price int `json:"price" form:"price" query:"price" yaml:"price" required:"true" description:"Book price"`
}
type ErrorResponse struct {
Success bool `json:"success"`
Status int `json:"status"`
Details any `json:"details"`
}
func main() {
// Create a new Okapi instance with default config
o := okapi.Default()
o.Post("/books", func(c okapi.Context) error {
book := Book{}
err := c.Bind(&book)
if err != nil {
return c.ErrorBadRequest(ErrorResponse{Success: false, Status: http.StatusBadRequest, Details: err.Error()})
}
response := Response{
Success: true,
Message: "This is a simple HTTP POST",
Data: book,
}
return c.OK(response)
},
// OpenAPI Documentation
okapi.DocSummary("Create a new Book"), // Route Summary
okapi.DocRequestBody(Book{}), // Request body
okapi.DocResponse(Response{}), // Success Response body
okapi.DocResponse(http.StatusBadRequest, ErrorResponse{}), // Error response body
)
// Start the server
if err := o.Start(); err != nil {
panic(err)
}
}
Now go to http://localhost:8080/docs
to see the interactive API documentation generated by Okapi.
And now, go to http://localhost:8080/redoc
to see the Redoc documentation.
Okapi supports all standard HTTP methods:
o.Get("/books", getBooks)
o.Post("/books", createBook)
o.Get("/books/:id", getBook)
o.Put("/books/:id", updateBook)
o.Delete("/books/:id", deleteBook)
Route groups in Okapi allow you to organize your routes under a common path prefix, apply middleware selectively, and control group-level behaviors like deprecation or disabling. This feature makes it easy to manage API versioning, logical route separation, and access control.
404 Not Found
for all its routes.o := okapi.Default()
// Create the main API group
api := o.Group("/api")
// Versioned subgroups
v1 := api.Group("/v1").Deprecated() // Marked as deprecated
v2 := api.Group("/v2") // Active version
v3 := api.Group("v3", testMiddleware).Disable() // Disabled, returns 404
// Define routes
v1.Get("/books", getBooks)
v2.Get("/books", v2GetBooks)
v3.Get("/books", v3GetBooks) // Will not be accessible
// Admin subgroup with middleware
admin := api.Group("/admin", adminMiddleware)
admin.Get("/dashboard", getDashboard)
This structure improves route readability and maintainability, especially in larger APIs.
Okapi supports flexible and expressive route path patterns, including named parameters and wildcards:
o.Get("/books/{id}", getBook) // Named path parameter using curly braces
o.Get("/books/:id", getBook) // Named path parameter using colon prefix
o.Get("/*", getBook) // Catch-all wildcard (matches everything)
o.Get("/*any", getBook) // Catch-all with named parameter (name is ignored)
o.Get("/*path", getBook) // Catch-all with named parameter
Use whichever syntax feels most natural — Okapi normalizes both {}
and :
styles for named parameters and supports glob-style wildcards for flexible matching.
o.Get("/books/:id", func(c okapi.Context) error {
id := c.Param("id")
return c.String(http.StatusOK, id)
})
o.Get("/books", func(c okapi.Context) error {
name := c.Query("name")
return c.String(http.StatusOK, name)
})
multipart/form-data
)Handle standard form fields and file uploads:
o.Post("/books", func(c okapi.Context) error {
name := c.FormValue("name")
price := c.FormValue("price")
logo, err := c.FormFile("logo")
if err != nil {
return c.AbortBadRequest("Bad request", err)
}
file, err := logo.Open()
if err != nil {
return c.AbortBadRequest("Bad request", err)
}
defer file.Close()
// You can now read or save the uploaded file
return c.String(http.StatusOK, "File uploaded successfully")
})
Bind request data directly into a struct from multiple sources:
type Book struct {
ID int `json:"id" param:"id" query:"id" form:"id"`
Name string `json:"name" xml:"name" form:"name" min:"4" max:"50" required:"true"`
Price int `json:"price" form:"price" required:"true"`
Logo *multipart.FileHeader `form:"logo" required:"true"`
Content string `header:"Content-Type" json:"content-type" xml:"content-type" required:"true"`
// Supports both ?tags=a&tags=b and ?tags=a,b
Tags []string `form:"tags" query:"tags" default:"a,b"`
}
o.Post("/books", func(c okapi.Context) error {
book := &Book{}
if err := c.Bind(book); err != nil {
return c.ErrorBadRequest(err)
}
return c.JSON(http.StatusOK, book)
})
param
query
form
json
xml
header
description
- OpenAPI descriptionOkapi supports simple, declarative validation using struct tags.
Field Type | Tag | Meaning |
---|---|---|
string | min:"10" | Minimum length = 10 |
string | max:"50" | Maximum length = 50 |
number | min:"5" | Minimum value = 5 |
number | max:"100" | Maximum value = 100 |
any | default:"..." | Default value if empty |
any | required:"true" | Field must be provided |
auth := okapi.BasicAuth{
Username: "admin",
Password: "password",
Realm: "Restricted",
ContextKey: "user",// where to store the username e.g. "user", default(username)
}
// Global middleware
o.Use(auth.Middleware)
// Attach SingleRouteMiddleware to this route only, without affecting others
o.Get("/", SingleRouteMiddlewareHandler).Use(SingleRouteMiddleware)
// Group middleware
o.Get("/admin", adminHandler)
Okapi includes powerful and flexible JWT middleware to secure your routes with JSON Web Tokens. It supports multiple signing mechanisms, key sources, claim validation strategies, and OpenAPI integration.
SigningSecret
RSAKey
JwksUrl
(e.g., OIDC or Auth0)JwksFile
ClaimsExpression
or ValidateClaims
.WithBearerAuth()
ForwardClaims
jwtAuth := okapi.JWTAuth{
SigningSecret: []byte("supersecret"), // Shared secret for HS256
TokenLookup: "header:Authorization", // Token source: header, query, or cookie (default: header:Authorization)
ContextKey: "user", // Key under which claims are stored in context
}
jwtAuth := okapi.JWTAuth{
JwksUrl: "https://example.com/.well-known/jwks.json", // Remote JWKS URL
TokenLookup: "header:Authorization",
ContextKey: "user",
}
Use ClaimsExpression
to define rules for validating claims using simple expressions. This is ideal for access control based on roles, scopes, or other custom claim logic.
Equals(field, value)
Prefix(field, prefix)
Contains(field, val1, val2, ...)
OneOf(field, val1, val2, ...)
!
— NOT&&
— AND (evaluated before OR)||
— OR (evaluated after AND)Example:
jwtAuth := okapi.JWTAuth{
SigningSecret: []byte("supersecret"),
ClaimsExpression: "Equals(`email_verified`, `true`) && Equals(`user.role`, `admin`) && Contains(`tags`, `gold`, `silver`)",
TokenLookup: "header:Authorization",
ContextKey: "user",
ForwardClaims: map[string]string{
"email": "user.email",
"role": "user.role",
"name": "user.name",
},
}
ForwardClaims
lets you expose specific claims to your handlers via the request context. This keeps handlers decoupled from the full JWT while retaining useful information.
Supports dot notation for nested claims.
Example:
jwtAuth.ForwardClaims = map[string]string{
"email": "user.email",
"role": "user.role",
"name": "user.name",
}
Get these claims in your handler:
func whoAmIHandler(c okapi.Context) error {
email := c.GetString("email")
if email == "" {
return c.AbortUnauthorized("Unauthorized", fmt.Errorf("user not authenticated"))
}
slog.Info("Who am I am ", "email", email, "role", c.GetString("role"), "name", c.GetString("name"))
// Respond with the current user information
return c.JSON(http.StatusOK, M{
"email": email,
"role": c.GetString("role"),
"name": c.GetString("name"),
}, )
}
You can define your own ValidateClaims
function to fully control claim checks. Use this for advanced logic beyond what ClaimsExpression
supports.
You can combine this with ClaimsExpression
for more complex scenarios.
Example:
jwtAuth.ValidateClaims = func(c Context, claims jwt.Claims) error {
method := c.Request().Method
slog.Info("Request method,", "method", method)
mapClaims, ok := claims.(jwt.MapClaims)
if !ok {
return errors.New("invalid claims type")
}
if emailVerified, _ := mapClaims["email_verified"].(bool); !emailVerified {
return errors.New("email not verified")
}
if role, _ := mapClaims["role"].(string); role != "admin" {
return errors.New("unauthorized role")
}
return nil
}
The OnUnauthorized
handler lets you customize responses for failed JWT validations, including:
ClaimsExpression
or ValidateClaims
)Example Implementation:
auth := okapi.JWTAuth{
Audience: "okapi.example.com",
SigningSecret: SigningSecret,
// ... other configurations
OnUnauthorized: func(c okapi.Context) error {
// Return custom unauthorized response
return c.ErrorUnauthorized("Unauthorized")
},
}
Apply the JWT middleware to route groups or individual routes to require authentication.
// Apply middleware globally (optional)
o.Use(jwtAuth.Middleware)
admin := o.Group("/admin", jwtAuth.Middleware). // Protect /admin routes
WithBearerAuth() // Adds Bearer auth to OpenAPI docs
admin.Get("/users", adminGetUsersHandler) // Secured route
// Attach SingleRouteMiddleware to this route only, without affecting others
o.Get("/", SingleRouteMiddlewareHandler).Use(SingleRouteMiddleware)
cors := okapi.Cors{AllowedOrigins: []string{"http://localhost:8080", "https://example.com"}, AllowedHeaders: []string{}}
o := okapi.New(okapi.WithCors(cors))
o.Get("/", func(c okapi.Context) error {
return c.String(http.StatusOK, "Hello World!")
})
func customMiddleware(next okapi.HandlerFunc) okapi.HandlerFunc {
return func(c okapi.Context) error {
start := time.Now()
err := next(c)
log.Printf("Request took %v", time.Since(start))
return err
}
}
o.Use(customMiddleware)
o.UseMiddleware(func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
slog.Info("Hello Go standard HTTP middleware function")
handler.ServeHTTP(w, r)
})
})
Okapi provides automatic OpenAPI (Swagger) documentation generation with a built-in interactive UI. The documentation is dynamically generated from your route definitions, ensuring it always stays in sync with your API implementation.
okapi.Default()
Documentation is enabled by default and served at /docs
.o := okapi.Default() // Docs available at /docs
okapi.New()
with WithOpenAPIDocs()
If you initialize Okapi with okapi.New()
, documentation is disabled by default.
You can enable it with WithOpenAPIDocs()
, this approach also allows you to dynamically disable the documentation at runtime (e.g., in production) based on environment variables or configuration.o := okapi.New() // Disabled
if os.Getenv("ENABLE_DOCS") == "true" {
o.WithOpenAPIDocs()
}
You can customize the OpenAPI documentation by passing a configuration object to WithOpenAPIDocs()
.
o := okapi.New().WithOpenAPIDocs(
okapi.OpenAPI{
Title: "Example API",
Version: "1.0.0",
Contact: okapi.Contact{
Name: "API Support",
Email: "support@example.com",
},
},
)
You can define authentication mechanisms for your API, such as Basic Auth, Bearer tokens, and OAuth2 flows.
o.WithOpenAPIDocs(okapi.OpenAPI{
Title: "Okapi Web Framework Example",
Version: "1.0.0",
License: okapi.License{Name: "MIT"},
SecuritySchemes: okapi.SecuritySchemes{
{
Name: "basicAuth",
Type: "http",
Scheme: "basic",
},
{
Name: "bearerAuth",
Type: "http",
Scheme: "bearer",
BearerFormat: "JWT",
},
{
Name: "OAuth2",
Type: "oauth2",
Flows: &okapi.OAuthFlows{
AuthorizationCode: &okapi.OAuthFlow{
AuthorizationURL: "https://auth.example.com/authorize",
TokenURL: "https://auth.example.com/token",
Scopes: map[string]string{
"read": "Read access",
"write": "Write access",
},
},
},
},
},
})
You can apply security schemes to individual routes or entire route groups.
var bearerAuthSecurity = []map[string][]string{
{"bearerAuth": {}},
}
o.Get("/books", getBooksHandler).WithSecurity(bearerAuthSecurity...)
api := o.Group("/api", jwtMiddleware).WithSecurity(bearerAuthSecurity)
api.Get("/", apiHandler)
Tip: If you are defining routes using
RouteDefinition
, you can also set security directly using theSecurity
field.
Okapi provides two ways to attach OpenAPI documentation to your routes:
This approach uses individual okapi.Doc*
functions for each aspect of your route documentation. It’s concise and works well for simple routes.
o.Get("/books", getBooksHandler,
okapi.DocSummary("List all available books"),
okapi.DocTags("Books"),
okapi.DocQueryParam("author", "string", "Filter by author name", false),
okapi.DocQueryParam("limit", "int", "Maximum results to return (default 20)", false),
okapi.DocResponseHeader("X-Client-Id", "string", "Client ID of the request"),
okapi.DocResponse([]Book{}), // Response for OpenAPI docs, Shorthand for DocResponse(200, value)
okapi.DocResponse(400, ErrorResponse{}),// Response error for OpenAPI docs
okapi.DocResponse(401, ErrorResponse{}),// Response error for OpenAPI docs
)
okapi.Doc()
+ .Build()
For more complex or dynamic documentation setup, Okapi offers a fluent builder API.
Use okapi.Doc()
to begin building, chain options, and call .Build()
or .AsOption()
to finalize.
o.Post("/books", createBookHandler,
okapi.Doc().
Summary("Add a new book to inventory").
Tags("Books").
BearerAuth().
ResponseHeader("X-Client-Id", "string", "Client ID of the request").
RequestBody(BookRequest{}).
Response(201,Book{}).
Response(400,ErrorResponse{}).
Response(401,ErrorResponse{}).
Build(),
)
Method | Description |
---|---|
DocSummary() /Doc().Summary() | Short endpoint description |
DocTag()/DocTags() /Doc().Tags() | Groups related endpoints |
DocBearerAuth() | Enables Bearer token authentication |
DocRequestBody() /Doc().RequestBody() | Documents request body structure |
DocResponse() /Doc().Response() | Documents response structure |
DocPathParam() /Doc().PathParam() | Documents path parameters |
DocQueryParam() /Doc().QueryParam() | Documents query parameters |
DocHeader() / Doc().Header() | Documents header parameters |
DocResponseHeader() /Doc().ResponseHeader() | Documents response header |
DocDeprecated() /Doc().Deprecated() | Mark route deprecated |
Okapi automatically generates Swagger UI for all routes:
Okapi gives you flexible control over your API by allowing routes and route groups to be dynamically enabled or disabled. This is a clean and efficient alternative to commenting out code when you want to temporarily remove endpoints.
You can disable:
This behavior is reflected both in runtime responses and API documentation.
Type | HTTP Response | Swagger Docs | Affects Child Routes |
---|---|---|---|
Disabled Route | 404 Not Found | Hidden | N/A |
Disabled Group | 404 Not Found | Hidden | Yes — all nested |
404 Not Found
.Disable()
or .Enable()
app := okapi.Default()
// Create the root API group
api := app.Group("api")
// Define and disable v1 group
v1 := api.Group("v1").Disable() // All v1 routes return 404 and are hidden from docs
v1.Get("/", func(c okapi.Context) error {
return c.OK(okapi.M{"version": "v1"})
})
// Define active v2 group
v2 := api.Group("v2")
v2.Get("/", func(c okapi.Context) error {
return c.OK(okapi.M{"version": "v2"})
})
// Start the server
if err := app.Start(); err != nil {
panic(err)
}
Disabled Route:
404 Not Found
Disabled Group:
To re-enable any route or group, simply call the .Enable()
method or remove the .Disable()
call.
o.Renderer = okapi.RendererFunc(func(w io.Writer, name string, data interface{}, c okapi.Context) error {
tmpl, err := template.ParseFiles("templates/" + name + ".html")
if err != nil {
return err
}
return tmpl.ExecuteTemplate(w, name, data)
})
type Template struct {
templates *template.Template
}
func (t *Template) Render(w io.Writer, name string, data interface{}, c okapi.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
tmpl := &Template{
templates: template.Must(template.ParseGlob("templates/*.html")),
}
o.With().WithRenderer(&Template{templates: template.Must(template.ParseGlob("public/views/*.html"))})
// or
// o.With().WithRenderer(tmpl)
o.Get("/", func(c okapi.Context) error {
return c.Render(http.StatusOK, "welcome", okapi.M{
"title": "Welcome Page",
"message": "Hello from Okapi!",
})
})
Serve static assets and individual files:
// Serve a single file
o.Get("/favicon.ico", func(c okapi.Context) error {
c.ServeFile("public/favicon.ico")
return nil
})
// Serve an entire directory
o.Static("/static", "public/assets")
// Initialize TLS configuration for secure HTTPS connections
tls, err := okapi.LoadTLSConfig("path/to/cert.pem", "path/to/key.pem", "", false)
if err != nil {
panic(fmt.Sprintf("Failed to load TLS configuration: %v", err))
}
// Create a new Okapi instance with default config
// With OpenAPI enabled, /docs
o := okapi.Default()
// Use HTTPS
// o := okapi.New(okapi.WithTLS(tls))
// Configure a secondary HTTPS server listening on port 8443
// This creates both HTTP (8080) and HTTPS (8443) endpoints
o.With(okapi.WithTLSServer(":8443", tls))
// Register application routes and handlers
o.Get("/", func(c okapi.Context) error {
return c.JSON(http.StatusOK, okapi.M{
"message": "Welcome to Okapi!",
"status": "operational",
})
})
// Start the servers
// This will launch both HTTP and HTTPS listeners in separate goroutines
log.Println("Starting server on :8080 (HTTP) and :8443 (HTTPS)")
if err := o.Start(); err != nil {
panic(fmt.Sprintf("Server failed to start: %v", err))
}
}
Okapi provides a powerful and lightweight Context
object that wraps the HTTP request and response. It is designed to simplify handling HTTP requests by offering a clean and expressive API for accessing request data, binding parameters, sending responses, and managing errors.
The Context
is passed to all route handlers and supports:
*http.Request
and http.ResponseWriter
for low-level controlThis makes it easy to focus on business logic without worrying about low-level HTTP details.
Method | Description |
---|---|
Request() | The underlying *http.Request for accessing request data |
Response() | The underlying http.ResponseWriter for sending responses |
The context supports multiple binding mechanisms depending on content type and request source
Okapi provides a rich set of response methods to send various types of responses back to the client. These methods automatically set the appropriate HTTP status codes and content types.
Okapi provides a comprehensive error-handling system. You can return an error
directly from your route handler, and Okapi will format the response automatically.
Additionally, the Context
includes many helper methods to send standardized HTTP error responses with custom messages and optional wrapped errors.
These helpers provide consistency and reduce boilerplate when handling errors in your handlers or middleware.
Okapi provides a clean, declarative way to define and register routes. It supports all standard HTTP methods, including GET
, POST
, PUT
, DELETE
, PATCH
, and OPTIONS
.
You can define routes individually or register multiple routes at once using the okapi.RegisterRoutes
function and the RouteDefinition
struct, which is especially useful when organizing routes by controller or feature module.
RouteDefinition
To group and manage routes more effectively, you can define them as a slice of okapi.RouteDefinition
. This pattern is ideal for structuring routes in controllers or service layers.
type BookController struct{}
func (bc *BookController) GetBooks(c okapi.Context) error {
// Simulate fetching books from a database
return c.OK(okapi.M{"success": true, "message": "Books retrieved successfully"})
}
func (bc *BookController) CreateBook(c okapi.Context) error {
// Simulate creating a book in a database
return c.Created(okapi.M{
"success": true,
"message": "Book created successfully",
})
}
func (bc *BookController) Routes() []okapi.RouteDefinition {
apiGroup := &okapi.Group{Prefix: "/api"}
return []okapi.RouteDefinition{
{
Method: http.MethodGet,
Path: "/books",
Handler: bc.GetBooks,
Group: apiGroup,
},
{
Method: http.MethodPost,
Path: "/books",
Handler: bc.CreateBook,
Group: apiGroup,
Middlewares: []okapi.Middleware{customMiddleware}
Options: []okapi.RouteOption{
okapi.DocSummary("Create Book"), // OpenAPI documentation
},
Security: bearerAuthSecurity, // Apply Bearer Auth security scheme
},
}
}
You can register routes using one of the following approaches:
app := okapi.Default()
bookController := &BookController{}
// Method 1: Register directly to the app instance
app.Register(bookController.Routes()...)
// Using a route group
// apiGroup := app.Group("/api")
// apiGroup.Register(bookController.Routes()...)
// Method 2: Use the global helper to register with the target instance
okapi.RegisterRoutes(app, bookController.Routes())
Both methods achieve the same result, choose the one that best fits your project’s style.
Okapi integrates seamlessly with Go’s net/http
standard library, enabling you to:
http.Handler
middlewarehttp.HandlerFunc
handlersThis makes Okapi ideal for gradual adoption or hybrid use in existing Go projects.
Okapi’s UseMiddleware
bridges standard http.Handler
middleware into Okapi’s middleware system. This lets you reuse the wide ecosystem of community-built middleware—such as logging, metrics, tracing, compression, and more.
func (o *Okapi) UseMiddleware(middleware func(http.Handler) http.Handler)
o := okapi.Default()
// Add a custom version header to all responses
o.UseMiddleware(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Version", "v1.2.0")
next.ServeHTTP(w, r)
})
})
You can register any http.HandlerFunc
using HandleStd
, or use full http.Handler
instances via HandleHTTP
. These retain Okapi’s routing and middleware features while supporting familiar handler signatures.
func (o *Okapi) HandleStd(method, path string, handler http.HandlerFunc, opts ...RouteOption)
o := okapi.Default()
o.HandleStd("GET", "/greeting", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from Okapi!"))
})
Migrating an existing net/http
application? Okapi makes it painless.
You can mix Okapi and standard handlers in the same application:
// Okapi-style route
o.Handle("GET", "/okapi", func(c okapi.Context) error {
return c.OK(okapi.M{"status": "ok"})
})
// Standard library handler
o.HandleStd("GET", "/standard", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("standard response"))
})
http.HandlerFunc
: must manually call w.WriteHeader(...)
okapi.Handle
: can return an error or use helpers like c.JSON
, c.Text
, c.OK
, c.ErrorNotFound()
or c.AbortBadRequest()
Are you building a microservices architecture? Do you need a powerful yet lightweight API Gateway or a high-performance reverse proxy to secure and manage your services effortlessly?
Check out my other project — Goma Gateway.
Goma Gateway is a high-performance, declarative API Gateway built for modern microservices. It comes with a rich set of built-in middleware, including:
Protocol support: REST, GraphQL, gRPC, TCP, and UDP
Security: Automatic HTTPS via Let’s Encrypt or use your own TLS certificates
Whether you're managing internal APIs or exposing public endpoints, Goma Gateway helps you do it efficiently, securely, and with minimal complexity.
Contributions are welcome!
⭐ If you find Okapi useful, please consider giving it a star on GitHub!
This project is licensed under the MIT License. See the LICENSE file for details.
Made with ❤️ for the Go community
⭐ Star us on GitHub — it helps!
Copyright (c) 2025 Jonas Kaninda
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The Rust Security Response WG is warning of phishing emails from rustfoundation.dev targeting crates.io users.
Product
Socket now lets you customize pull request alert headers, helping security teams share clear guidance right in PRs to speed reviews and reduce back-and-forth.
Product
Socket's Rust support is moving to Beta: all users can scan Cargo projects and generate SBOMs, including Cargo.toml-only crates, with Rust-aware supply chain checks.