Skip to content

Access Keys — Data hooks (CRUD) + mutation keys + API types #65

@mahid797

Description

@mahid797

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.


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 from lib/models/index.ts.
  • Extend mutationKeys with an accessKeys group.
  • Ensure all mutations invalidate queryKeys.accessKeys.list() on success.
  • Errors surface via axios; consumers will route them to handleFormError and toast.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/:id204

All timestamps are ISO strings; expiresAt and lastUsedAt may be null.


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.ts

    export 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.ts

    • GET /api/access-keysAccessKeyListResponse
    • Query key: queryKeys.accessKeys.list()
    • No retries; rely on global defaults
  • useCreateAccessKeyMutation.ts

    • POST /api/access-keys with CreateAccessKeyRequest
    • Returns AccessKeyCreateResponse
    • mutationKey: mutationKeys.accessKeys.create
    • On success: invalidateQueries({ queryKey: queryKeys.accessKeys.list() })
  • useUpdateAccessKeyMutation.ts

    • PATCH /api/access-keys/:id with UpdateAccessKeyPatch
    • Returns AccessKeyUpdateResponse
    • mutationKey: mutationKeys.accessKeys.update
    • On success: invalidate list
  • useRevokeAccessKeyMutation.ts

    • Thin wrapper for PATCH /api/access-keys/:id with { revoked: boolean }
    • mutationKey: mutationKeys.accessKeys.revoke
    • On success: invalidate list
  • useDeleteAccessKeyMutation.ts

    • DELETE /api/access-keys/:id
    • mutationKey: mutationKeys.accessKeys.delete
    • On success: invalidate list

Shared details

  • Use axios directly (consistent with auth hooks).
  • Base path constant: const ACCESS_KEYS_API = '/api/access-keys';
  • Prefer async/await; throw axios errors as-is (consumers will call handleFormError).
  • 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 with preview, lastUsedAt|null, expiresAt|null, timestamps as ISO strings.
  • Create: Call useCreateAccessKeyMutation(); response contains accessKey.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

Metadata

Metadata

Assignees

No one assigned

    Labels

    ⚡ ImportantHigh-impact issue that needs to be resolved before the next release.🎨 FrontendFrontend related issue.🧩 CoreChanges to core system architecture or critical modules.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions