--- title: Connect a Micronaut Kotlin application to Neon Postgres subtitle: Learn how to make server-side queries to Postgres from a Micronaut Kotlin application enableTableOfContents: true updatedOn: '2025-10-29T16:57:58.906Z' --- [Micronaut](https://micronaut.io/) is a modern, JVM-based, full-stack framework for building modular, easily testable microservice and serverless applications. This guide describes how to create a Neon Postgres database and connect to it from a Micronaut Kotlin application. The final application will expose REST endpoints to perform CRUD (Create, Read, Update, Delete) operations on a `book` table in your Neon database. To create a Neon project and access it from a Micronaut Kotlin application, you will: 1. [Create a Neon project](#create-a-neon-project) 2. [Create a Micronaut Kotlin project](#create-a-micronaut-kotlin-project) 3. [Configure your database connection](#configure-your-database-connection) 4. [Build the application components](#build-the-application-components) 5. [Run and test the application](#run-and-test-the-application) ## Create a Neon project If you do not have one already, create a Neon project. 1. Navigate to the [Projects](https://console.neon.tech/app/projects) page in the Neon Console. 2. Click **New Project**. 3. Specify your project settings and click **Create Project**. Save your connection details. You will need them in a later step. ## Create a Micronaut Kotlin project You can create a new Micronaut project using either the Micronaut CLI or the [Micronaut Launch](https://launch.micronaut.io/) website. For this guide, we will use the Micronaut CLI. > Install the Micronaut CLI by following the instructions in the [Micronaut documentation](https://micronaut.io/download/). You also need to have JDK 21 installed on your machine. Run the following command in your terminal. This command creates a new application and includes features for PostgreSQL connectivity, JDBC connection pooling (Hikari), database migrations (Flyway), data access, and YAML configuration. ```bash mn create-app with-micronaut-kotlin \ --lang=kotlin \ --jdk=21 \ --features=postgres,jdbc-hikari,flyway,data-jdbc,yaml ``` After creating the project, open the `build.gradle.kts` file and add the following configuration inside the `kotlin` block to ensure compatibility with JDK 21 and prevent potential build errors: ```kotlin // build.gradle.kts kotlin { jvmToolchain(21) } ``` ## Configure your database connection The project creation process generated a configuration file at `src/main/resources/application.yml`. You need to edit this file to add your Neon database credentials. Add the `url`, `username`, and `password` fields under the `datasources.default` section. Your updated `application.yml` file should look like this: ```yaml {8-10} # src/main/resources/application.yml micronaut: application: name: with-micronaut-kotlin datasources: default: url: 'jdbc:postgresql:///?sslmode=require&channelBinding=require' username: '' password: '' driver-class-name: org.postgresql.Driver db-type: postgres dialect: POSTGRES flyway: datasources: default: enabled: true ``` > Replace ``, ``, ``, and `` with your actual Neon database connection details you saved earlier. ## Build the application components Now you can create the components for a simple book inventory API: an entity, a repository, a controller, and a database migration script. ### 1. Create the database schema with Flyway Flyway handles database migrations automatically when the application starts. Create a SQL file at `src/main/resources/db/migration/V1__create_book_table.sql` to define your table schema and add some initial data. ```sql -- src/main/resources/db/migration/V1__create_book_table.sql CREATE TABLE IF NOT EXISTS book ( id SERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, author VARCHAR(255) NOT NULL ); INSERT INTO book (title, author) VALUES ('The Hobbit', 'J.R.R. Tolkien'); INSERT INTO book (title, author) VALUES ('1984', 'George Orwell'); ``` ### 2. Create the entity Create a data class that maps to the `book` table. The `@Serdeable` annotations are required for Micronaut to handle JSON serialization and deserialization for your API. ```kotlin // src/main/kotlin/com/example/entity/Book.kt package com.example.entity import io.micronaut.data.annotation.GeneratedValue import io.micronaut.data.annotation.Id import io.micronaut.data.annotation.MappedEntity import io.micronaut.serde.annotation.Serdeable @MappedEntity @Serdeable data class Book( @field:Id @field:GeneratedValue var id: Long? = null, var title: String, var author: String ) ``` ### 3. Create the repository Create a repository interface that extends `CrudRepository`. This interface provides CRUD operations for the `Book` entity. ```kotlin // src/main/kotlin/com/example/repository/BookRepository.kt package com.example.repository import com.example.entity.Book import io.micronaut.data.jdbc.annotation.JdbcRepository import io.micronaut.data.model.query.builder.sql.Dialect import io.micronaut.data.repository.CrudRepository @JdbcRepository(dialect = Dialect.POSTGRES) interface BookRepository : CrudRepository ``` ### 4. Create the controller Finally, create a controller to expose the REST endpoints for interacting with the books. ```kotlin // src/main/kotlin/com/example/controller/BookController.kt package com.example.controller import com.example.entity.Book import com.example.repository.BookRepository import io.micronaut.http.annotation.* import io.micronaut.scheduling.TaskExecutors import io.micronaut.scheduling.annotation.ExecuteOn @Controller("/books") class BookController(private val bookRepository: BookRepository) { @Get @ExecuteOn(TaskExecutors.IO) fun getAll(): List = bookRepository.findAll().toList() @Get("/{id}") @ExecuteOn(TaskExecutors.IO) fun getById(id: Long): Book? = bookRepository.findById(id).orElse(null) @Post @ExecuteOn(TaskExecutors.IO) fun save(@Body book: Book): Book = bookRepository.save(book) } ``` ## Run and test the application You are now ready to run your application. 1. Start the application using the Gradle wrapper: ```bash ./gradlew run ``` You should see output similar to the following: ```bash $ ./gradlew run [test-resources-service] 15:48:33.940 [main] INFO i.m.c.DefaultApplicationContext$RuntimeConfiguredEnvironment - Established active environments: [test] > Task :run __ __ _ _ | \/ (_) ___ _ __ ___ _ __ __ _ _ _| |_ | |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __| | | | | | (__| | | (_) | | | | (_| | |_| | |_ |_| |_|_|\___|_| \___/|_| |_|\__,_|\__,_|\__| 15:48:43.830 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... 15:48:45.974 [main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@30506c0d 15:48:45.975 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. 15:48:46.126 [main] INFO i.m.flyway.AbstractFlywayMigration - Running migrations for database with qualifier [default] 15:48:46.298 [main] INFO org.flywaydb.core.FlywayExecutor - Database: jdbc:postgresql://endpoint.neon.tech/examples?sslmode=require&channelBinding=require (PostgreSQL 17.5) 15:48:48.110 [main] INFO o.f.c.i.s.JdbcTableSchemaHistory - Schema history table "public"."flyway_schema_history" does not exist yetn 15:48:48.250 [main] INFO o.f.core.internal.command.DbValidate - Successfully validated 1 migration (execution time 00:00.432s) 15:48:49.524 [main] INFO o.f.c.i.s.JdbcTableSchemaHistory - Creating Schema History table "public"."flyway_schema_history" ... 15:48:51.817 [main] INFO o.f.core.internal.command.DbMigrate - Current version of schema "public": << Empty Schema >> 15:48:52.243 [main] INFO o.f.core.internal.command.DbMigrate - Migrating schema "public" to version "1 - create book table" 15:48:54.757 [main] INFO o.f.core.internal.command.DbMigrate - Successfully applied 1 migration to schema "public", now at version v1 (execution time 00:00.969s) 15:48:55.841 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 12788ms. Server Running: http://localhost:8080 :run <============-> 92% EXECUTING [38s] > :run > IDLE ``` The logs indicate the following sequence of events: - HikariCP initializes the connection pool to the Neon Postgres database. - Flyway checks the database schema and found that the `flyway_schema_history` table does not exist. - Flyway creates the `flyway_schema_history` table and applies the migrations present in the migration folder. - The `book` table is created as per the migration script (i.e., `V1__create_book_table.sql`). - The application starts successfully and is ready to handle requests. Now with the application running, you can test the API endpoints. 2. Test the API endpoints using `curl` or any API client: ```bash # Get all books curl http://localhost:8080/books # Expected Output: [{"id":1,"title":"The Hobbit","author":"J.R.R. Tolkien"},{"id":2,"title":"1984","author":"George Orwell"}] # Get a specific book by ID curl http://localhost:8080/books/1 # Expected Output: {"id":1,"title":"The Hobbit","author":"J.R.R. Tolkien"} # Create a new book curl -X POST \ -H "Content-Type: application/json" \ -d '{"title":"The Great Gatsby","author":"F. Scott Fitzgerald"}' \ http://localhost:8080/books # Expected Output: {"id":3,"title":"The Great Gatsby","author":"F. Scott Fitzgerald"} ``` You have successfully connected a Micronaut Kotlin application to your Neon Postgres database! ## Source code You can find the source code for the application described in this guide on GitHub. Get started with Micronaut Kotlin and Neon ## Resources - [Micronaut Documentation](https://docs.micronaut.io/) - [Micronaut API Reference](https://docs.micronaut.io/4.10.7/api/) - [Micronaut Schema Migration with Flyway](https://guides.micronaut.io/latest/micronaut-flyway-maven-java.html) - [Micronaut Data JDBC documentation](https://micronaut-projects.github.io/micronaut-data/latest/guide/index.html#jdbc) - [Micronaut Hikari JDBC Connection Pool documentation](https://micronaut-projects.github.io/micronaut-sql/latest/guide/index.html#jdbc) - [Flyway](https://www.red-gate.com/products/flyway/community/)