Agent skill

new-go-endpoint

This skill automates the API-first workflow for creating new Go API endpoints. It handles OpenAPI spec updates, code generation, SQLC query creation, handler implementation, and Redis caching patterns. Use when adding new endpoints to the Lexicon backend.

Stars 2
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/adryanev/.dotfiles/tree/main/.claude/commands/new-go-endpoint

SKILL.md

Ask the user for:

  1. Endpoint path (e.g., /api/v1/beneficial-ownership/search)
  2. HTTP method (GET, POST, PUT, DELETE)
  3. Purpose - Brief description of what the endpoint does
  4. Request parameters - Query params, path params, or request body
  5. Response structure - What data should be returned
  6. Caching requirements - Should this endpoint use Redis caching?
  7. Database query needed - Does this need a new SQLC query?

Step 2: Update OpenAPI Spec

Edit api/openapi.yaml to add:

  • New path and operation
  • Request parameters/body schema
  • Response schema with all fields
  • Proper tags for grouping

Template for new endpoint:

yaml
  /api/v1/{resource}:
    get:
      summary: Brief description
      operationId: GetResource
      tags:
        - resource
      parameters:
        - name: param
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResourceResponse'

Step 3: Generate API Code

Run code generation:

bash
make api-generate

This generates internal/api/api.gen.go with:

  • Request/response types
  • Server interface with new method signature

Step 4: Create SQLC Query (if needed)

Add query to internal/db/queries/{resource}.sql:

Query patterns from this codebase:

sql
-- Array-based filtering (prevents SQL injection)
-- name: SearchCases :many
SELECT * FROM bo_v1.cases
WHERE (cardinality($1::int[]) = 0 OR subject_type = ANY($1::int[]))
AND (cardinality($2::text[]) = 0 OR LOWER(nation) = ANY($2::text[]))
LIMIT $3 OFFSET $4;

-- Count query for pagination
-- name: CountCases :one
SELECT COUNT(*) FROM bo_v1.cases
WHERE (cardinality($1::int[]) = 0 OR subject_type = ANY($1::int[]));

Then generate:

bash
make sqlc-generate

Step 5: Implement Handler

Add handler in internal/api/handlers.go:

Handler template with caching:

go
func (s *Server) GetResource(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    // Parse parameters
    params := r.URL.Query()

    // Check cache (if applicable)
    cacheKey := "resource:key"
    if cached, err := s.redis.Get(ctx, cacheKey).Result(); err == nil {
        var result ResourceResponse
        if err := json.Unmarshal([]byte(cached), &result); err == nil {
            respondJSON(w, http.StatusOK, result)
            return
        }
    }

    // Query database
    data, err := s.queries.GetResource(ctx, db.GetResourceParams{...})
    if err != nil {
        respondError(w, http.StatusInternalServerError, "Failed to fetch resource")
        return
    }

    // Map to response
    response := mapToResourceResponse(data)

    // Cache result (5 minute TTL)
    if jsonData, err := json.Marshal(response); err == nil {
        s.redis.Set(ctx, cacheKey, jsonData, 5*time.Minute)
    }

    respondJSON(w, http.StatusOK, response)
}

Handler template without caching:

go
func (s *Server) GetResource(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    // Parse and validate parameters
    // ...

    // Query database
    data, err := s.queries.GetResource(ctx, params)
    if err != nil {
        respondError(w, http.StatusInternalServerError, "Failed to fetch resource")
        return
    }

    respondJSON(w, http.StatusOK, mapToResponse(data))
}

Step 6: Add Route

Add route in internal/api/routes.go if not auto-registered.

Step 7: Type Mapping Helpers

For SQLC queries with CASE expressions, add helper functions:

go
// Convert nullable interface{} to string (for CASE expressions)
func convertLabel(label interface{}) string {
    if label == nil {
        return ""
    }
    if str, ok := label.(string); ok {
        return str
    }
    return fmt.Sprintf("%v", label)
}

// Map database int codes to API string enums
func mapSubjectType(code int32) string {
    switch code {
    case 1: return "individual"
    case 2: return "company"
    case 3: return "organization"
    default: return "unknown"
    }
}

Step 8: Test the Endpoint

Run the server and test with curl:

bash
make api-dev
curl -X GET "http://localhost:8080/api/v1/{resource}?param=value" | jq

<validation_patterns>

ULID Validation

go
var ulidRegex = regexp.MustCompile(`^[0-9A-HJKMNP-TV-Z]{26}$`)

func isValidULID(id string) bool {
    return ulidRegex.MatchString(id)
}

Year Range Validation

go
const (
    MinYear = 1900
    MaxYear = 2100
    MaxYearSpan = 50
)

func validateYearRange(startYear, endYear int) error {
    if startYear < MinYear || endYear > MaxYear {
        return errors.New("year out of range")
    }
    if endYear - startYear > MaxYearSpan {
        return errors.New("year range too large")
    }
    return nil
}

</validation_patterns>

<cache_keys>

Standard Cache Key Patterns

Pattern TTL Example
chart:{type} 5 min chart:countries
lkpp:{type} 5 min lkpp:province
filter:{hash} 1 min filter:abc123
search:{hash} 30 sec search:def456
</cache_keys>

<success_criteria> Endpoint creation is complete when:

  • OpenAPI spec updated with full schema
  • make api-generate runs without errors
  • SQLC query added (if needed)
  • make sqlc-generate runs without errors
  • Handler implemented with proper error handling
  • Route registered
  • Tested with curl and response documented
  • make check passes (lint + tests) </success_criteria>

Expand your agent's capabilities with these related and highly-rated skills.

adryanev/.dotfiles

karpathy-mode

This skill should be used to enforce a disciplined coding workflow that avoids common LLM mistakes. It applies when writing code, implementing features, or when the user says /karpathy, /k-mode, or asks for "careful", "disciplined", or "minimal" implementation. Enforces assumption surfacing, simplicity-first coding, surgical changes, and test-driven execution.

2 0
Explore
adryanev/.dotfiles

disciplined-work

This skill should be used to execute work plans with strict coding discipline that avoids common LLM mistakes. It merges structured plan execution (phased workflow, incremental commits, quality checks) with disciplined coding guardrails (assumption surfacing, simplicity-first, surgical changes, test-driven execution). Triggers on /disciplined-work, /k-work, "disciplined work", or "careful work".

2 0
Explore
adryanev/.dotfiles

github-issues-from-spec

This skill transforms feature specifications, requirements documents, or plans into well-structured GitHub issues. It chunks large features into small, testable issues, creates them via gh CLI, and adds them to the appropriate GitHub project with labels. Use when converting specs to actionable issues.

2 0
Explore
adryanev/.dotfiles

test-endpoints

This skill automates API endpoint testing and documentation. It runs curl requests against specified endpoints, captures responses, validates against OpenAPI spec, and generates markdown documentation. Use after implementing new endpoints or when verifying API behavior.

2 0
Explore
mattpocock/skills

obsidian-vault

Search, create, and manage notes in the Obsidian vault with wikilinks and index notes. Use when user wants to find, create, or organize notes in Obsidian.

111,310 9,758
Explore
mattpocock/skills

edit-article

Edit and improve articles by restructuring sections, improving clarity, and tightening prose. Use when user wants to edit, revise, or improve an article draft.

111,310 9,758
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results