Skip to content

Annotate Server Helper Functions for Clarity#57

Merged
christianhelp merged 5 commits intomainfrom
52-annotate-server-helper-functions-for-clarity
Feb 21, 2026
Merged

Annotate Server Helper Functions for Clarity#57
christianhelp merged 5 commits intomainfrom
52-annotate-server-helper-functions-for-clarity

Conversation

@christianhelp
Copy link
Contributor

@christianhelp christianhelp commented Feb 21, 2026

Satisfies

#52

Summary by CodeRabbit

  • New Features

    • Enhanced user authentication and session management capabilities
    • Improved team member access controls and join request workflows
  • Chores

    • Strengthened logging and error tracking infrastructure
    • Cleaned up internal utilities and improved code documentation

@christianhelp christianhelp linked an issue Feb 21, 2026 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Feb 21, 2026

Warning

Rate limit exceeded

@christianhelp has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 4 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This PR refactors API utility functions by removing legacy database operations (getDatabaseDumpTurso, pingDatabase) and introducing new team data-access helpers, permission checking, and logging utilities. Two new middleware functions are added to handle user session context and authentication enforcement, along with a development mode detection utility.

Changes

Cohort / File(s) Summary
Database Utilities Refactor
apps/api/src/lib/functions/database.ts
Removed legacy database dump/ping functions. Added public data-access helpers for teams (findTeam, leaveTeam, getAdminUserForTeam, getJoinTeamRequest variants). Introduced isUserSiteAdminOrQueryHasPermissions for authorization short-circuiting. Expanded logging with logError, logInfo, logWarning, logToDb functions and contextual data extraction utilities.
Middleware Enhancement
apps/api/src/lib/functions/middleware.ts
Added setUserSessionContextMiddleware to extract session from auth, set context values (requestId, user, session, teamId), and log requests. Added authenticatedMiddleware to enforce authentication on protected routes, bypassing checks for public routes and returning 401 for unauthorized access.
Utilities Addition
apps/api/src/lib/functions/index.ts
Added isInDevMode() utility function to detect development environment. Updated documentation for HonoBetterAuth function.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant setUserSessionContextMiddleware
    participant Auth as Auth API
    participant authenticatedMiddleware
    participant Route as Route Handler
    participant Logger as Logger

    Client->>setUserSessionContextMiddleware: HTTP Request
    setUserSessionContextMiddleware->>Auth: getSession(headers)
    Auth-->>setUserSessionContextMiddleware: session/null
    setUserSessionContextMiddleware->>setUserSessionContextMiddleware: Generate requestId
    setUserSessionContextMiddleware->>setUserSessionContextMiddleware: Set Context values
    setUserSessionContextMiddleware->>Logger: Log request path & user
    setUserSessionContextMiddleware->>authenticatedMiddleware: next()
    
    alt Public Route
        authenticatedMiddleware->>Route: next()
        Route-->>Client: Response
    else Protected Route & Authenticated
        authenticatedMiddleware->>authenticatedMiddleware: Verify (user, session)
        authenticatedMiddleware->>Route: next()
        Route-->>Client: Response
    else Protected Route & Unauthenticated
        authenticatedMiddleware->>Logger: Log unauthorized access
        authenticatedMiddleware-->>Client: 401 NOT_AUTHENTICATED
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Rework API Responses #41 — Makes overlapping changes to database.ts (logging, permission helpers) and middleware.ts (authentication behavior) within the same API utilities module.

Suggested reviewers

  • mjanderson1227

Poem

🐰 Hops through the code with glee,
New middleware flows so free,
Sessions tracked, permissions checked,
Auth flows perfectly aligned and decked!
Logging whispers through the night,
API utilities shining bright!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Annotate Server Helper Functions for Clarity' is vague and generic. While annotations/documentation updates are part of the changes, the PR primarily adds new functions (findTeam, leaveTeam, logging functions, middleware), removes old functions (getDatabaseDumpTurso, pingDatabase), and restructures helper utilities—not simply annotating existing functions. Revise the title to reflect the main changes more specifically, such as 'Add data-access helpers and logging utilities for teams' or 'Refactor server functions: add helpers and middleware, remove legacy database functions'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 52-annotate-server-helper-functions-for-clarity

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.

@christianhelp christianhelp marked this pull request as ready for review February 21, 2026 21:32
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 (5)
apps/api/src/lib/functions/middleware.ts (4)

70-75: ⚠️ Potential issue | 🟡 Minor

Debug console.log statements are a production artifact.

Lines 72 and 74 log raw context values to stdout and are gated only by a TODO comment referencing issue #26. These should be removed or at minimum guarded by isInDevMode() until the proper logging implementation is in place.

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

In `@apps/api/src/lib/functions/middleware.ts` around lines 70 - 75, In
afterRouteLogicMiddleware, remove the two debug console.log statements that
print c.get("teamId") (or at minimum wrap them in a dev-only check); update the
function (and any temporary TODO) so production builds do not log raw context to
stdout — locate afterRouteLogicMiddleware in middleware.ts and either delete the
console.log calls or guard them behind isInDevMode() while preserving the await
next() and existing TODO.

44-46: ⚠️ Potential issue | 🟠 Major

Overly broad startsWith check can inadvertently bypass authentication.

c.req.path.startsWith("/api/auth") will also match any future route whose path begins with /api/auth — such as /api/authorization, /api/authenticate, etc. — treating them as public even if they are meant to be protected.

🛡️ Proposed fix — anchor to path-segment boundary
-	const isPublicRoute = MIDDLEWARE_PUBLIC_ROUTES.some((route) =>
-		c.req.path.startsWith(route),
-	);
+	const isPublicRoute = MIDDLEWARE_PUBLIC_ROUTES.some((route) =>
+		c.req.path === route || c.req.path.startsWith(route + "/"),
+	);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/lib/functions/middleware.ts` around lines 44 - 46, The current
isPublicRoute check uses c.req.path.startsWith(route) which can incorrectly mark
routes like "/api/authorization" public when the intent was to only allow the
exact segment "/api/auth"; update the matching logic used when computing
isPublicRoute (and where MIDDLEWARE_PUBLIC_ROUTES is referenced) to only match
whole path segments by anchoring to the segment boundary — e.g., treat a route
as public if c.req.path === route or c.req.path.startsWith(route + "/") or use a
regex equivalent that enforces ^route(\/|$) — so that only the exact segment or
its subpaths are considered public and similarly-named routes remain protected.

23-23: ⚠️ Potential issue | 🟠 Major

logInfo is fire-and-forget — logs can be silently dropped in serverless runtimes.

logInfo(...) on line 23 (and again on line 53) is not awaited. In serverless environments (e.g., Cloudflare Workers), promises not tied to the response lifecycle can be terminated mid-flight before the DB insert completes, causing logs to vanish without error. The current design also means the initial request log never captures userId or teamId because those context values haven't been set yet at call time.

If this is intentional fire-and-forget, consider using a platform-aware mechanism (e.g., c.executionCtx.waitUntil(...) on Workers) to ensure log durability.

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

In `@apps/api/src/lib/functions/middleware.ts` at line 23, The request logging
calls using logInfo (the initial call that logs `Middleware for request path
${c.req.path} for ${userString}` and the later call) are fire-and-forget and may
be dropped in serverless runtimes; update the middleware to either await the
returned promise from logInfo or, in platform-specific code paths, schedule it
with the execution lifecycle (e.g., use c.executionCtx.waitUntil(...) on
Workers) to ensure durability, and move the initial log until after you set
c.userId/c.teamId (or reconstruct userString) so the log includes userId/teamId;
locate the logInfo calls in the middleware function and replace them with an
awaited call or waitUntil scheduling as appropriate.

32-34: ⚠️ Potential issue | 🟡 Minor

teamId is not initialized for authenticated requests — inconsistent with the unauthenticated path.

For unauthenticated requests (lines 25-30), teamId is explicitly set to null. For authenticated requests, teamId is never set, so c.get("teamId") returns undefined until a route handler sets it. Any downstream code that checks c.get("teamId") === null to mean "no team" will behave differently depending on auth state. Consider explicitly setting c.set("teamId", null) here (mirroring the unauthenticated branch) to establish a consistent baseline.

📝 Proposed fix
 	c.set("user", session.user);
 	c.set("session", session.session);
+	c.set("teamId", null);
 	await next();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/lib/functions/middleware.ts` around lines 32 - 34, Authenticated
branch in middleware.ts never initializes teamId, causing inconsistent
downstream behavior versus the unauthenticated path; add c.set("teamId", null)
in the authenticated branch (alongside c.set("user", session.user) and
c.set("session", session.session")) before calling await next() so handlers can
reliably check c.get("teamId") === null as "no team".
apps/api/src/lib/functions/database.ts (1)

29-42: ⚠️ Potential issue | 🟡 Minor

JSDoc return type is inaccurate — .returning() returns an array.

The doc says "Returns the teamId that was deleted", implying a scalar, but db.delete(...).returning(...) resolves to an array of row objects ({ teamId: string }[]). Callers may be surprised if they expect a single value.

📝 Suggested doc fix
- * `@returns` The teamId that was deleted
+ * `@returns` An array of objects containing the teamId for each deleted row
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/lib/functions/database.ts` around lines 29 - 42, The JSDoc for
leaveTeam is wrong: db.delete(...).returning(...) yields an array of row
objects, not a single scalar; update the JSDoc for the leaveTeam function to
state it returns Promise<{ teamId: string }[]> (or similar phrasing) and mention
it returns an array of deleted rows, referencing leaveTeam, db.delete and
.returning to locate the code; if the intended behavior is to return a single
teamId instead, change the implementation to extract the first row (e.g., await
db.delete(...).returning(...); return rows[0]?.teamId) and update the JSDoc/type
accordingly.
🧹 Nitpick comments (2)
apps/api/src/lib/functions/middleware.ts (1)

16-18: Redundant optional chain inside a truthy ternary branch.

session is already narrowed to truthy by the ternary condition, so session?.user.id can simply be session.user.id.

♻️ Proposed fix
-		? `Authenticated user (id: ${session?.user.id})`
+		? `Authenticated user (id: ${session.user.id})`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/lib/functions/middleware.ts` around lines 16 - 18, The ternary
that builds userString redundantly uses an optional chain despite session being
truthy; update the expression in the true branch to use session.user.id (replace
session?.user.id with session.user.id) so the value is accessed without
unnecessary optional chaining—modify the userString assignment where session is
checked to remove the redundant ?. and keep the rest of the ternary logic
unchanged.
apps/api/src/lib/functions/database.ts (1)

108-119: Eager-Promise argument defeats the short-circuit optimization.

When callers pass an already-resolved Promise<T> (i.e., an invoked query), the DB round-trip has already started regardless of whether the user is a site admin. The short-circuit on line 113 only saves work when the () => Promise<T> form is used. The comment acknowledges this, but it may be worth enforcing the lazy form to prevent accidental eager queries or at minimum adding a note to the JSDoc.

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

In `@apps/api/src/lib/functions/database.ts` around lines 108 - 119, The function
isUserSiteAdminOrQueryHasPermissions currently accepts a Promise or a thunk
which defeats short-circuiting when callers pass an already-started Promise;
change the API to require a thunk (() => Promise<T>) only (remove Promise<T>
from the union) so the DB/query is only invoked after the admin check, and
update the function signature and all callers to pass a zero-arg function that
returns the Promise; alternatively, if you must keep the union, add a runtime
guard in isUserSiteAdminOrQueryHasPermissions that throws a clear error when
query is a Promise (and update the JSDoc for
isUserSiteAdminOrQueryHasPermissions to explicitly require lazy thunks),
referencing the function name so reviewers can locate and update callers like
places invoking isUserSiteAdminOrQueryHasPermissions with eager Promises.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/lib/functions/middleware.ts`:
- Line 43: Remove the stale developer note "// First check if it is a public
route and if so we will return (make sure this works)" from the middleware file
— locate the comment in apps' middleware.ts near the middleware function that
checks public routes (e.g., around any isPublicRoute or matcher logic) and
either delete it or replace it with a concise, production-ready comment like "//
Skip public routes" so no dev-side parentheses remain.

---

Outside diff comments:
In `@apps/api/src/lib/functions/database.ts`:
- Around line 29-42: The JSDoc for leaveTeam is wrong:
db.delete(...).returning(...) yields an array of row objects, not a single
scalar; update the JSDoc for the leaveTeam function to state it returns
Promise<{ teamId: string }[]> (or similar phrasing) and mention it returns an
array of deleted rows, referencing leaveTeam, db.delete and .returning to locate
the code; if the intended behavior is to return a single teamId instead, change
the implementation to extract the first row (e.g., await
db.delete(...).returning(...); return rows[0]?.teamId) and update the JSDoc/type
accordingly.

In `@apps/api/src/lib/functions/middleware.ts`:
- Around line 70-75: In afterRouteLogicMiddleware, remove the two debug
console.log statements that print c.get("teamId") (or at minimum wrap them in a
dev-only check); update the function (and any temporary TODO) so production
builds do not log raw context to stdout — locate afterRouteLogicMiddleware in
middleware.ts and either delete the console.log calls or guard them behind
isInDevMode() while preserving the await next() and existing TODO.
- Around line 44-46: The current isPublicRoute check uses
c.req.path.startsWith(route) which can incorrectly mark routes like
"/api/authorization" public when the intent was to only allow the exact segment
"/api/auth"; update the matching logic used when computing isPublicRoute (and
where MIDDLEWARE_PUBLIC_ROUTES is referenced) to only match whole path segments
by anchoring to the segment boundary — e.g., treat a route as public if
c.req.path === route or c.req.path.startsWith(route + "/") or use a regex
equivalent that enforces ^route(\/|$) — so that only the exact segment or its
subpaths are considered public and similarly-named routes remain protected.
- Line 23: The request logging calls using logInfo (the initial call that logs
`Middleware for request path ${c.req.path} for ${userString}` and the later
call) are fire-and-forget and may be dropped in serverless runtimes; update the
middleware to either await the returned promise from logInfo or, in
platform-specific code paths, schedule it with the execution lifecycle (e.g.,
use c.executionCtx.waitUntil(...) on Workers) to ensure durability, and move the
initial log until after you set c.userId/c.teamId (or reconstruct userString) so
the log includes userId/teamId; locate the logInfo calls in the middleware
function and replace them with an awaited call or waitUntil scheduling as
appropriate.
- Around line 32-34: Authenticated branch in middleware.ts never initializes
teamId, causing inconsistent downstream behavior versus the unauthenticated
path; add c.set("teamId", null) in the authenticated branch (alongside
c.set("user", session.user) and c.set("session", session.session")) before
calling await next() so handlers can reliably check c.get("teamId") === null as
"no team".

---

Nitpick comments:
In `@apps/api/src/lib/functions/database.ts`:
- Around line 108-119: The function isUserSiteAdminOrQueryHasPermissions
currently accepts a Promise or a thunk which defeats short-circuiting when
callers pass an already-started Promise; change the API to require a thunk (()
=> Promise<T>) only (remove Promise<T> from the union) so the DB/query is only
invoked after the admin check, and update the function signature and all callers
to pass a zero-arg function that returns the Promise; alternatively, if you must
keep the union, add a runtime guard in isUserSiteAdminOrQueryHasPermissions that
throws a clear error when query is a Promise (and update the JSDoc for
isUserSiteAdminOrQueryHasPermissions to explicitly require lazy thunks),
referencing the function name so reviewers can locate and update callers like
places invoking isUserSiteAdminOrQueryHasPermissions with eager Promises.

In `@apps/api/src/lib/functions/middleware.ts`:
- Around line 16-18: The ternary that builds userString redundantly uses an
optional chain despite session being truthy; update the expression in the true
branch to use session.user.id (replace session?.user.id with session.user.id) so
the value is accessed without unnecessary optional chaining—modify the
userString assignment where session is checked to remove the redundant ?. and
keep the rest of the ternary logic unchanged.

@christianhelp christianhelp self-assigned this Feb 21, 2026
@christianhelp christianhelp merged commit 00d9ad6 into main Feb 21, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Annotate Server Helper functions for Clarity

1 participant