Standardize database access and repository patterns using Kotlin and SQL transactions.
Install
mkdir -p .claude/skills/repository && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15048" && unzip -o skill.zip -d .claude/skills/repository && rm skill.zipInstalls to .claude/skills/repository
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Repository-mønster med Kotlin object og Connection/TransactionalSession extension-funksjoner, DBUtils-transaksjoner og ResultSet-mappingKey capabilities
- →Implement repository pattern with Kotlin objects
- →Use `Connection` and `TransactionalSession` extension functions
- →Manage database transactions via `DBUtils.asyncTransaction` or `DBUtils.transaction`
- →Centralize `ResultSet` to domain mapping in `RepositoryMappers.kt`
- →Handle database errors using `useAndHandleErrors`
- →Configure `PostgresDataSource` with HikariCP and Vault integration
How it works
The skill defines patterns for database access using Kotlin objects, extension functions, and `DBUtils` for transactions, centralizing data source configuration and result set mapping.
Inputs & outputs
When to use repository
- →Implementing repository-pattern data access
- →Configuring transactional database operations
- →Setting up SQL migration workflows
About this skill
Repository & Database Access Patterns
Repository- og databaseaksessmønstre: object-repositories med Connection-extensions, DBUtils-transaksjoner, ResultSet-mapping og feilhåndtering.
All database access goes through
DBUtils.asyncTransaction {}(suspending) orDBUtils.transaction {}(blocking). Repositories are Kotlinobjects with extension functions onConnectionorTransactionalSession.
DataSource
PostgresDataSource is a singleton object using HikariCP. In non-local environments it integrates with Vault for credentials:
object PostgresDataSource {
val dataSource: HikariDataSource by lazy { dataSource() }
fun migrate() {
dataSource(role = postgresConfig.adminUser).use { migrate(it) }
}
fun migrate(dataSource: HikariDataSource) {
Flyway.configure()
.dataSource(dataSource)
.initSql("""SET ROLE "${postgresConfig.adminUser}"""")
.lockRetryCount(-1)
.validateMigrationNaming(true)
.load()
.migrate()
}
}
Transactions via DBUtils
Always use DBUtils.asyncTransaction {} (suspending) or DBUtils.transaction {} (blocking). Never use bare JDBC connections directly.
// Suspending (use in coroutine context / service layer)
dataSource.asyncTransaction { session ->
KravRepository.run { session.insertAllNewKrav(kravLinjer, filnavn) }
}
// Blocking (use in sync contexts)
dataSource.transaction { session ->
KravRepository.run { session.updateStatus(corrId, status) }
}
Repository Pattern
Repositories are Kotlin objects with extension functions on Connection (raw JDBC) or TransactionalSession (kotliquery). Use RepositoryExtensions helpers:
object KravRepository {
fun Connection.getAllUnsentKrav() =
executeSelect(
"""select * from krav where status = ?""",
Status.KRAV_IKKE_SENDT.value,
).toKrav()
fun Connection.updateSentKrav(
corrId: String,
kravidentifikatorSKE: String,
status: String,
) = executeUpdate(
"""update krav set kravidentifikator_ske = ?, status = ?, tidspunkt_sendt = now() where corr_id = ?""",
kravidentifikatorSKE,
status,
corrId,
)
// For kotliquery TransactionalSession (e.g. when returnGeneratedKey is needed)
fun getKravTableIdFromCorrelationId(
tx: TransactionalSession,
corrID: String,
): Long =
tx.single(
queryOf("select id from krav where corr_id = ?", corrID)
.map { row -> row.long("id") }
.asSingle,
) ?: throw IllegalStateException("Krav med corrId $corrID ikke funnet")
}
ResultSet Mapping
Centralise ResultSet → domain mapping in RepositoryMappers.kt using getColumn<T>():
fun ResultSet.toKrav() =
toList {
Krav(
kravId = getColumn("id"),
saksnummerNAV = getColumn("saksnummer_nav"),
status = getColumn("status"),
kravtype = getColumn("kravtype"),
corrId = getColumn("corr_id"),
kravidentifikatorSKE = getColumn("kravidentifikator_ske"),
// ... remaining fields
)
}
private fun <T> ResultSet.toList(mapper: ResultSet.() -> T) =
buildList {
while (next()) { add(mapper()) }
}
Error Handling
Wrap bare Connection usage with useAndHandleErrors:
dataSource.connection.useAndHandleErrors { con ->
con.getAllUnsentKrav()
}
Boundaries
✅ Always
- Use
DBUtils.asyncTransaction {}/DBUtils.transaction {}— never bareConnectionin service code - Use
object+ extension functions for repositories - Map
ResultSet→ domain inRepositoryMappers.ktusinggetColumn<T>() - Wrap
Connectionusage withuseAndHandleErrors
🚫 Never
- Skip Flyway migrations for schema changes
- Use bare JDBC connections directly in service code
- Scatter ResultSet mapping logic outside
RepositoryMappers.kt
When not to use it
- →When skipping Flyway migrations for schema changes
- →When using bare JDBC connections directly in service code
Limitations
- →All database access must go through `DBUtils.asyncTransaction {}` or `DBUtils.transaction {}`
- →ResultSet mapping logic must be centralized in `RepositoryMappers.kt`
- →Bare JDBC connections are not to be used directly in service code
How it compares
This skill enforces a structured, transactional, and type-safe approach to database access in Kotlin, preventing common pitfalls like scattered ResultSet mapping or unmanaged connections, unlike direct JDBC usage.
Compared to similar skills
repository side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| repository (this skill) | 0 | 3mo | No flags | Intermediate |
| drizzle-orm | 32 | 2mo | No flags | Intermediate |
| database-migration | 3 | 2mo | No flags | Advanced |
| backend-dev | 1 | 6mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by navikt
View all by navikt →You might also like
drizzle-orm
EpicenterHQ
Drizzle ORM patterns for type branding and custom types. Use when working with Drizzle column definitions, branded types, or custom type conversions.
database-migration
wshobson
Execute database migrations across ORMs and platforms with zero-downtime strategies, data transformation, and rollback procedures. Use when migrating databases, changing schemas, performing data transformations, or implementing zero-downtime deployment strategies.
backend-dev
marmelab
Coding practices for backend development in Atomic CRM. Use when deciding whether backend logic is needed, or when creating/modifying database migrations, views, triggers, RLS policies, edge functions, or custom dataProvider methods that call Supabase APIs.
prisma-database
slashwhy
Prisma schema conventions, migrations, seeding, and query patterns. Use when modifying database schema, creating migrations, or writing complex queries.
efcore-migrations
thecaaz
**WORKFLOW SKILL** — EF Core migrations workflow for backend model changes: generate, review, and apply EF Core migrations using the repository's README guidance. Agents MUST NOT hand-edit migration code — migrations must be generated with the `dotnet ef migrations add` tool.
ck:databases
lengo0951
Design schemas, write queries for MongoDB and PostgreSQL. Use for database design, SQL/NoSQL queries, aggregation pipelines, indexes, migrations, replication, performance optimization, psql CLI.