Skip to content

refactor: deepen architecture — consolidate response, wrap repo boile…#8

Merged
sumdahl merged 1 commit into
mainfrom
feat/improve-architecture
May 13, 2026
Merged

refactor: deepen architecture — consolidate response, wrap repo boile…#8
sumdahl merged 1 commit into
mainfrom
feat/improve-architecture

Conversation

@sumdahl

@sumdahl sumdahl commented May 13, 2026

Copy link
Copy Markdown
Owner

…rplate, make email send explicit, reduce DI friction

…rplate, make email send explicit, reduce DI friction
Copilot AI review requested due to automatic review settings May 13, 2026 04:07
@sumdahl
sumdahl merged commit a3331b0 into main May 13, 2026
4 checks passed
@sumdahl
sumdahl deleted the feat/improve-architecture branch May 13, 2026 04:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the server architecture by consolidating HTTP response utilities into a single module, introducing a shared DB error wrapper to reduce repository boilerplate, and simplifying DI/router wiring by passing the Awilix cradle directly. It also makes email sending in registration explicit and surfaces whether the verification email was sent.

Changes:

  • Consolidate response formatting/handlers/schemas into src/server/infrastructure/http/response/response.ts and update imports accordingly.
  • Introduce withDbError() and refactor Postgres repositories to use it instead of per-method try/catch.
  • Adjust router dependency shapes to *UseCase names and update integration tests; extend register flow to return verificationEmailSent.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/unit/use-cases/auth/register.test.ts Asserts the new verificationEmailSent field in register result.
tests/integration/auth/auth.routes.test.ts Updates router dependency keys to *UseCase.
tests/integration/admin/admin.routes.test.ts Updates router dependency keys to *UseCase.
src/server/infrastructure/persistence/verification-token.pg.repository.ts Refactors DB error handling to withDbError().
src/server/infrastructure/persistence/user.pg.repository.ts Refactors DB error handling to withDbError() and maps unique violations.
src/server/infrastructure/persistence/token.pg.repository.ts Refactors DB error handling to withDbError().
src/server/infrastructure/persistence/role.pg.repository.ts Refactors DB error handling to withDbError().
src/server/infrastructure/persistence/password-reset-token.pg.repository.ts Refactors DB error handling to withDbError().
src/server/infrastructure/http/shared/create-router.ts Updates formatError import to the consolidated response module.
src/server/infrastructure/http/response/response.types.ts Removes now-consolidated response types file.
src/server/infrastructure/http/response/response.schemas.ts Removes now-consolidated response schemas file.
src/server/infrastructure/http/response/response.handler.ts Removes now-consolidated success handler file.
src/server/infrastructure/http/response/response.formatter.ts Removes now-consolidated formatter file.
src/server/infrastructure/http/response/response.ts New single module for response types, formatters, handlers, and OpenAPI schemas.
src/server/infrastructure/http/middleware/rate-limiter.ts Updates formatError import to the consolidated response module.
src/server/infrastructure/http/middleware/error-handler.ts Updates formatError import to the consolidated response module.
src/server/infrastructure/http/auth/index.ts Simplifies DI wiring by passing container.cradle directly to router/middleware.
src/server/infrastructure/http/auth/auth.schemas.ts Adds registerResponseSchema including verificationEmailSent.
src/server/infrastructure/http/auth/auth.routes.ts Updates deps naming to *UseCase, uses consolidated response module, and updates register response schema.
src/server/infrastructure/http/admin/index.ts Simplifies DI wiring by passing container.cradle directly to router/middleware.
src/server/infrastructure/http/admin/admin.routes.ts Updates deps naming to *UseCase and uses consolidated response module.
src/server/infrastructure/db/with-db-error.ts Introduces shared DB error wrapper and DB error type guard.
src/server/index.ts Removes no-longer-needed destructured DI items.
src/server/core/use-cases/auth/register.ts Awaits verification email send and returns verificationEmailSent in the result.
Comments suppressed due to low confidence (8)

src/server/infrastructure/persistence/token.pg.repository.ts:37

  • Same return-type issue as above: delete() promises void but the callback passed to withDbError returns the Drizzle delete result. Make the callback async and await the query (returning void) or otherwise discard the result.
    return withDbError("delete refresh token", () =>
      this.db.delete(refreshTokens).where(eq(refreshTokens.token, token)),
    );

src/server/infrastructure/persistence/token.pg.repository.ts:43

  • Same return-type issue: deleteAllForUser() promises void but the withDbError callback returns the Drizzle delete result. Ensure the callback returns Promise<void> by awaiting and discarding the query result.
    return withDbError("delete all refresh tokens for user", () =>
      this.db.delete(refreshTokens).where(eq(refreshTokens.userId, userId)),
    );

src/server/infrastructure/persistence/password-reset-token.pg.repository.ts:37

  • Return type mismatch risk: delete() returns Promise<void> but the withDbError callback returns the Drizzle delete result. Make the callback async and await the query so it resolves to void.
    return withDbError("delete password reset token", () =>
      this.db.delete(passwordResetTokens).where(eq(passwordResetTokens.token, token)),
    );

src/server/infrastructure/persistence/password-reset-token.pg.repository.ts:43

  • Return type mismatch risk: deleteAllForUser() returns Promise<void> but the withDbError callback returns the Drizzle delete result. Ensure the callback resolves to void by awaiting and discarding the result.
    return withDbError("delete all password reset tokens for user", () =>
      this.db.delete(passwordResetTokens).where(eq(passwordResetTokens.userId, userId)),
    );

src/server/infrastructure/persistence/user.pg.repository.ts:62

  • Same Promise<void> vs query-result return mismatch: updatePassword() promises void but returns whatever the Drizzle update resolves to via withDbError. Await/discard the query result inside the callback so the method resolves to void.
    return withDbError("update password", () =>
      this.db.update(users).set({ passwordHash }).where(eq(users.id, userId)),
    );

src/server/infrastructure/persistence/user.pg.repository.ts:86

  • Same issue: delete() promises void but the callback passed to withDbError returns the Drizzle delete result. Make the callback async and await the delete (or discard the result) so the method resolves to void.
    return withDbError("delete user", () =>
      this.db.delete(users).where(eq(users.id, userId)),
    );

src/server/infrastructure/persistence/role.pg.repository.ts:74

  • Return type mismatch risk: removeRoleFromUser() returns Promise<void> but the callback passed to withDbError returns the delete query result. Use an async callback that awaits the delete and returns void (or discard the result).
    return withDbError("remove role from user", () =>
      this.db.delete(userRoles).where(and(eq(userRoles.userId, userId), eq(userRoles.roleId, roleId))),
    );

src/server/infrastructure/persistence/verification-token.pg.repository.ts:45

  • Same Promise<void> vs query-result mismatch: deleteAllForUser() promises void but returns the delete query result via withDbError. Await/discard the result inside an async callback so the method resolves to void.
    return withDbError("delete all verification tokens for user", () =>
      this.db.delete(verificationTokens).where(eq(verificationTokens.userId, userId)),
    );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import { AppError, ErrorCode } from "../../core/errors";

export function isDbError(err: unknown): err is { code: string; message: string } {
return typeof err === "object" && err !== null && "code" in err;
import { logger } from "../logger";
import { eq, sql } from "drizzle-orm";
import { DB } from "../db";
import { withDbError, isDbError } from "../db/with-db-error";
Comment on lines +11 to 15
return withDbError("save refresh token", () =>
this.db.insert(refreshTokens).values({
id: crypto.randomUUID(),
userId,
token,
Comment on lines +11 to 16
return withDbError("save password reset token", () =>
this.db.insert(passwordResetTokens).values({
id: crypto.randomUUID(),
userId,
token,
expiresAt,
Comment on lines +54 to +56
return withDbError("verify user", () =>
this.db.update(users).set({ isVerified: true }).where(eq(users.id, userId)),
);
Comment on lines +66 to +68
return withDbError("assign role to user", () =>
this.db.insert(userRoles).values({ userId, roleId }).onConflictDoNothing(),
);
Comment on lines +37 to +39
return withDbError("delete verification token", () =>
this.db.delete(verificationTokens).where(eq(verificationTokens.token, token)),
);
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.

2 participants