-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor API key response mapping to use TypeScript interfaces #23
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
Conversation
…proved type safety
WalkthroughRefactors GET /api-keys response construction to use a locally defined typed interface. The handler now maps records into ApiKeyResponse objects, masking keys, formatting createdAt, and conditionally including totalUsed while preserving status. No exported signatures or routes changed. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant R as GET /api-keys Route
participant D as Data Source
participant M as Mapper (ApiKeyResponse)
C->>R: HTTP GET /api-keys
R->>D: Fetch API key records
D-->>R: Key records
R->>M: Transform records (mask key, format createdAt, add totalUsed?)
M-->>R: ApiKeyResponse[]
R-->>C: 200 OK with typed array
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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: 0
🧹 Nitpick comments (3)
src/routes/apiKeys.ts (3)
190-197
: Hoist ApiKeyResponse (or derive from TypeBox) instead of defining inside the handler.Avoid per-request type redefinition. Either move the type to module scope or derive it from the response schema via TypeBox to prevent drift.
Top-level addition (preferred with TypeBox):
import { Static, Type } from "@sinclair/typebox"; // Extract once near the schema export const ApiKeyItem = Type.Object({ id: Type.String(), key: Type.String(), createdAt: Type.String(), // consider { format: 'date-time' } totalUsed: Type.Optional(Type.Number()), status: Type.String(), }); export type ApiKeyResponse = Static<typeof ApiKeyItem>;Then remove the in-function interface:
- interface ApiKeyResponse { - id: string; - key: string; - createdAt: string; - totalUsed?: number; - status: string; - } + // ApiKeyResponse is defined at module scope
198-215
: Trim redundant inline types; simplify mapping with destructuring.Leverage inference (or Prisma types) and keep nullish handling concise.
- const apiKeys: ApiKeyResponse[] = user.apiKeys.map((key: { - id: string; - key: string; - createdAt: Date; - totalUsed?: number; - status: string; - }): ApiKeyResponse => { - const apiKeyObj: ApiKeyResponse = { - id: key.id, - key: maskApiKey(key.key), - createdAt: key.createdAt.toISOString(), - status: key.status, - }; - if (key.totalUsed !== undefined) { - apiKeyObj.totalUsed = key.totalUsed; - } - return apiKeyObj; - }); + const apiKeys: ApiKeyResponse[] = user.apiKeys.map( + ({ id, key: hashedKey, createdAt, totalUsed, status }) => ({ + id, + key: maskApiKey(hashedKey), + createdAt: createdAt.toISOString(), + ...(totalUsed != null ? { totalUsed } : {}), + status, + }) + );
207-209
: Masked value is derived from the hash, not the plaintext key — confirm UX or store a display suffix.Since only the hash is stored,
maskApiKey(key.key)
exposes the last 8 chars of the hash, which won’t match the user’s plaintext key suffix and can confuse identification. Consider persisting a non-sensitive display suffix (e.g., last 6–8 chars of the plaintext) at creation time and using that here; fall back to the hash suffix if absent. Optionally rename the response field tomaskedKey
in a future version.Suggested changes (conceptual):
- On create (outside this hunk):
const displaySuffix = newApiKey.slice(-8); await prisma.apiKey.create({ data: { /* ... */, key: hashedKey, displaySuffix } });
- In GET mapping:
- key: maskApiKey(hashedKey), + key: key.displaySuffix + ? `****************${key.displaySuffix}` + : maskApiKey(hashedKey),Would you like a small migration + code patch to add
displaySuffix
?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/routes/apiKeys.ts
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/routes/apiKeys.ts (1)
src/utils/generateApiKey.ts (1)
maskApiKey
(40-47)
This PR fixes the type issue by adding an interface to the API key response
Summary by CodeRabbit