From 781165170987a95e162986ea130280191e8b6712 Mon Sep 17 00:00:00 2001
From: Ole Langbehn <ole.langbehn@inoio.de>
Date: Mon, 2 Dec 2024 19:57:43 +0100
Subject: [PATCH] feat: add /health, /ready endpoints, define readiness and
 liveness check

---
 app/goodnews.ts                      |  5 ++---
 app/server.ts                        | 16 ++++++++++++----
 deno.json                            |  2 +-
 terraform/environments/deployment.tf | 15 +++++++++++++++
 4 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/app/goodnews.ts b/app/goodnews.ts
index bb364a1..499a301 100644
--- a/app/goodnews.ts
+++ b/app/goodnews.ts
@@ -103,9 +103,8 @@ const fetchPage =
       const fetchResult = await fetch(url)
       const resultJson = await fetchResult.json()
       return resultJson
-      // deno-lint-ignore no-explicit-any
-    } catch (err: any) {
-      logger.error('fetching articles failed: ' + err.message)
+    } catch (err) {
+      logger.error('fetching articles failed:', err as Error)
       throw err
     } finally {
       const duration = Date.now() - start
diff --git a/app/server.ts b/app/server.ts
index fdc5ea7..dcf2436 100644
--- a/app/server.ts
+++ b/app/server.ts
@@ -1,5 +1,5 @@
 import { GoodNewsArticleQueryResponse, GoodNewsLanguage } from './api_types.ts'
-import { createSchema, createYoga, gql, serve, useResponseCache } from './deps.ts'
+import { createSchema, createYoga, gql, useResponseCache } from './deps.ts'
 import { DEFAULT_LANGUAGE, executeArticlesQuery, SUPPORTED_LANGUAGES } from './goodnews.ts'
 import { logger } from './logging.ts'
 
@@ -94,6 +94,8 @@ export interface ServerConfig {
   contentful: ContentfulConfig
 }
 
+const isAlive = () => new Response(`${true}`)
+
 export const createGraphQLServer = (config: ServerConfig): GraphQLServer => {
   const plugins = config.cacheEnabled || DEFAULT_CACHE_ENABLED
     ? [
@@ -128,17 +130,23 @@ export type GraphQLContext = {
   }
 }
 
-export const startServer = (config: ServerConfig): Promise<void> => {
+export const startServer = (config: ServerConfig): Deno.HttpServer<Deno.NetAddr> => {
   const graphQLServer: GraphQLServer = createGraphQLServer(config)
-  return serve(graphQLServer, {
+  return Deno.serve({
     port: config.port || DEFAULT_PORT,
+    handler: (req: Request) => {
+      const url = new URL(req.url)
+      const pathname = url.pathname
+      if (pathname.startsWith('/health') || pathname.startsWith('/ready')) return isAlive()
+      return graphQLServer.handleRequest(req)
+    },
     onListen({ port, hostname }) {
       logger.info(
         `Server started at http://${hostname === '0.0.0.0' ? 'localhost' : hostname}:${port}/graphql`,
       )
       if (config.contentful.fake) {
         logger.info(
-          `Server is serving fake data due to FAKE env var set to true`,
+          'Server is serving fake data due to FAKE env var set to true',
         )
       }
     },
diff --git a/deno.json b/deno.json
index 0952ad5..5697d87 100644
--- a/deno.json
+++ b/deno.json
@@ -4,7 +4,7 @@
       "lint": "deno lint",
       "fmt": "deno fmt",
       "fmt:check": "deno fmt --check",
-      "test": "deno test --allow-import ",
+      "test": "deno test --allow-import",
       "updateDeps": "deno cache --allow-import --lock=deno.lock --lock-write app/deps.ts app/dev_deps.ts",
       "install": "deno cache --allow-import --reload --lock=deno.lock app/deps.ts app/dev_deps.ts",
       "cache": "deno cache --allow-import app/main.ts",
diff --git a/terraform/environments/deployment.tf b/terraform/environments/deployment.tf
index a728e61..585cad1 100644
--- a/terraform/environments/deployment.tf
+++ b/terraform/environments/deployment.tf
@@ -44,6 +44,21 @@ resource "google_cloud_run_service" "goodnews_api" {
         ports {
           container_port = 8002
         }
+        startup_probe {
+          http_get {
+            path = "/ready"
+          }
+          timeout_seconds   = 10
+          period_seconds    = 10
+          failure_threshold = 6
+        }
+        liveness_probe {
+          http_get {
+            path = "/health"
+          }
+          timeout_seconds = 20
+          period_seconds  = 60
+        }
         env {
           name  = "ENVIRONMENT"
           value = local.environment
-- 
GitLab