| name | kotlin-spring |
| description | Spring Boot-applikasjoner i Kotlin — controllers, services, @ProtectedWithClaims, NAIS-miljøvariabler, token-validation, Testcontainers og MockOAuth2Server. Brukes via /kotlin-spring ved Spring Boot-arbeid. |
Spring Boot — Nav-spesifikt
Dette er en konfig- og review-skill, ikke en scaffolding-skill. For nytt
Spring Boot-prosjekt: bruk Nav sine starter-templates/-repos (f.eks.
navikt/spring-boot-template-varianter eller klon et eksisterende
team-repo som utgangspunkt). Denne skillen forutsetter at prosjektet
finnes, og viser hvordan det skal konfigureres for Nais.
Autentisering
@ProtectedWithClaims(issuer = "azuread", claimMap = ["NAVident=*"])
Krever token-validation-spring-avhengigheten.
NAIS-miljøvariabler for database
spring:
datasource:
url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_DATABASE}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
Testing
@SpringBootTest + Testcontainers for integrasjonstester
@MockkBean (krever com.ninja-squad:springmockk)
- MockOAuth2Server for autentiseringstester
token-support for TokenX og Azure AD
Bruk navikt/token-support —
Spring Boot-modulen heter token-validation-spring-boot-starter og gir
auto-konfigurert JWT-validering for både TokenX (borger-flows) og
Azure AD (intern).
dependencies {
implementation("no.nav.security:token-validation-spring-boot-starter:5.0.13")
}
no.nav.security.jwt:
issuer:
azuread:
discoveryurl: ${AZURE_APP_WELL_KNOWN_URL}
accepted-audience: ${AZURE_APP_CLIENT_ID}
tokenx:
discoveryurl: ${TOKEN_X_WELL_KNOWN_URL}
accepted-audience: ${TOKEN_X_CLIENT_ID}
@ProtectedWithClaims(issuer = "azuread", claimMap = ["NAVident=*"])
class InternController
@ProtectedWithClaims(issuer = "tokenx", claimMap = ["acr=Level4"])
class BorgerController
Starteren eksponerer også TokenValidationContextHolder for å hente
claims (f.eks. pid fra TokenX, NAVident fra Azure) i service-laget.
Actuator — Nais health-probes
Nais liveness/readiness peker på Spring Boot Actuator sine
dedikerte probe-endepunkter, ikke custom /isAlive//isReady.
Bruk base-path: /internal for å holde probes utenfor accessPolicy.
management:
endpoints:
web:
exposure:
include: health,prometheus
base-path: /internal
endpoint:
health:
probes:
enabled: true
show-details: always
health:
livenessstate:
enabled: true
readinessstate:
enabled: true
Resulterer i:
/internal/health/liveness
/internal/health/readiness
/internal/prometheus
Nais-manifest:
spec:
liveness:
path: /internal/health/liveness
readiness:
path: /internal/health/readiness
prometheus:
enabled: true
path: /internal/prometheus
Merk: Nais kan godt peke på /isAlive / /isReady hvis du eksponerer
egne endepunkter, men Actuator-probene er standard og reflekterer
Spring sin egen ApplicationAvailability (ReadinessState.REFUSING_TRAFFIC er intern state og skal ikke toggles manuelt for å håndtere Nais-shutdown).
Plattformkonteksten er felles — se nais-manifest-skillen for hvordan NAIS preStop-hook fungerer.
HikariCP — defaults for Nais-containere
Nais-pods er små og korte-lived. Start med liten pool og forutsigbar
lifecycle. Se postgresql-review for utdypning.
spring:
datasource:
hikari:
maximum-pool-size: 3
minimum-idle: 1
idle-timeout: 600000
max-lifetime: 1800000
connection-timeout: 30000
leak-detection-threshold: 60000
Hvorfor disse:
max-lifetime=30m < Cloud SQL/PgBouncer-timeouts → unngå "connection was closed unexpectedly".
maximum-pool-size=3 × replicas holder totalt connection-budsjett under Cloud SQL-grensa.
leak-detection-threshold fanger lekkede connections i dev.
Structured logging — Nais/Loki-vennlig JSON
Nais samler stdout til Loki; JSON pr. linje gir søkbare felt. Bruk
logstash-logback-encoder. Legg logback-spring.xml under
src/main/resources/:
<configuration>
<springProfile name="!local">
<appender name="stdout_json" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>x_callId</includeMdcKeyName>
<includeMdcKeyName>x_consumerId</includeMdcKeyName>
<customFields>{"application":"${spring.application.name}"}</customFields>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout_json"/>
</root>
</springProfile>
<springProfile name="local">
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout"/>
</root>
</springProfile>
</configuration>
implementation("net.logstash.logback:logstash-logback-encoder:8.0")
Propager x_callId/Nav-Call-Id via MDC i en request-filter slik at
logger fra samme kall kan korreleres på tvers av tjenester i Loki.
Logg aldri fødselsnummer, tokens eller andre sensitive felt —
bruk @Loggable-patterns / eksplisitt maskering.
Checklist før deploy