Skip to content

fix: ensure tags tables exist at startup#87

Merged
bd73-com merged 2 commits intomainfrom
claude/fix-critical-bug-aBABg
Mar 6, 2026
Merged

fix: ensure tags tables exist at startup#87
bd73-com merged 2 commits intomainfrom
claude/fix-critical-bug-aBABg

Conversation

@bd73-com
Copy link
Owner

@bd73-com bd73-com commented Mar 6, 2026

Summary

PR #86 ("Add monitor organisation with tags") introduced tags and monitor_tags database tables but did not add the corresponding startup migration to create them. On deployments where drizzle-kit push hasn't been re-run, getMonitorsWithTags() queries non-existent tables, causing /api/monitors to return a 500 error. This completely breaks the dashboard — users see "Error Loading Dashboard / Failed to fetch monitors".

Changes

Server startup migration (server/services/ensureTables.ts):

  • Added ensureTagTables() function that issues CREATE TABLE IF NOT EXISTS for both tags and monitor_tags, along with their indexes (tags_user_idx, tags_user_name_lower_uniq, monitor_tags_monitor_tag_uniq)
  • Follows the exact same pattern used by ensureApiKeysTable() and ensureChannelTables()

Route registration (server/routes.ts):

  • Imported and called ensureTagTables() at startup alongside the other ensure* functions

Test update (server/routes.migration.test.ts):

  • Updated expected db.execute call count from 11 to 16 (5 new DDL statements for tag tables)
  • Added assertions verifying tags and monitor_tags DDL is issued

How to test

  1. Run npm run test — all 1123 tests should pass
  2. Run npm run check — no new TypeScript errors (pre-existing scraper.ts errors are unrelated)
  3. Deploy to an environment where the tags/monitor_tags tables don't exist yet
  4. Verify the dashboard loads successfully and monitors are displayed
  5. Verify tag functionality (create tags, assign to monitors) works correctly

https://claude.ai/code/session_012qN2riMykNDMYS26j3TJkY

Summary by CodeRabbit

  • New Features

    • Added infrastructure for a tag management system so users can create colored tags and attach them to monitored items for improved organization and filtering.
    • App now ensures tag-related data structures are prepared at startup, reducing errors on first use.
  • Tests

    • Expanded migration tests to validate proper initialization of tag-related database components.

The tags feature (PR #86) added new tables but missed the startup
ensureTable migration, causing getMonitorsWithTags() to fail with a
SQL error on databases that haven't run drizzle-kit push.

https://claude.ai/code/session_012qN2riMykNDMYS26j3TJkY
@github-actions github-actions bot added the fix label Mar 6, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 87c48789-2cb0-4ea2-aab4-ed86c5f3bac1

📥 Commits

Reviewing files that changed from the base of the PR and between 5fb44b3 and b4be402.

📒 Files selected for processing (1)
  • server/routes.migration.test.ts

📝 Walkthrough

Walkthrough

Adds a new ensureTagTables() function that creates tags and monitor_tags tables and indices, invokes it during route registration, and updates migration tests to expect additional DDL executions and assert the new CREATE TABLE statements.

Changes

Cohort / File(s) Summary
Table bootstrap & service
server/services/ensureTables.ts
New exported ensureTagTables(): Promise<void> that runs CREATE TABLE IF NOT EXISTS for tags and monitor_tags, creates indexes and FK constraints, and logs errors on failure.
Route integration
server/routes.ts
Imported and invoked ensureTagTables() during route registration to bootstrap tag-related schema at startup.
Migration tests
server/routes.migration.test.ts
Increased expected db.execute calls (11 → 16) and added assertions that executed SQL includes CREATE TABLE IF NOT EXISTS tags and monitor_tags; duplicated insertion of the same test block is present in the diff.

Sequence Diagram(s)

sequenceDiagram
    participant Router as Router/registerRoutes
    participant Ensure as ensureTagTables
    participant DB as Database

    Router->>Ensure: call ensureTagTables()
    Ensure->>DB: execute CREATE TABLE IF NOT EXISTS tags...
    DB-->>Ensure: success / error
    Ensure->>DB: execute CREATE TABLE IF NOT EXISTS monitor_tags...
    DB-->>Ensure: success / error
    Ensure-->>Router: resolves (or logs error)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Security note: runtime schema creation requires DB user privileges; confirm least-privilege policies and consider idempotency/race conditions when multiple instances start concurrently.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: ensuring tag tables are created at startup to fix a critical bug where missing tables cause 500 errors.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/fix-critical-bug-aBABg

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/routes.migration.test.ts (1)

170-189: ⚠️ Potential issue | 🟡 Minor

Make the new tag migration assertions specific.

includes("tags") is too weak here because every monitor_tags statement also satisfies it. This test can still pass if the standalone tags DDL disappears, and it won't catch drift on required columns/indexes like name_lower, colour, or tags_user_name_lower_uniq.

Suggested fix
-    expect(callStrings.some((s: string) => s.includes("tags"))).toBe(true);
-    expect(callStrings.some((s: string) => s.includes("monitor_tags"))).toBe(true);
+    const tagsCreate = callStrings.find((s: string) =>
+      s.includes("CREATE TABLE IF NOT EXISTS tags")
+    );
+    expect(tagsCreate).toBeDefined();
+    expect(tagsCreate).toContain("user_id");
+    expect(tagsCreate).toContain("name");
+    expect(tagsCreate).toContain("name_lower");
+    expect(tagsCreate).toContain("colour");
+    expect(callStrings.some((s: string) => s.includes("tags_user_idx"))).toBe(true);
+    expect(callStrings.some((s: string) => s.includes("tags_user_name_lower_uniq"))).toBe(true);
+
+    const monitorTagsCreate = callStrings.find((s: string) =>
+      s.includes("CREATE TABLE IF NOT EXISTS monitor_tags")
+    );
+    expect(monitorTagsCreate).toBeDefined();
+    expect(monitorTagsCreate).toContain("monitor_id");
+    expect(monitorTagsCreate).toContain("tag_id");
+    expect(monitorTagsCreate).toContain("ON DELETE CASCADE");
+    expect(callStrings.some((s: string) => s.includes("monitor_tags_monitor_tag_uniq"))).toBe(true);

As per coding guidelines, **/*.test.ts: Assertions are specific (not just truthy/falsy).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/routes.migration.test.ts` around lines 170 - 189, The test currently
uses expect(callStrings.some(s => s.includes("tags"))) which is too broad and
matches "monitor_tags"; update the assertions that inspect callStrings (derived
from mockDbExecute.mock.calls) to specifically look for the standalone tags
table and its required columns/indexes: assert a call contains "CREATE TABLE
tags" (or a word-boundary match for "tags"), and separately assert the DDL
includes the specific column names "name_lower" and "colour" and the unique
index name "tags_user_name_lower_uniq" (keep existing checks for "monitor_tags"
untouched); use explicit expect(callStrings.some(s => s.includes("CREATE TABLE
tags"))) and expect(callStrings.some(s => s.includes("name_lower"))) etc., so
the test fails if the standalone tags DDL or required columns/indexes are
removed or changed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/services/ensureTables.ts`:
- Around line 104-130: ensureTagTables currently catches errors and only logs
them, allowing startup to proceed despite failed DDL; change ensureTagTables
(the function in server/services/ensureTables.ts) to rethrow the caught error
(or remove the try/catch) so failures propagate to the caller (e.g.,
server/routes.ts) and prevent mounting tag/monitor routes when the tables
couldn't be created; ensure you preserve the logging but then throw the original
error (or a wrapped Error) after logging.

---

Outside diff comments:
In `@server/routes.migration.test.ts`:
- Around line 170-189: The test currently uses expect(callStrings.some(s =>
s.includes("tags"))) which is too broad and matches "monitor_tags"; update the
assertions that inspect callStrings (derived from mockDbExecute.mock.calls) to
specifically look for the standalone tags table and its required
columns/indexes: assert a call contains "CREATE TABLE tags" (or a word-boundary
match for "tags"), and separately assert the DDL includes the specific column
names "name_lower" and "colour" and the unique index name
"tags_user_name_lower_uniq" (keep existing checks for "monitor_tags" untouched);
use explicit expect(callStrings.some(s => s.includes("CREATE TABLE tags"))) and
expect(callStrings.some(s => s.includes("name_lower"))) etc., so the test fails
if the standalone tags DDL or required columns/indexes are removed or changed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 73cc6d2f-a79b-4be9-82bc-07b96902bed0

📥 Commits

Reviewing files that changed from the base of the PR and between eb61a78 and 5fb44b3.

📒 Files selected for processing (3)
  • server/routes.migration.test.ts
  • server/routes.ts
  • server/services/ensureTables.ts

Comment on lines +104 to +130
export async function ensureTagTables(): Promise<void> {
try {
await db.execute(sql`
CREATE TABLE IF NOT EXISTS tags (
id SERIAL PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id),
name TEXT NOT NULL,
name_lower TEXT NOT NULL,
colour TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
)
`);
await db.execute(sql`CREATE INDEX IF NOT EXISTS tags_user_idx ON tags(user_id)`);
await db.execute(sql`CREATE UNIQUE INDEX IF NOT EXISTS tags_user_name_lower_uniq ON tags(user_id, name_lower)`);

await db.execute(sql`
CREATE TABLE IF NOT EXISTS monitor_tags (
id SERIAL PRIMARY KEY,
monitor_id INTEGER NOT NULL REFERENCES monitors(id) ON DELETE CASCADE,
tag_id INTEGER NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
)
`);
await db.execute(sql`CREATE UNIQUE INDEX IF NOT EXISTS monitor_tags_monitor_tag_uniq ON monitor_tags(monitor_id, tag_id)`);
} catch (e) {
console.error("Could not ensure tag tables:", e);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Propagate tag-table bootstrap failure instead of only logging it.

This helper always resolves successfully, so server/routes.ts continues mounting /api/monitors and the tag routes even when the DDL fails. In read-only or permission-constrained deployments, that leaves the original missing-table 500 path intact.

Suggested fix
-export async function ensureTagTables(): Promise<void> {
+export async function ensureTagTables(): Promise<boolean> {
   try {
     await db.execute(sql`
       CREATE TABLE IF NOT EXISTS tags (
         id SERIAL PRIMARY KEY,
         user_id TEXT NOT NULL REFERENCES users(id),
         name TEXT NOT NULL,
         name_lower TEXT NOT NULL,
         colour TEXT NOT NULL,
         created_at TIMESTAMP NOT NULL DEFAULT NOW()
       )
     `);
     await db.execute(sql`CREATE INDEX IF NOT EXISTS tags_user_idx ON tags(user_id)`);
     await db.execute(sql`CREATE UNIQUE INDEX IF NOT EXISTS tags_user_name_lower_uniq ON tags(user_id, name_lower)`);

     await db.execute(sql`
       CREATE TABLE IF NOT EXISTS monitor_tags (
         id SERIAL PRIMARY KEY,
         monitor_id INTEGER NOT NULL REFERENCES monitors(id) ON DELETE CASCADE,
         tag_id INTEGER NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
         created_at TIMESTAMP NOT NULL DEFAULT NOW()
       )
     `);
     await db.execute(sql`CREATE UNIQUE INDEX IF NOT EXISTS monitor_tags_monitor_tag_uniq ON monitor_tags(monitor_id, tag_id)`);
+    return true;
   } catch (e) {
     console.error("Could not ensure tag tables:", e);
+    return false;
   }
 }
-  await ensureTagTables();
+  const tagTablesReady = await ensureTagTables();
+  if (!tagTablesReady) {
+    throw new Error("Tag tables are unavailable");
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/services/ensureTables.ts` around lines 104 - 130, ensureTagTables
currently catches errors and only logs them, allowing startup to proceed despite
failed DDL; change ensureTagTables (the function in
server/services/ensureTables.ts) to rethrow the caught error (or remove the
try/catch) so failures propagate to the caller (e.g., server/routes.ts) and
prevent mounting tag/monitor routes when the tables couldn't be created; ensure
you preserve the logging but then throw the original error (or a wrapped Error)
after logging.

Address CodeRabbit feedback: replace weak `includes("tags")` assertion
(which also matches "monitor_tags") with `includes("CREATE TABLE IF NOT
EXISTS tags")`. Add dedicated test for tag table DDL columns, indexes,
and constraints, following the pattern of existing table tests.

https://claude.ai/code/session_012qN2riMykNDMYS26j3TJkY
@bd73-com bd73-com merged commit ff1b7f0 into main Mar 6, 2026
1 of 2 checks passed
@bd73-com bd73-com deleted the claude/fix-critical-bug-aBABg branch March 6, 2026 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants