-
Notifications
You must be signed in to change notification settings - Fork 9
Description
Goal
Wire typed React Query hooks for Access Keys, centralize API types/shapes, and extend mutation keys so the rest of the Access Keys features can consume a consistent client surface.
- Parent: Access Keys FE #64
- Depends on: none
- Blocks: Access Keys — List view + Status badges + Sorting #66, Access Keys — Create flow refactor + One-time Reveal #67, Access Keys — Edit / Revoke / Delete #68
Description
Create a small, typed client layer for Access Keys:
- Hooks (TanStack Query + axios):
useAccessKeysQuery,useCreateAccessKeyMutation,useUpdateAccessKeyMutation,useRevokeAccessKeyMutation,useDeleteAccessKeyMutation. - Types under
lib/models/AccessKey.ts(responses/DTOs) and re-export fromlib/models/index.ts. - Extend
mutationKeyswith anaccessKeysgroup. - Ensure all mutations invalidate
queryKeys.accessKeys.list()on success. - Errors surface via axios; consumers will route them to
handleFormErrorandtoast.error.
API contracts (final)
GET /api/access-keys→{ accessKeys: AccessKeyListItem[] }POST /api/access-keys{ name?, description? }→{ accessKey: { id, key, createdAt, name?, description? } }PATCH /api/access-keys/:id{ name?, description?, revoked?, expiresAt?: string | null }→{ accessKey: AccessKeyListItem }DELETE /api/access-keys/:id→204
All timestamps are ISO strings;
expiresAtandlastUsedAtmay benull.
Tasks
A) Central API types
-
Create
src/lib/models/AccessKey.ts// List item (preview-only shape returned by GET and PATCH) export type AccessKeyListItem = { id: string; name: string | null; description: string | null; revoked: boolean; expiresAt: string | null; lastUsedAt: string | null; createdAt: string; // ISO updatedAt: string; // ISO preview: string; // e.g., "lr_…a1b2" }; export type AccessKeyListResponse = { accessKeys: AccessKeyListItem[] }; // Create export type CreateAccessKeyRequest = { name?: string; description?: string; }; export type AccessKeyCreateResponse = { accessKey: { id: string; key: string; // full key — shown once createdAt: string; // ISO name?: string | null; description?: string | null; }; }; // Update / Revoke export type UpdateAccessKeyPatch = Partial<{ name: string; description: string; revoked: boolean; expiresAt: string | null; // ISO string or null to clear }>; export type AccessKeyUpdateResponse = { accessKey: AccessKeyListItem };
-
Export from
src/lib/models/index.ts(create index if missing).
B) Mutation keys
-
Extend
src/lib/mutationKeys.tsexport const mutationKeys = { ...mutationKeys, accessKeys: { create: ['access-keys', 'create'] as const, update: ['access-keys', 'update'] as const, revoke: ['access-keys', 'revoke'] as const, delete: ['access-keys', 'delete'] as const, }, } as const;
C) Hooks (axios + React Query)
Location:
src/app/(client)/hooks/data/accessKeys/
-
useAccessKeysQuery.tsGET /api/access-keys→AccessKeyListResponse- Query key:
queryKeys.accessKeys.list() - No retries; rely on global defaults
-
useCreateAccessKeyMutation.tsPOST /api/access-keyswithCreateAccessKeyRequest- Returns
AccessKeyCreateResponse mutationKey: mutationKeys.accessKeys.create- On success:
invalidateQueries({ queryKey: queryKeys.accessKeys.list() })
-
useUpdateAccessKeyMutation.tsPATCH /api/access-keys/:idwithUpdateAccessKeyPatch- Returns
AccessKeyUpdateResponse mutationKey: mutationKeys.accessKeys.update- On success: invalidate list
-
useRevokeAccessKeyMutation.ts- Thin wrapper for
PATCH /api/access-keys/:idwith{ revoked: boolean } mutationKey: mutationKeys.accessKeys.revoke- On success: invalidate list
- Thin wrapper for
-
useDeleteAccessKeyMutation.tsDELETE /api/access-keys/:idmutationKey: mutationKeys.accessKeys.delete- On success: invalidate list
Shared details
- Use
axiosdirectly (consistent with auth hooks). - Base path constant:
const ACCESS_KEYS_API = '/api/access-keys'; - Prefer
async/await; throw axios errors as-is (consumers will callhandleFormError). - Add minimal JSDoc to each hook (arguments, return shape, errors).
D) Light integration sanity
-
In any temporary scratch usage (or via React Query Devtools), confirm:
- List fetch works
- Create returns full key
- Update/revoke/delete mutate and list invalidates
Tests (manual)
Using React Query Devtools or a temporary test component:
- List: Hitting
useAccessKeysQuery()returns items withpreview,lastUsedAt|null,expiresAt|null, timestamps as ISO strings. - Create: Call
useCreateAccessKeyMutation(); response containsaccessKey.key(full key). After success, list refetches. - Update: PATCH name/description/expiresAt; response returns list-item shape; list refetches.
- Revoke/Unrevoke: PATCH
{ revoked: true|false }; list refetches. - Delete: DELETE returns
204; list refetches. - Errors: Force 4xx/5xx (e.g., invalid
id) → axios error surfaces; no unhandled promise rejections.
Acceptance Criteria
- Strongly-typed hooks and DTOs in place; no duplicate type declarations in components.
- All mutations invalidate
queryKeys.accessKeys.list()on success. - Hooks use axios, match the final contracts, and propagate errors suitable for
handleFormError. - No new dependencies; build passes.
Notes
- Do not add a global axios interceptor in this issue; keep hooks simple and explicit.
- For PATCH shapes, components will supply only dirty fields later (handled in Access Keys — Edit / Revoke / Delete #68).
- This issue does not render UI; it only delivers the typed client layer consumed by Access Keys — List view + Status badges + Sorting #66, Access Keys — Create flow refactor + One-time Reveal #67, Access Keys — Edit / Revoke / Delete #68.