-
-
Notifications
You must be signed in to change notification settings - Fork 195
integrate database health check in server #581
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Added a `checkDatabaseHealth` function to the server routes for Elysia, Express, Fastify, Hono, and Next.js templates. This enhances the root endpoint to return the database health status, improving error handling and response accuracy.
WalkthroughRoot endpoints in multiple server templates now call a new async checkDatabaseHealth utility and return its JSON result; HTTP status is set to 500 when the health object contains an error. Two ORM-aware health-check template modules were added (server-base and Next.js). Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor C as Client
participant S as Server (Elysia/Express/Fastify/Hono/Next)
participant H as Health Module
participant D as DB/ORM
C->>S: GET /
S->>H: checkDatabaseHealth()
alt ORM configured
H->>D: Lightweight ping (SELECT 1 / $queryRaw / admin().ping())
D-->>H: OK / Error
H-->>S: {status:"OK", database:"connected"|"disconnected", error?}
else No ORM
H-->>S: {status:"OK", database:"none"}
end
alt health.error truthy
S-->>C: 500 JSON(health)
else
S-->>C: 200 JSON(health)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (1)**/*.{hbs,handlebars}📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)
Files:
🔇 Additional comments (2)
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (9)
apps/cli/templates/backend/server/express/src/index.ts.hbs (1)
116-119
: Prevent caching of health responses.Add a no‑store header to avoid stale health data from proxies/CDNs.
app.get("/", async (_req, res) => { const health = await checkDatabaseHealth(); const status = health.error ? 500 : 200; - res.status(status).json(health); + res.set("Cache-Control", "no-store").status(status).json(health); });apps/cli/templates/backend/server/fastify/src/index.ts.hbs (1)
173-179
: Make health responses non-cacheable.Set a
Cache-Control: no-store
header.fastify.get('/', async (request, reply) => { const health = await checkDatabaseHealth(); if (health.error) { reply.status(500); } - return health; + reply.header('Cache-Control', 'no-store'); + return health; });apps/cli/templates/backend/server/hono/src/index.ts.hbs (1)
149-152
: Disable caching of health responses.Add
Cache-Control: no-store
to avoid stale results.app.get("/", async (c) => { const health = await checkDatabaseHealth(); const status = health.error ? 500 : 200; - return c.json(health, status); + c.header("Cache-Control", "no-store"); + return c.json(health, status); });apps/cli/templates/backend/server/server-base/src/lib/health.ts.hbs (3)
12-12
: Avoid explicit return types per guidelines.Let TS infer the Promise type; keeps the template cleaner.
-export async function checkDatabaseHealth(): Promise<{ status: string; database: string; error?: string }> { +export async function checkDatabaseHealth() {
27-35
: Report a non-OK status on failure.Returning
status: "OK"
withdatabase: "disconnected"
is misleading. Use an error status.- return { - status: "OK", + return { + status: "ERROR", database: "disconnected", error: error instanceof Error ? error.message : String(error) };
38-40
: Avoid explicit return types in the no‑ORM branch.Let inference handle it.
-export async function checkDatabaseHealth(): Promise<{ status: string; database: string }> { +export async function checkDatabaseHealth() { return { status: "OK", database: "none" }; }apps/cli/templates/backend/server/next/src/app/route.ts (1)
2-7
: Named import is correct; optionally add no-store cache header to the health responseapps/cli/templates/backend/server/next/src/lib/health.ts.hbs exports checkDatabaseHealth as a named function (lines 12 & 38), so the current named import is correct.
File: apps/cli/templates/backend/server/next/src/app/route.ts
@@ - return NextResponse.json(health, { status }); + return NextResponse.json(health, { status, headers: { "Cache-Control": "no-store" } });apps/cli/templates/backend/server/next/src/lib/health.ts.hbs (2)
38-40
: Unify the return type across variants for simpler consumers.The no‑ORM branch omits
error?: string
, creating two shapes. Keep a consistent type (even iferror
is absent).Apply this diff:
-export async function checkDatabaseHealth(): Promise<{ status: string; database: string }> { +export async function checkDatabaseHealth(): Promise<{ status: string; database: string; error?: string }> { return { status: "OK", database: "none" }; }
28-29
: Consider avoiding raw error logs in production.Use a structured logger and suppress PII. At minimum, gate verbose errors behind
NODE_ENV !== "production"
.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/cli/templates/backend/server/elysia/src/index.ts.hbs
(2 hunks)apps/cli/templates/backend/server/express/src/index.ts.hbs
(2 hunks)apps/cli/templates/backend/server/fastify/src/index.ts.hbs
(2 hunks)apps/cli/templates/backend/server/hono/src/index.ts.hbs
(2 hunks)apps/cli/templates/backend/server/next/src/app/route.ts
(1 hunks)apps/cli/templates/backend/server/next/src/lib/health.ts.hbs
(1 hunks)apps/cli/templates/backend/server/server-base/src/lib/health.ts.hbs
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
**/*.{ts,tsx}
: Use Id from './_generated/dataModel' to type document ids (e.g., Id<'users'>)
Ensure Record key/value types align with validators (e.g., v.record(v.id('users'), v.string()) => Record<Id<'users'>, string>)
Be strict with types for document ids; prefer Id<'table'> over string
Use 'as const' for string literals in discriminated unions
When using Array and Record types, declare with explicit generic types (e.g., const arr: Array = ...)
**/*.{ts,tsx}
: Use TypeScript type aliases instead of interface declarations.
Do not use explicit return types in TypeScript.
Files:
apps/cli/templates/backend/server/next/src/app/route.ts
**/*.{js,jsx,ts,tsx,mjs,cjs}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
**/*.{js,jsx,ts,tsx,mjs,cjs}
: Do not use dotenv; Bun auto-loads .env
UseBun.serve()
for HTTP/WebSockets; do not useexpress
Usebun:sqlite
for SQLite; do not usebetter-sqlite3
UseBun.redis
for Redis; do not useioredis
UseBun.sql
for Postgres; do not usepg
orpostgres.js
Use built-inWebSocket
; do not usews
PreferBun.file
overnode:fs
readFile/writeFile
UseBun.$
instead ofexeca
for shelling out
Files:
apps/cli/templates/backend/server/next/src/app/route.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)
Define functions using the standard function declaration syntax, not arrow functions.
Files:
apps/cli/templates/backend/server/next/src/app/route.ts
**/*.{hbs,handlebars}
📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)
In Handlebars templates, avoid generic if/else blocks; write explicit conditions (e.g., if (eq orm "prisma") and else if (eq orm "drizzle")).
Files:
apps/cli/templates/backend/server/elysia/src/index.ts.hbs
apps/cli/templates/backend/server/express/src/index.ts.hbs
apps/cli/templates/backend/server/server-base/src/lib/health.ts.hbs
apps/cli/templates/backend/server/next/src/lib/health.ts.hbs
apps/cli/templates/backend/server/fastify/src/index.ts.hbs
apps/cli/templates/backend/server/hono/src/index.ts.hbs
🔇 Additional comments (9)
apps/cli/templates/backend/server/express/src/index.ts.hbs (1)
28-28
: LGTM on importing the health helper.apps/cli/templates/backend/server/fastify/src/index.ts.hbs (1)
33-33
: LGTM on importing the health helper.apps/cli/templates/backend/server/hono/src/index.ts.hbs (1)
24-24
: LGTM on importing the health helper.apps/cli/templates/backend/server/elysia/src/index.ts.hbs (1)
28-28
: Add/generate ./lib/health export for Elysia (checkDatabaseHealth)apps/cli/templates/backend/server/elysia/src/index.ts.hbs imports the named export checkDatabaseHealth from "./lib/health" (line 28) but there is no elysia src/lib/health.ts.hbs; implementations exist at apps/cli/templates/backend/server/next/src/lib/health.ts.hbs and apps/cli/templates/backend/server/server-base/src/lib/health.ts.hbs — add a lib/health that exports checkDatabaseHealth or update the import to match.
apps/cli/templates/backend/server/server-base/src/lib/health.ts.hbs (2)
15-21
: Confirm Drizzle dialect methods; prefer sql-tagged queries.Could not locate the Drizzle db factory in the repo — manually verify whether the sqlite client exposes db.get. If it does not, apply the change below.
- {{#if (eq database "sqlite")}} - await db.get("SELECT 1"); + {{#if (eq database "sqlite")}} + await db.execute(sql`SELECT 1`);{{#if (eq orm "drizzle")}} import { db } from "../db"; +import { sql } from "drizzle-orm"; {{/if}}
24-26
: Validate the Mongo/Mongoose ping path.health.ts.hbs (server-base & next) imports
{ client } from "../db"
and callsawait client.admin().ping()
— that matches the native MongoClient API. Mongoose health checks should usemongoose.connection.db.admin().ping()
ormongoose.connection.db.command({ ping: 1 })
, unless../db
actually exports a MongoClient instance namedclient
. I couldn't locate the../db
export in the templates; confirm what the Mongoose template exports. Then either (a) update the health check to usemongoose.connection.db...
or (b) have../db
export a MongoClientclient
soclient.admin().ping()
is valid.apps/cli/templates/backend/server/next/src/lib/health.ts.hbs (3)
27-35
: Return a non-OK status on DB check failure.Returning
{ status: "OK", database: "disconnected" }
misleads downstream health handlers. Use "ERROR" (or "DEGRADED" if you prefer) when the ping fails.[ suggest_recommended_refactor ]
Apply this diff:
- return { - status: "OK", - database: "disconnected", - error: error instanceof Error ? error.message : String(error) - }; + return { + status: "ERROR", + database: "disconnected", + error: error instanceof Error ? error.message : String(error) + };
25-25
: Verify whatclient
is in the Mongoose template.File: apps/cli/templates/backend/server/next/src/lib/health.ts.hbs (around line 25) currently uses:
await client.admin().ping();
If
client
is a MongoDB MongoClient, use:
await client.db().command({ ping: 1 })
(orawait client.db().admin().ping()
).If
client
is a Mongoose connection, use:
await mongoose.connection.db.command({ ping: 1 })
(orawait mongoose.connection.db.admin().ping()
).Repository search returned no results for the DB template export — confirm which object
client
refers to (or paste the DB template/export) so this can be verified.
15-21
: ```shell
#!/bin/bash
set -euo pipefailecho "PWD: $(pwd)"
echo "---- find health.ts.hbs ----"
rg -n --hidden -S 'health.ts.hbs' || trueTEMPLATE="apps/cli/templates/backend/server/next/src/lib/health.ts.hbs"
if [ -f "$TEMPLATE" ]; then
echo "---- $TEMPLATE ----"
sed -n '1,240p' "$TEMPLATE"
else
echo "$TEMPLATE not found; searching for any health..hbs under apps/cli..."
rg -n --hidden -S 'health..hbs' apps/cli || true
fiecho
echo "---- occurrences of 'database' in apps/cli ----"
rg -n --hidden -S --glob 'apps/cli/**' '\bdatabase\b' -C2 || trueecho
echo "---- occurrences of sqlite/postgres/mysql/postgresql/libsql in apps/cli ----"
rg -n --hidden -S --glob 'apps/cli/**' 'sqlite|postgresql|postgres|mysql|libsql' -C2 || trueecho
echo "---- ProjectConfig / type definitions in apps/cli ----"
rg -n --hidden -S --glob 'apps/cli/**' 'type ProjectConfig|interface ProjectConfig|export type ProjectConfig' -C3 || trueecho
echo "---- templates using 'eq database' ----"
rg -n --hidden -S --glob 'apps/cli/templates/**' 'eq database' -C2 || trueecho
echo "---- templates referencing drizzle/sql/db.get/db.execute ----"
rg -n --hidden -S --glob 'apps/cli/templates/**' 'drizzle|drizzle-orm|sql`|db.get|db.execute' -C2 || trueecho
echo "---- done ----"</blockquote></details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
{{#if orm}} | ||
{{#if (eq orm "drizzle")}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace the top-level generic Handlebars if
with explicit eq
branches to avoid false “connected” on unknown ORMs.
As written, any truthy orm
value enters the main branch; if it’s an unsupported value, none of the inner checks run, yet the function still returns { status: "OK", database: "connected" }
. Also, coding guidelines discourage generic if/else in templates.
Refactor to an explicit eq
chain and unify the function/type shape across variants.
Apply this diff:
-{{#if orm}}
-{{#if (eq orm "drizzle")}}
-import { db } from "../../db";
-{{/if}}
-{{#if (eq orm "prisma")}}
-import prisma from "../../db";
-{{/if}}
-{{#if (eq orm "mongoose")}}
-import { client } from "../../db";
-{{/if}}
-
-export async function checkDatabaseHealth(): Promise<{ status: string; database: string; error?: string }> {
- try {
- {{#if (eq orm "drizzle")}}
- {{#if (eq database "sqlite")}}
- await db.get("SELECT 1");
- {{else if (eq database "postgres")}}
- await db.execute("SELECT 1");
- {{else if (eq database "mysql")}}
- await db.execute("SELECT 1");
- {{/if}}
- {{else if (eq orm "prisma")}}
- await prisma.$queryRaw`SELECT 1`;
- {{else if (eq orm "mongoose")}}
- await client.admin().ping();
- {{/if}}
- return { status: "OK", database: "connected" };
- } catch (error) {
- console.error("Database connection error:", error);
- return {
- status: "OK",
- database: "disconnected",
- error: error instanceof Error ? error.message : String(error)
- };
- }
-}
-{{else}}
-export async function checkDatabaseHealth(): Promise<{ status: string; database: string }> {
- return { status: "OK", database: "none" };
-}
-{{/if}}
+{{!-- Keep a consistent shape across all variants --}}
+type Health = {
+ status: "OK" | "ERROR";
+ database: "connected" | "disconnected" | "none";
+ error?: string;
+};
+
+{{#if (eq orm "drizzle")}}
+import { db } from "../../db";
+import { sql } from "drizzle-orm";
+
+export async function checkDatabaseHealth(): Promise<Health> {
+ try {
+ {{#if (eq database "sqlite")}}
+ await db.get(sql`SELECT 1`);
+ {{else if (eq database "postgres")}}
+ await db.execute(sql`SELECT 1`);
+ {{else if (eq database "mysql")}}
+ await db.execute(sql`SELECT 1`);
+ {{/if}}
+ return { status: "OK", database: "connected" };
+ } catch (error) {
+ console.error("Database connection error:", error);
+ return {
+ status: "ERROR",
+ database: "disconnected",
+ error: error instanceof Error ? error.message : String(error),
+ };
+ }
+}
+{{else if (eq orm "prisma")}}
+import prisma from "../../db";
+
+export async function checkDatabaseHealth(): Promise<Health> {
+ try {
+ await prisma.$queryRaw`SELECT 1`;
+ return { status: "OK", database: "connected" };
+ } catch (error) {
+ console.error("Database connection error:", error);
+ return {
+ status: "ERROR",
+ database: "disconnected",
+ error: error instanceof Error ? error.message : String(error),
+ };
+ }
+}
+{{else if (eq orm "mongoose")}}
+import { client } from "../../db";
+
+export async function checkDatabaseHealth(): Promise<Health> {
+ try {
+ await client.admin().ping();
+ return { status: "OK", database: "connected" };
+ } catch (error) {
+ console.error("Database connection error:", error);
+ return {
+ status: "ERROR",
+ database: "disconnected",
+ error: error instanceof Error ? error.message : String(error),
+ };
+ }
+}
+{{else}}
+export async function checkDatabaseHealth(): Promise<Health> {
+ return { status: "OK", database: "none" };
+}
+{{/if}}
Also applies to: 37-41
🤖 Prompt for AI Agents
In apps/cli/templates/backend/server/next/src/lib/health.ts.hbs around lines 1-2
and 37-41, the template uses a top-level generic Handlebars {{#if orm}} which
allows any truthy orm value (including unsupported ones) to enter the branch and
produce a misleading `{ status: "OK", database: "connected" }`; replace the
generic if with explicit equality branches (e.g., {{#if (eq orm "drizzle")}}
{{else if (eq orm "prisma")}} ... {{else}}) so each supported ORM has its own
branch and the fallback returns a consistent shape indicating unknown/disabled
DB, and ensure the exported function/type shape is unified across all branches
(same return object keys) to avoid mismatches.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
889c085
to
b167db0
Compare
Added a
checkDatabaseHealth
function to the server routes for Elysia, Express, Fastify, Hono, and Next.js templates. This enhances the root endpoint to return the database health status, improving error handling and response accuracy.Summary by CodeRabbit