Create API Endpoint Skill
When to Use
- Creating new REST API endpoints
- Modifying existing API endpoints
- Need OpenAPI spec annotations
- API versioning requirements
Process
Phase 1: Load Context
Automatically read:
.claude/docs/api-guidelines.md- Complete annotation guide.claude/docs/development-guidelines.md- API versioning, documentation.claude/docs/testing-guidelines.md- API testing requirements
Phase 2: Design API
Define:
- HTTP method (GET/POST/PUT/DELETE)
- URL path (must start with
/api/$VERSION/...) - Request/Response DTOs
- API version required (increment Current Version if new behavior)
- Authentication requirements
API Versioning Rules:
- New functionality → New API version
- New endpoint → New API version
- Modified endpoint → New API version
- Bug fix → Same API version
Phase 3: Write API Tests First
Test Requirements:
- ✅ New behavior works with NEW API version
- ❌ New behavior does NOT work with OLD API version
- ✅ Error conditions return correct responses
- ✅ Success conditions work in all call patterns
Example Test:
def "should return projects for API v44"() {
when:
def response = client.get("/api/44/projects")
then:
response.status == 200
response.json.size() > 0
}
def "should reject for API v43"() {
when:
def response = client.get("/api/43/projects")
then:
response.status == 404 // or appropriate error
}
Phase 4: Create/Update DTOs
Annotate Data Classes:
@Schema(description = "Project response")
class ProjectResponse {
@Schema(description = "Project name", example = "MyProject")
String name
@Schema(description = "Project description")
String description
@Schema(description = "Creation date", example = "2025-01-01T00:00:00Z")
String created
}
Phase 5: Create/Update Controller
Step 1: Add @Controller Annotation:
@Controller(value = "/api/44")
class ProjectController {
// ...
}
Step 2: Annotate Method:
@Operation(
method = "GET",
summary = "List Projects",
description = "Returns a list of all projects accessible to the user"
)
@Tag(name = "Project")
@ApiResponse(
responseCode = "200",
description = "Project list successfully retrieved",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ProjectListResponse.class)
)
)
@Get(uri = "/projects", produces = MediaType.APPLICATION_JSON)
def listProjects() {
// implementation
}
For POST/PUT with Request Body:
@Operation(
method = "POST",
summary = "Create Project",
description = "Creates a new project with the specified configuration",
requestBody = @RequestBody(
description = "Project configuration",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ProjectCreateRequest.class)
),
required = true
)
)
@ApiResponse(
responseCode = "201",
description = "Project created successfully",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ProjectResponse.class)
)
)
@Post(uri = "/projects")
def createProject() {
// implementation
}
For Query/Path Parameters:
@Parameters([
@Parameter(
name = "project",
in = ParameterIn.PATH,
description = "Project name",
required = true,
schema = @Schema(type = "string")
),
@Parameter(
name = "includeArchived",
in = ParameterIn.QUERY,
description = "Include archived items",
schema = @Schema(type = "boolean", defaultValue = "false")
)
])
@Get(uri = "/project/{project}/jobs")
def getProjectJobs() {
// implementation
}
Phase 6: Update build.gradle (If New Plugin)
// Add dependencies using versions centralized in gradle.properties
compileOnly "io.micronaut.openapi:micronaut-openapi:${micronautOpenapiVersion}"
implementation "io.swagger.core.v3:swagger-annotations:${swaggerVersion}"
// Set target file
tasks.withType(GroovyCompile) {
def target = new File(
project.rootDir,
"rundeckapp/build/openapi/${project.name}.yml"
).absolutePath
configure(groovyOptions) {
forkOptions.jvmArgs = [
'-Xmx1024m',
"-Dmicronaut.openapi.target.file=${target}".toString()
]
}
}
Phase 7: Run Tests and Verify
# API tests
./gradlew :functional-test:apiTest
# Verify OpenAPI spec generated
ls rundeckapp/build/classes/groovy/main/META-INF/swagger/rundeck-*.yml
# Full test suite
./gradlew test
Phase 8: Update Documentation
- Rundeck Docs: Update API Reference
- API Version History: Document changes in API Version History
- If new API version: Document what was added/changed
Required Annotations Summary
Controller Level
@Controller(value = "/api/44/base-path") // Required
Method Level
@Get|@Post|@Put|@Delete(uri = "/path") // Required - HTTP method
@Operation(...) // Required - OpenAPI operation
@Tag(name = "Category") // Required - Grouping (or tags=["Category"] in @Operation)
@ApiResponse(...) // Required - Response definition
@Parameters([...]) // Optional - Query/path params
Data Types
@Schema(...) // Required on DTO classes
Checklist
Design:
- API design reviewed (HTTP method, path, versioning)
- API version incremented for new functionality
build.gradle:
- Micronaut/Swagger dependencies added
- Target file configured for plugin (if new plugin)
Controller:
- Annotated with
@Controller - Each method has
@Get/@Post/@Put/@Delete - Each method has
@Operationwith detailed description - Each method has exactly one tag (
@Tag(name = "X")ortags = ["X"]in@Operation) - Each method has
@ApiResponse - Request body specified for POST/PUT
- Parameters specified if needed
Data Types:
- DTO classes annotated with
@Schema - DTO fields annotated with
@Schema
Testing:
- API tests written first (TDD)
- Tests verify new API version works
- Tests verify old API version does NOT support new behavior
- API tests pass
Verification:
- Build and verify YAML generated in
rundeckapp/build/classes/groovy/main/META-INF/swagger/ - OpenAPI spec includes new endpoints
- Rundeck documentation updated
Common Mistakes to Avoid
❌ Don't:
- Forget
@Controllerannotation (methods won't appear in spec!) - Use old API version for new functionality
- Skip API version tests
- Omit
@Schemaon DTOs - Forget to update Rundeck docs
✅ Do:
- Always use
@Controller - Increment API version for new behavior
- Test both old and new API versions
- Annotate all DTOs completely
- Update official documentation
Resources
.claude/docs/api-guidelines.md- Complete annotation guide.claude/docs/development-guidelines.md- API versioning- Reference Plugin: rundeck-ec2-nodes-plugin
- Micronaut OpenAPI
- Swagger Annotations