원클릭으로
api-design
REST API-designmønstre, versjonering, feilhåndtering (RFC 7807) og OpenAPI-konvensjoner for Nav-tjenester
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
REST API-designmønstre, versjonering, feilhåndtering (RFC 7807) og OpenAPI-konvensjoner for Nav-tjenester
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Generer conventional commit-meldinger med Nav-relevante scopes og breaking change-format
Expert builder for the Aksel design system (Nav / @navikt) React components, design tokens, layout primitives, theming (light/dark), icons, CSS, the Tailwind preset, version migrations, and Figma-to-code. Trigger on any frontend UI task that mentions Aksel, Nav/Navikt, "designsystemet", or @navikt/ds-* / @navikt/aksel-* packages — or that asks to add, create, build, or refactor a component (button, input, modal, table, alert, card, form) or layout, or to implement a design from Figma (a pasted figma.com/design/...?node-id link, "implement this design", "build this from Figma", design-to-code). Strong signals "using/with aksel", "@navikt/ds-react", "design system", a pasted figma.com link. If the work is frontend UI and there is any Aksel signal, invoke this skill unless the user explicitly opts out.
Integrer og konfigurer Nav Dekoratøren – felles header og footer for nav.no-applikasjoner. Bruk når et team skal ta i bruk Dekoratøren, oppdatere konfigurasjon, legge til breadcrumbs/språkvelger/analytics, håndtere samtykke (ekomloven), CSP eller feilsøke integrasjon mot dekoratøren.
Lag responsive layouts med Aksel Design System (v8+) - spacing tokens, layout primitives (Box, HStack, VStack, HGrid, Page, Bleed) og ResponsiveProp
Generer og kjør Playwright E2E-tester for webapplikasjoner med page objects, auth fixtures og tilgjengelighetstester
Kompakt output-stil som kutter fyllord og beholder teknisk substans — spar output-tokens uten å miste nøyaktighet.
| name | api-design |
| description | REST API-designmønstre, versjonering, feilhåndtering (RFC 7807) og OpenAPI-konvensjoner for Nav-tjenester |
| license | MIT |
| compatibility | Go or Kotlin backend on Nais |
| metadata | {"domain":"backend","tags":"api rest design openapi error-handling"} |
REST API design for Nav services. Covers naming conventions, error handling with ProblemDetail, versioning, pagination, and OpenAPI spec.
# ✅ Correct
GET /api/vedtak # List
GET /api/vedtak/{id} # Get by ID
POST /api/vedtak # Create
PUT /api/vedtak/{id} # Full update
PATCH /api/vedtak/{id} # Partial update
DELETE /api/vedtak/{id} # Delete
# ✅ Sub-resources
GET /api/vedtak/{id}/aktiviteter # List child resources
POST /api/vedtak/{id}/aktiviteter # Create child resource
# ✅ Actions (verb as sub-resource)
POST /api/vedtak/{id}/godkjenn # State transition
# ❌ Wrong
GET /api/getVedtak # Verb in URL
GET /api/vedtak/hentAlle # Verb in URL
POST /api/createVedtak # Verb in URL
GET /api/Vedtak # PascalCase
// Spring Boot 3+ — built-in ProblemDetail support
@RestControllerAdvice
class ErrorHandler {
@ExceptionHandler(ResourceNotFoundException::class)
fun handleNotFound(ex: ResourceNotFoundException): ProblemDetail =
ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.message ?: "Resource not found").apply {
title = "Resource not found"
setProperty("resourceType", ex.resourceType)
setProperty("resourceId", ex.resourceId)
}
@ExceptionHandler(MethodArgumentNotValidException::class)
fun handleValidation(ex: MethodArgumentNotValidException): ProblemDetail =
ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Validation failed").apply {
title = "Invalid request"
setProperty("errors", ex.bindingResult.fieldErrors.map {
mapOf("field" to it.field, "message" to it.defaultMessage)
})
}
}
For Ktor, use the StatusPages plugin:
install(StatusPages) {
exception<ResourceNotFoundException> { call, cause ->
call.respond(HttpStatusCode.NotFound, ProblemResponse(
title = "Resource not found",
status = 404,
detail = cause.message ?: "Resource not found",
instance = call.request.uri,
))
}
exception<ValidationException> { call, cause ->
call.respond(HttpStatusCode.BadRequest, ProblemResponse(
title = "Invalid request",
status = 400,
detail = "Validation failed",
errors = cause.errors,
))
}
}
Response format (RFC 7807):
{
"type": "about:blank",
"title": "Resource not found",
"status": 404,
"detail": "Vedtak with id 123 does not exist",
"instance": "/api/vedtak/123",
"resourceType": "vedtak",
"resourceId": "123"
}
Use offset-based pagination with consistent parameter names:
@GetMapping("/api/vedtak")
fun list(
@RequestParam(defaultValue = "0") page: Int,
@RequestParam(defaultValue = "20") size: Int,
@RequestParam(defaultValue = "opprettetDato") sort: String,
@RequestParam(defaultValue = "desc") order: String,
): Page<VedtakDTO> {
require(size in 1..100) { "size must be between 1 and 100" }
val pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.fromString(order), sort))
return vedtakService.findAll(pageable)
}
Response:
{
"content": [...],
"page": {
"size": 20,
"number": 0,
"totalElements": 142,
"totalPages": 8
}
}
data class CreateVedtakRequest(
@field:NotBlank(message = "Title is required")
val tittel: String,
@field:Size(min = 11, max = 11, message = "FNR must be 11 digits")
@field:Pattern(regexp = "\\d{11}", message = "FNR must consist of digits")
val fnr: String,
@field:Positive(message = "Amount must be positive")
val belop: BigDecimal,
@field:NotNull(message = "Start date is required")
val fom: LocalDate,
)
| Code | Usage |
|---|---|
200 OK | Successful GET, PUT, PATCH |
201 Created | Successful POST (new resource) |
204 No Content | Successful DELETE |
400 Bad Request | Invalid input / validation failed |
401 Unauthorized | Missing or invalid token |
403 Forbidden | Valid token, but no access |
404 Not Found | Resource does not exist |
409 Conflict | Duplicate / state conflict |
422 Unprocessable Entity | Semantic error (valid format, wrong content) |
500 Internal Server Error | Unexpected server error |
// Spring Boot + springdoc-openapi
@Operation(
summary = "Hent vedtak",
description = "Henter vedtak basert på ID",
responses = [
ApiResponse(responseCode = "200", description = "Vedtak funnet"),
ApiResponse(responseCode = "404", description = "Vedtak ikke funnet"),
]
)
@GetMapping("/{id}")
fun getById(@PathVariable id: UUID): ResponseEntity<VedtakDTO>
Use URL-based versioning when breaking changes are necessary:
// v1 — original
@RestController
@RequestMapping("/api/v1/vedtak")
class VedtakV1Controller
// v2 — ny kontrakt
@RestController
@RequestMapping("/api/v2/vedtak")
class VedtakV2Controller
Alternatively, avoid versioning by:
@Deprecated before removal/api/vedtak-perioderopprettetDato, brukerId@ValidContent-Type: application/json on all responses