[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M73958HTB1FZPFFMGH332NJ2":3,"skill-same:01M73958HTB1FZPFFMGH332NJ2":49},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":27,"commitSha":28,"skillDirectory":29,"forksCount":14,"repositoryHtmlUrl":30,"repositoryHomepageUrl":6,"ownerHtmlUrl":31,"ownerBlogUrl":32,"ownerDescription":33,"updatedAt":34,"install":35,"resources":48},"01M73958HTB1FZPFFMGH332NJ2","baks-spring-boot-best-practices",null,"Spring Boot best practices for Kotlin — injection, transactions, proxy annotations, JPA entities, naming, and REST API design. Use when writing or reviewing Spring Boot code in Kotlin.","navikt\u002Ffamilie-baks-ai","navikt","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F11848947?v=4","01M73951HJ1NES3SDFYH4AKNRK","Nav IT","01M73951J77Z8PJZ3XSYE4RYSF",0,"MIT","https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbaks-spring-boot-best-practices\u002FSKILL.md",[],[],"2026-11-11 15:11:14",false,"---\nname: baks-spring-boot-best-practices\ndescription: Spring Boot best practices for Kotlin — injection, transactions, proxy annotations, JPA entities, naming, and REST API design. Use when writing or reviewing Spring Boot code in Kotlin.\nlicense: MIT\nmetadata:\n  domain: backend\n  tags: spring-boot kotlin jpa rest transactions proxy logging naming\n---\n\n# Spring Boot Best Practices (Kotlin)\n\n## Injection\n\n**Use constructor injection, not field injection.**\n\n```kotlin\n\u002F\u002F Bad\n@Service\nclass PaymentService {\n    @Autowired\n    lateinit var repository: PaymentRepository\n}\n\n\u002F\u002F Good\n@Service\nclass PaymentService(\n    private val repository: PaymentRepository,\n)\n```\n\n## Proxy Annotations\n\n**Proxy annotations (`@Transactional`, `@Cacheable`, `@CacheEvict`, `@CachePut`, `@Async`) must not be placed on private methods** — the Spring proxy cannot intercept them.\n\n```kotlin\n\u002F\u002F Bad\n@Service\nclass InvoiceService {\n    fun createInvoice() { persistInvoice() }\n\n    @Transactional\n    private fun persistInvoice() { \u002F* proxy never intercepts this *\u002F }\n}\n\n\u002F\u002F Good\n@Service\nclass InvoiceService {\n    @Transactional\n    fun createInvoice() { persistInvoice() }\n\n    private fun persistInvoice() { \u002F* transaction opened on the public method *\u002F }\n}\n```\n\n**Avoid self-invocation between proxy-annotated methods.** Calls via `this` bypass the proxy — inject a separate bean instead.\n\n```kotlin\n\u002F\u002F Bad\n@Service\nclass ReportService {\n    @Transactional\n    fun generate(id: Long) { archive(id) } \u002F\u002F @Async never fires\n\n    @Async\n    fun archive(id: Long) { }\n}\n\n\u002F\u002F Good\n@Service\nclass ReportService(private val archiveService: ArchiveService) {\n    @Transactional\n    fun generate(id: Long) { archiveService.archive(id) }\n}\n\n@Service\nclass ArchiveService {\n    @Async\n    fun archive(id: Long) { }\n}\n```\n\n## Configuration\n\n**`@Configuration` classes must not hold mutable state** (exceptions: `@Value` and `@ConfigurationProperties` fields).\n\n```kotlin\n\u002F\u002F Bad\n@Configuration\nclass MailConfiguration {\n    var retries = 0\n    @Bean fun mailClient(): MailClient = MailClient()\n}\n\n\u002F\u002F Good\n@Configuration\nclass MailConfiguration {\n    @Bean\n    fun mailClient(properties: MailProperties): MailClient =\n        MailClient(properties.host, properties.port)\n}\n```\n\n**`@Bean` methods must live in `@Configuration` classes**, not in `@Component`.\n\n## Exceptions\n\n**Custom exceptions must extend `RuntimeException`**, not `Exception`.\n\n```kotlin\n\u002F\u002F Bad\nclass PaymentFailedException(message: String) : Exception(message)\n\n\u002F\u002F Good\nclass PaymentFailedException(message: String) : RuntimeException(message)\n```\n\n## JPA\n\n**Kotlin `data class` must not be used as JPA entities** — `data class` is `final` and prevents lazy-loading proxies.\n\n```kotlin\n\u002F\u002F Bad\n@Entity\ndata class UserEntity(@Id val id: Long, val email: String)\n\n\u002F\u002F Good\n@Entity\nclass UserEntity(\n    @Id var id: Long? = null,\n    var email: String,\n)\n```\n\n**All `@Entity` classes must declare an `@Id` field.**\n\n**`@Transactional` belongs in the service layer**, not in controllers.\n\n**Domain and entity packages must not import `org.springframework.*`** — keep the domain model independent of Spring.\n\n## Naming\n\n| Annotation | Suffix |\n|---|---|\n| `@Service` | `Service` |\n| `@Repository` | `Repository` |\n| `@Controller` \u002F `@RestController` | `Controller` |\n| `@ControllerAdvice` \u002F `@RestControllerAdvice` | `ExceptionHandler` or `Advice` |\n| `@ConfigurationProperties` | `Properties` |\n\n## REST API\n\n**Use specific HTTP method annotations** — avoid `@RequestMapping` where `@GetMapping`, `@PostMapping`, etc. apply.\n\n**No trailing slash** on mapping paths: `\u002Fusers`, not `\u002Fusers\u002F`.\n\n**`GET` endpoints must return something** — not `Unit`\u002F`void`. If it is a command, use `POST`.\n\n**Do not expose JPA entities from REST** — use dedicated DTOs.\n\n**Controllers must not inject repositories directly** — go through a service.\n\n```kotlin\n\u002F\u002F Bad\n@RestController\nclass UserController(private val userRepository: UserRepository)\n\n\u002F\u002F Good\n@RestController\nclass UserController(private val userService: UserService)\n```\n\n","# Spring Boot Best Practices (Kotlin)\n\n## Injection\n\n**Use constructor injection, not field injection.**\n\n```kotlin\n\u002F\u002F Bad\n@Service\nclass PaymentService {\n    @Autowired\n    lateinit var repository: PaymentRepository\n}\n\n\u002F\u002F Good\n@Service\nclass PaymentService(\n    private val repository: PaymentRepository,\n)\n```\n\n## Proxy Annotations\n\n**Proxy annotations (`@Transactional`, `@Cacheable`, `@CacheEvict`, `@CachePut`, `@Async`) must not be placed on private methods** — the Spring proxy cannot intercept them.\n\n```kotlin\n\u002F\u002F Bad\n@Service\nclass InvoiceService {\n    fun createInvoice() { persistInvoice() }\n\n    @Transactional\n    private fun persistInvoice() { \u002F* proxy never intercepts this *\u002F }\n}\n\n\u002F\u002F Good\n@Service\nclass InvoiceService {\n    @Transactional\n    fun createInvoice() { persistInvoice() }\n\n    private fun persistInvoice() { \u002F* transaction opened on the public method *\u002F }\n}\n```\n\n**Avoid self-invocation between proxy-annotated methods.** Calls via `this` bypass the proxy — inject a separate bean instead.\n\n```kotlin\n\u002F\u002F Bad\n@Service\nclass ReportService {\n    @Transactional\n    fun generate(id: Long) { archive(id) } \u002F\u002F @Async never fires\n\n    @Async\n    fun archive(id: Long) { }\n}\n\n\u002F\u002F Good\n@Service\nclass ReportService(private val archiveService: ArchiveService) {\n    @Transactional\n    fun generate(id: Long) { archiveService.archive(id) }\n}\n\n@Service\nclass ArchiveService {\n    @Async\n    fun archive(id: Long) { }\n}\n```\n\n## Configuration\n\n**`@Configuration` classes must not hold mutable state** (exceptions: `@Value` and `@ConfigurationProperties` fields).\n\n```kotlin\n\u002F\u002F Bad\n@Configuration\nclass MailConfiguration {\n    var retries = 0\n    @Bean fun mailClient(): MailClient = MailClient()\n}\n\n\u002F\u002F Good\n@Configuration\nclass MailConfiguration {\n    @Bean\n    fun mailClient(properties: MailProperties): MailClient =\n        MailClient(properties.host, properties.port)\n}\n```\n\n**`@Bean` methods must live in `@Configuration` classes**, not in `@Component`.\n\n## Exceptions\n\n**Custom exceptions must extend `RuntimeException`**, not `Exception`.\n\n```kotlin\n\u002F\u002F Bad\nclass PaymentFailedException(message: String) : Exception(message)\n\n\u002F\u002F Good\nclass PaymentFailedException(message: String) : RuntimeException(message)\n```\n\n## JPA\n\n**Kotlin `data class` must not be used as JPA entities** — `data class` is `final` and prevents lazy-loading proxies.\n\n```kotlin\n\u002F\u002F Bad\n@Entity\ndata class UserEntity(@Id val id: Long, val email: String)\n\n\u002F\u002F Good\n@Entity\nclass UserEntity(\n    @Id var id: Long? = null,\n    var email: String,\n)\n```\n\n**All `@Entity` classes must declare an `@Id` field.**\n\n**`@Transactional` belongs in the service layer**, not in controllers.\n\n**Domain and entity packages must not import `org.springframework.*`** — keep the domain model independent of Spring.\n\n## Naming\n\n| Annotation | Suffix |\n|---|---|\n| `@Service` | `Service` |\n| `@Repository` | `Repository` |\n| `@Controller` \u002F `@RestController` | `Controller` |\n| `@ControllerAdvice` \u002F `@RestControllerAdvice` | `ExceptionHandler` or `Advice` |\n| `@ConfigurationProperties` | `Properties` |\n\n## REST API\n\n**Use specific HTTP method annotations** — avoid `@RequestMapping` where `@GetMapping`, `@PostMapping`, etc. apply.\n\n**No trailing slash** on mapping paths: `\u002Fusers`, not `\u002Fusers\u002F`.\n\n**`GET` endpoints must return something** — not `Unit`\u002F`void`. If it is a command, use `POST`.\n\n**Do not expose JPA entities from REST** — use dedicated DTOs.\n\n**Controllers must not inject repositories directly** — go through a service.\n\n```kotlin\n\u002F\u002F Bad\n@RestController\nclass UserController(private val userRepository: UserRepository)\n\n\u002F\u002F Good\n@RestController\nclass UserController(private val userService: UserService)\n```\n\n",{"name":5,"license":15,"metadata":24,"description":7},{"tags":25,"domain":26},"spring-boot kotlin jpa rest transactions proxy logging naming","backend","main","4977abc6448cab9cd6db1187ad9207d734e37479",".github\u002Fskills\u002Fbaks-spring-boot-best-practices","https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai","https:\u002F\u002Fgithub.com\u002Fnavikt","https:\u002F\u002Fnav.no","Arbeids- og velferdsdirektoratet - The Norwegian Labour and Welfare Directorate","2026-06-23 20:53:19",{"source":8,"skillName":5,"commands":36,"prompt":46,"zipAvailable":47},[37,40,43],{"tool":38,"command":39},"npx","npx skills add navikt\u002Ffamilie-baks-ai --skill baks-spring-boot-best-practices",{"tool":41,"command":42},"bunx","bunx skills add navikt\u002Ffamilie-baks-ai --skill baks-spring-boot-best-practices",{"tool":44,"command":45},"pnpm","pnpm dlx skills add navikt\u002Ffamilie-baks-ai --skill baks-spring-boot-best-practices","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbaks-spring-boot-best-practices\u002FSKILL.md",true,[],[50,57,62,69,76,82],{"skillNo":51,"name":52,"nameZh":6,"description":53,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":54,"categories":55,"keywords":56,"publishedAt":19},"01M7395JXHY23DF5NV40VFAPHJ","baks-testcontainers-colima","Use when running tests with Maven (mvn test, mvn verify, mvn package, mvn install) or other JVM tools on macOS with Colima\u002FDocker Desktop. Automatically configures Testcontainers to connect to PostgreSQL containers through Colima's Docker socket.","https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai\u002Fblob\u002Fmain\u002F.opencode\u002Fskills\u002Fbaks-testcontainers-colima\u002FSKILL.md",[],[],{"skillNo":58,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":59,"categories":60,"keywords":61,"publishedAt":19},"01M7395H1K9QRT1BPNBD6TRAKJ","https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai\u002Fblob\u002Fmain\u002F.opencode\u002Fskills\u002Fbaks-spring-boot-best-practices\u002FSKILL.md",[],[],{"skillNo":63,"name":64,"nameZh":6,"description":65,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":68,"publishedAt":19},"01M7395F4VTR777DBS0SDT2ABC","baks-maven-kotlin-build","Use when running Maven builds (mvn) in this project. The Kotlin compiler daemon fails with \"cannot connect to registry: Operation not permitted\" in this environment. Always add -Dkotlin.compiler.daemon=false to all mvn commands.","https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai\u002Fblob\u002Fmain\u002F.opencode\u002Fskills\u002Fbaks-maven-kotlin-build\u002FSKILL.md",[],[],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":19},"01M7395DKWR1CRX8NNBDG5V2X1","baks-hello-project-skill","Dummy skill – prints its own name","https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai\u002Fblob\u002Fmain\u002F.opencode\u002Fskills\u002Fbaks-hello-project\u002FSKILL.md",[],[],{"skillNo":77,"name":52,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":81,"publishedAt":19},"01M7395B78K9DVXD93F8T4JQ75","Bruk når du kjører tester med Maven (mvn test, mvn verify, mvn package, mvn install) eller andre JVM-verktøy på macOS med Colima\u002FDocker Desktop. Konfigurerer automatisk Testcontainers til å koble til PostgreSQL-containere via Colimas Docker-socket.","https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbaks-testcontainers-colima\u002FSKILL.md",[],[],{"skillNo":83,"name":64,"nameZh":6,"description":84,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":19},"01M73955R67S6JTETDXKC8WDJT","Bruk når du kjører Maven-bygg (mvn) i dette prosjektet. Kotlin compiler daemon feiler med \"cannot connect to registry: Operation not permitted\" i dette miljøet. Legg alltid til -Dkotlin.compiler.daemon=false i alle mvn-kommandoer.","https:\u002F\u002Fgithub.com\u002Fnavikt\u002Ffamilie-baks-ai\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbaks-maven-kotlin-build\u002FSKILL.md",[],[]]