feat: add GET /creators/:id/holders endpoint#428
Merged
Chucks1093 merged 2 commits intoJun 24, 2026
Merged
Conversation
Adds a paginated key holder list endpoint so creators can see which
wallets hold their keys and how many.
## Endpoint
GET /api/v1/creators/:id/holders
Query params:
- limit (default 20, max 100)
- offset (default 0)
- sort "key_balance" (default) | "held_since"
Response body:
{
"success": true,
"data": {
"items": [{ "wallet_address", "key_balance", "held_since" }],
"meta": { "limit", "offset", "total", "hasMore" }
}
}
## Behaviour
- Default sort is key_balance desc (largest holders first)
- sort=held_since returns earliest buyers first (held_since asc)
- held_since is derived from KeyOwnership.createdAt — the timestamp
when the ownership row was first created (wallet's first buy)
- Returns 404 when the creator id/handle does not exist
- Returns empty items [] (not 404) when creator exists but has no holders
- Accepts creator cuid id OR handle as the :id param
## Files added / changed
- src/modules/creators/creator-holders.schemas.ts — Zod query schema
- src/modules/creators/creator-holders.service.ts — DB queries
- src/modules/creators/creator-holders.controller.ts — HTTP handler
- src/modules/creators/creator-holders.integration.test.ts — Jest tests
- src/modules/creators/creators.routes.ts — register route
- src/constants/creator-public-routes.constants.ts — GET_HOLDERS constant
- src/constants/creator-public-cache.constants.ts — 5-min cache preset
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add 'holders' to CreatorReadEndpoint union so createCreatorReadMetricsMiddleware accepts it; update registry, snapshot, and reset accordingly - Narrow req.params['id'] from string | string[] to string before passing to findCreatorByIdOrHandle — Express v5 types route params as string | string[] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #422
Adds
GET /api/v1/creators/:id/holders— a paginated endpoint that lets creators see which wallets hold their keys, the current balance for each holder, and when they first bought (for loyalty sorting).idorhandleas the:idparamitems: [](not 404) if creator exists but has no holderskey_balancedescending (largest holders first)?sort=held_sincereturns earliest buyers first (KeyOwnership.createdAtascending)held_sinceis derived fromKeyOwnership.createdAt— the timestamp when the ownership row is first created, which corresponds to the wallet's first buy for that creatorlimit/offset) withmeta.hasMoreResponse shape
{ "success": true, "data": { "items": [ { "wallet_address": "GABC...", "key_balance": 5, "held_since": "2024-03-01T00:00:00.000Z" } ], "meta": { "limit": 20, "offset": 0, "total": 1, "hasMore": false } } }Files changed
src/modules/creators/creator-holders.schemas.tssrc/modules/creators/creator-holders.service.tsfindCreatorByIdOrHandle+fetchCreatorHolderssrc/modules/creators/creator-holders.controller.tshttpGetCreatorHoldershandlersrc/modules/creators/creator-holders.integration.test.tssrc/modules/creators/creators.routes.tsGET /:id/holders+ 405 handlersrc/constants/creator-public-routes.constants.tsGET_HOLDERSroute name constantsrc/constants/creator-public-cache.constants.tsTest plan
key_balancedesc by default?sort=held_sincepasses correct sort to servicehasMore,total,limit,offset) is correcthasMore=falseon the last pagewallet_address,key_balance,held_sincesortvaluenext(error)is called on service exception🤖 Generated with Claude Code