Summary
Context: The UsersTable component on the settings page uses a GraphQL fragment to fetch user data from the backend using the Relay connection pattern.
Bug: The GraphQL fragment is missing required pagination fields (cursor and pageInfo).
Actual vs. expected: The query returns only the first 50 users (the GraphQL schema default) instead of providing pagination controls to access all users.
Impact: Administrators cannot view or manage users beyond the 50th entry, and no error or indication is shown that more users exist.
Code with bug
The fragment at app/src/pages/settings/__generated__/UsersTable_users.graphql.ts contains:
users {
edges {
user: node {
id
email
username
createdAt
authMethod
profilePictureUrl
role {
name
id
}
}
# cursor is MISSING <-- BUG 🔴 prevents pagination
}
# pageInfo is MISSING <-- BUG 🔴 no way to detect if more users exist
}users {
edges {
user: node {
id
email
username
createdAt
authMethod
profilePictureUrl
role {
name
id
}
}
# cursor is MISSING <-- BUG 🔴 prevents pagination
}
# pageInfo is MISSING <-- BUG 🔴 no way to detect if more users exist
}users {
edges {
user: node {
id
email
username
createdAt
authMethod
profilePictureUrl
role {
name
id
}
}
# cursor is MISSING <-- BUG 🔴 prevents pagination
}
# pageInfo is MISSING <-- BUG 🔴 no way to detect if more users exist
}The GraphQL schema defines users(first: Int = 50, ...), meaning only 50 users are returned by default.
Codebase inconsistency
Other tables in the same codebase correctly implement pagination. For example, the ModelsTable at app/src/pages/settings/__generated__/ModelsTable_generativeModels.graphql.ts includes the required fields:
generativeModels(first: $first, after: $after) {
edges {
generativeModel: node {
...
}
cursor # ✓ Present
node {
...
}
}
pageInfo { # ✓ Present
endCursor
hasNextPage
}
}generativeModels(first: $first, after: $after) {
edges {
generativeModel: node {
...
}
cursor # ✓ Present
node {
...
}
}
pageInfo { # ✓ Present
endCursor
hasNextPage
}
}generativeModels(first: $first, after: $after) {
edges {
generativeModel: node {
...
}
cursor # ✓ Present
node {
...
}
}
pageInfo { # ✓ Present
endCursor
hasNextPage
}
}Similarly, RetentionPoliciesTable correctly includes both cursor and pageInfo fields in its fragment, demonstrating this is an established pattern in the codebase.
Recommended fix
Update the GraphQL fragment in UsersTable.tsx to include the missing pagination fields:
fragment UsersTable_users on Query
@refetchable(queryName: "UsersTableQuery") {
users(first: 1000) {
edges {
user: node {
id
email
username
createdAt
authMethod
profilePictureUrl
role {
name
}
}
cursor # <-- FIX 🟢
}
pageInfo { # <-- FIX 🟢
endCursor
hasNextPage
}
}
}fragment UsersTable_users on Query
@refetchable(queryName: "UsersTableQuery") {
users(first: 1000) {
edges {
user: node {
id
email
username
createdAt
authMethod
profilePictureUrl
role {
name
}
}
cursor # <-- FIX 🟢
}
pageInfo { # <-- FIX 🟢
endCursor
hasNextPage
}
}
}fragment UsersTable_users on Query
@refetchable(queryName: "UsersTableQuery") {
users(first: 1000) {
edges {
user: node {
id
email
username
createdAt
authMethod
profilePictureUrl
role {
name
}
}
cursor # <-- FIX 🟢
}
pageInfo { # <-- FIX 🟢
endCursor
hasNextPage
}
}
}After updating the source fragment, regenerate the GraphQL types using the Relay compiler. This will enable proper pagination or, at minimum with first: 1000, allow viewing significantly more users than the current 50-user limit.