feat(01-02): add /ready endpoint with database connectivity check

- Add GET /ready endpoint that checks database connection
- Returns 200 'ready' when connected, 503 when not
- Update Routes to require DataSource dependency
- Provide dataSourceLayer in Main.scala
- Use 'hikari' prefix for HikariCP config to avoid ZIO config conflict
This commit is contained in:
Jakub Zych
2026-02-04 22:20:35 +01:00
parent ddb1964d1a
commit 0059add4a1
5 changed files with 33 additions and 13 deletions

View File

@@ -5,7 +5,7 @@ server {
port = ${?SERVER_PORT} port = ${?SERVER_PORT}
} }
# Database configuration for ZIO config (used by AppConfig) # Database configuration for ZIO config (used by AppConfig.DatabaseConfig)
database { database {
host = "localhost" host = "localhost"
host = ${?DB_HOST} host = ${?DB_HOST}
@@ -20,8 +20,8 @@ database {
} }
# HikariCP configuration for Quill DataSource # HikariCP configuration for Quill DataSource
# Reads from "database" prefix via Quill.DataSource.fromPrefix # Uses separate prefix to avoid conflict with ZIO config "database" property
database { hikari {
dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource" dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
dataSource { dataSource {
serverName = "localhost" serverName = "localhost"

View File

@@ -4,6 +4,7 @@ import zio.config.typesafe.TypesafeConfigProvider
import api.Routes import api.Routes
import _root_.config.{AppConfig as SummerConfig} import _root_.config.{AppConfig as SummerConfig}
import db.QuillContext
object Main extends ZIOAppDefault { object Main extends ZIOAppDefault {
@@ -29,7 +30,11 @@ object Main extends ZIOAppDefault {
_ <- Console.printLine(banner) _ <- Console.printLine(banner)
_ <- Console.printLine(s" Starting on port ${cfg.server.port}...") _ <- Console.printLine(s" Starting on port ${cfg.server.port}...")
_ <- Console.printLine("") _ <- Console.printLine("")
_ <- Server.serve(Routes.routes).provide(Server.defaultWithPort(cfg.server.port)) // Note: Migrations are NOT auto-run. Use CLI to run migrations (Phase 5).
_ <- Server.serve(Routes.routes).provide(
Server.defaultWithPort(cfg.server.port),
QuillContext.dataSourceLayer
)
} yield () } yield ()
} }

View File

@@ -1,20 +1,33 @@
package api package api
import zio.*
import zio.http.* import zio.http.*
import javax.sql.DataSource
/** Health check endpoints /** Health check endpoints
* *
* Provides basic health check endpoint for load balancers and monitoring systems. The /health * Provides health check endpoints for load balancers and monitoring systems.
* endpoint returns 200 OK when the server is running.
* *
* Future endpoints: * Endpoints:
* - /ready will verify database connectivity * - GET /health - Returns 200 OK when server is running (liveness probe)
* - GET /ready - Returns 200 when database is connected, 503 when not (readiness probe)
*/ */
object HealthRoutes { object HealthRoutes {
val routes: Routes[Any, Response] = val routes: Routes[DataSource, Response] =
Routes( Routes(
Method.GET / "health" -> Handler.text("ok") // Liveness probe - always 200 if server is running
Method.GET / "health" -> Handler.text("ok"),
// Readiness probe - checks database connectivity
Method.GET / "ready" -> handler {
ZIO.serviceWithZIO[DataSource] { ds =>
ZIO.attempt {
val conn = ds.getConnection
conn.close()
}.as(Response.text("ready"))
.catchAll(_ => ZIO.succeed(Response.status(Status.ServiceUnavailable)))
}
}
) )
} }

View File

@@ -1,6 +1,7 @@
package api package api
import zio.http.* import zio.http.*
import javax.sql.DataSource
/** Route composition point /** Route composition point
* *
@@ -8,7 +9,7 @@ import zio.http.*
*/ */
object Routes { object Routes {
val routes: Routes[Any, Response] = val routes: Routes[DataSource, Response] =
HealthRoutes.routes HealthRoutes.routes
} }

View File

@@ -27,11 +27,12 @@ object QuillContext {
/** DataSource layer from HikariCP configuration /** DataSource layer from HikariCP configuration
* *
* Reads configuration from "database" prefix in application.conf. * Reads configuration from "hikari" prefix in application.conf.
* Uses separate prefix from "database" to avoid conflict with ZIO config.
* HikariCP manages the connection pool. * HikariCP manages the connection pool.
*/ */
val dataSourceLayer: ZLayer[Any, Throwable, DataSource] = val dataSourceLayer: ZLayer[Any, Throwable, DataSource] =
Quill.DataSource.fromPrefix("database") Quill.DataSource.fromPrefix("hikari")
/** Quill PostgreSQL context with snake_case naming /** Quill PostgreSQL context with snake_case naming
* *