AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Route Definition

skill-jkaninda-okapi-skills-route-definition · by jkaninda

A Claude skill from jkaninda/okapi-skills.

No reviews yet
0 installs
23 views
0.0% view→install

Install

$ agentstack add skill-jkaninda-okapi-skills-route-definition

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-jkaninda-okapi-skills-route-definition)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Route Definition? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Okapi Route Definition Skills

Okapi's RouteDefinition is a declarative alternative to imperative route registration (.Get(), .Post(), etc.). Instead of chaining method calls, you describe each route as a struct and register it with app.Register(). This keeps route metadata — method, path, handler, documentation, middleware — in one place, making routes easier to read, move between files, and generate programmatically.

RouteDefinition Struct

| Field | Type | Description | |---------------|-------------------------------|--------------------------------------------------------------| | Method | string | HTTP method (http.MethodGet, http.MethodPost, etc.) | | Path | string | Route path relative to its group (e.g. "/books/{id:int}") | | Handler | okapi.HandlerFunc | Handler function — use okapi.H() for generic input binding | | Group | *okapi.Group | Group this route belongs to (prefix, middleware, tags) | | OperationId | string | OpenAPI operation ID | | Summary | string | Short description for OpenAPI docs | | Description | string | Detailed description for OpenAPI docs | | Tags | []string | OpenAPI tags (overrides group tags if set) | | Request | any | Request body schema for OpenAPI docs | | Response | any | Success response schema (defaults to 200) | | Security | []map[string][]string | OpenAPI security requirements | | Options | []okapi.RouteOption | Additional route options (error responses, path params, etc.)| | Middlewares | []okapi.Middleware | Per-route middleware |

Declarative vs Imperative

Imperative (traditional):

group.Post("/books", okapi.H(handler.Create),
    okapi.DocTags("Books"),
    okapi.DocSummary("Create a book"),
    okapi.Request(&CreateBookRequest{}),
    okapi.DocResponse(201, &BookResponse{}),
    okapi.DocResponse(409, &ErrorResponse{}),
)

Declarative (RouteDefinition):

app.Register(okapi.RouteDefinition{
    Method:  http.MethodPost,
    Path:    "/books",
    Handler: okapi.H(handler.Create),
    Group:   bookGroup,
    Summary: "Create a book",
    Request: &CreateBookRequest{},
    Options: []okapi.RouteOption{
        okapi.DocResponse(201, &BookResponse{}),
        okapi.DocResponse(409, &ErrorResponse{}),
    },
})

Registration

// On the app — single or variadic
app.Register(routeDef)
app.Register(routeDefs...)

// Package-level equivalent (takes a slice)
okapi.RegisterRoutes(app, routeDefs)

// On a group — assigns the group to every route that doesn't set one
api := app.Group("/api")
api.Register(routeDefs...)

When Group is nil the route is registered on the root instance; when the group's Okapi reference is unset it is filled in automatically. Supported methods are GET, POST, PUT, DELETE, PATCH, HEAD, and OPTIONS — an unsupported method panics.

Middleware comes from Middlewares on the definition, or from Use() on the app/group.

Using Struct Fields vs Options

Use struct fields for common metadata — they're cleaner and more readable:

| Use struct field | Use Options for | |---------------------------|--------------------------------------------------| | Summary, Description | okapi.DocPathParam(...) — path parameters | | Tags | okapi.DocResponse(201, ...) — non-200 statuses | | Request | okapi.DocResponse(204, nil) — no content | | Response (200 only) | okapi.DocResponse(404, &ErrorResponse{}) — errors | | Security | okapi.DocHide() — hide from docs | | Middlewares | okapi.DocQueryParam(...) — query parameters | | OperationId | okapi.DocHeader(...) — header parameters |

> okapi.DocErrorResponse(status, v) is deprecated — use okapi.DocResponse(status, v).

Group-Level Tags with .WithTags()

Use .WithTags() on groups so routes inherit tags automatically — no need to repeat Tags on every route. Only set Tags on a route when it needs to override the group default.

// All routes in this group inherit the "Books" tag
bookGroup := app.Group("/api/v1/books", authMiddleware).WithTags([]string{"Books"})
bookGroup.WithBearerAuth()

app.Register(
    // Inherits "Books" tag from group
    okapi.RouteDefinition{
        Method:   http.MethodGet,
        Path:     "",
        Handler:  bookService.List,
        Group:    bookGroup,
        Summary:  "List books",
        Response: &BooksResponse{},
    },
    // Overrides group tag
    okapi.RouteDefinition{
        Method:  http.MethodGet,
        Path:    "/categories",
        Handler: categoryService.List,
        Group:   bookGroup,
        Tags:    []string{"Categories"},
        Summary: "List categories",
    },
)

Organizing Routes in a Large Project

For large projects with many routes, use a Router struct and split route definitions across multiple files by domain. Each file contains methods that return []okapi.RouteDefinition.

File structure:

internal/routes/
├── routes.go          # Router struct, InitRoutes, registerRoutes orchestrator
├── auth_routes.go     # Auth & public API routes
├── user_routes.go     # User-scoped routes
└── admin_routes.go    # Admin routes

Router struct (in routes.go):

type Router struct {
    app *okapi.Okapi
    cfg *config.Config
    v1  *okapi.Group

    // Auth middleware
    jwtAuth      okapi.JWTAuth
    jwtAdminAuth okapi.JWTAuth

    // Handlers
    userHandler  *handlers.UserHandler
    bookHandler  *handlers.BookHandler
    adminHandler *handlers.AdminHandler
}

func InitRoutes(app *okapi.Okapi, db *gorm.DB, cfg *config.Config) {
    // Initialize repositories, services, handlers...

    r := &Router{
        app:          app,
        cfg:          cfg,
        v1:           app.Group("/api/v1"),
        jwtAuth:      middlewares.JWTAuth(cfg),
        jwtAdminAuth: middlewares.JWTAdminAuth(cfg),
        userHandler:  handlers.NewUserHandler(userRepo),
        bookHandler:  handlers.NewBookHandler(bookRepo),
        adminHandler: handlers.NewAdminHandler(db),
    }
    r.registerRoutes()
}

func (r *Router) registerRoutes() {
    r.app.Use(okapi.RequestID())

    r.app.Register(r.authRoutes()...)
    r.app.Register(r.userRoutes()...)
    r.app.Register(r.adminRoutes()...)

    // Special registrations that don't fit RouteDefinition
    r.app.Static("/assets", "public/assets")
    r.app.Web("/", "./web") // SPA index fallback — register after every API route
}

Domain-specific route files (e.g. auth_routes.go):

func (r *Router) authRoutes() []okapi.RouteDefinition {
    authGroup := r.v1.Group("/auth").WithTags([]string{"Auth"})

    return []okapi.RouteDefinition{
        {
            Method:   http.MethodPost,
            Path:     "/login",
            Handler:  okapi.H(r.userHandler.Login),
            Group:    authGroup,
            Summary:  "Login",
            Request:  &LoginRequest{},
            Response: &AuthResponse{},
        },
    }
}

Key principles:

  • One Router struct holds all handlers, middleware, and config
  • Each file returns []okapi.RouteDefinition from methods on Router
  • Groups are created inside each method with .WithTags() to avoid tag repetition
  • registerRoutes() orchestrates all registration via app.Register()
  • Conditional routes (dev mode, optional features) use if guards in registerRoutes()
  • Special registrations (static files, NoRoute, global middleware) stay imperative

Common Patterns

CRUD resource — a typical set of routes for a resource:

func (r *Router) bookRoutes() []okapi.RouteDefinition {
    g := r.v1.Group("/books", r.jwtAuth.Middleware).WithTags([]string{"Books"})
    g.WithBearerAuth()

    return []okapi.RouteDefinition{
        {Method: http.MethodGet, Path: "", Handler: okapi.H(r.bookHandler.List), Group: g,
            Summary: "List books", Request: &ListRequest{}, Response: &PageableResponse[Book]{}},
        {Method: http.MethodGet, Path: "/{id:int}", Handler: okapi.H(r.bookHandler.Get), Group: g,
            Summary: "Get book", Response: &Response[Book]{},
            Options: []okapi.RouteOption{okapi.DocPathParam("id", "integer", "Book ID"), okapi.DocResponse(404, &ErrorResponse{})}},
        {Method: http.MethodPost, Path: "", Handler: okapi.H(r.bookHandler.Create), Group: g,
            Summary: "Create book", Request: &CreateBookRequest{},
            Options: []okapi.RouteOption{okapi.DocResponse(201, &Response[Book]{}), okapi.DocResponse(409, &ErrorResponse{})}},
        {Method: http.MethodPut, Path: "/{id:int}", Handler: okapi.H(r.bookHandler.Update), Group: g,
            Summary: "Update book", Request: &UpdateBookRequest{}, Response: &Response[Book]{},
            Options: []okapi.RouteOption{okapi.DocPathParam("id", "integer", "Book ID"), okapi.DocResponse(404, &ErrorResponse{})}},
        {Method: http.MethodDelete, Path: "/{id:int}", Handler: okapi.H(r.bookHandler.Delete), Group: g,
            Summary: "Delete book",
            Options: []okapi.RouteOption{okapi.DocPathParam("id", "integer", "Book ID"), okapi.DocResponse(204, nil), okapi.DocResponse(404, &ErrorResponse{})}},
    }
}

Conditional routes — only registered when a feature is enabled:

func (r *Router) registerRoutes() {
    r.app.Register(r.publicRoutes()...)
    r.app.Register(r.userRoutes()...)

    if r.cfg.DevMode {
        r.app.Register(r.devRoutes()...)
    }
}

Optional handler — append to the slice when a dependency is available:

func (r *Router) adminRoutes() []okapi.RouteDefinition {
    g := r.v1.Group("/admin", r.jwtAdminAuth.Middleware).WithTags([]string{"Admin"})
    g.WithBearerAuth()

    routes := []okapi.RouteDefinition{
        // ... standard admin routes
    }

    if r.cronHandler != nil {
        routes = append(routes, okapi.RouteDefinition{
            Method:  http.MethodGet,
            Path:    "/jobs",
            Handler: r.cronHandler.List,
            Group:   g,
            Summary: "List scheduled jobs",
            Response: &Response[[]JobStatus]{},
        })
    }

    return routes
}

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.