Users
Administrative profiles, analytics, role and club changes, and constrained deletion.
Status: complete · Source: content/docs/api-reference/users.mdx
The entire /users router requires a verified ADMIN token, including its read operations. Shared 401/403 responses and relative paths follow the reference conventions. Membership and leadership semantics are explained in Clubs, Events & Registrations.
List users
/usersAuthenticated · Role restrictedReturn selected profiles with club names and registration/blog counts.
No query validator or pagination is attached; the declared user-list schema is not used by this route. HTTP 200: { "users": [...] }, ordered by createdAt descending. Each user contains:
id,email,firstName,lastName,role, nullableclubId, andcreatedAt.club:{ "name": "Example Coding Club" }or null._count:{ "eventRegistrations": 0, "blogs": 0 }with that user's actual counts.
This selection excludes password, googleId, avatar, and updatedAt. Error: 500 Error fetching users.
Read platform analytics
/users/analyticsAuthenticated · Role restrictedReturn record totals, grouped role counts, and up to ten recent registrations.
No body or validated query. HTTP 200 response shape:
{
"analytics": {
"totalUsers": 2,
"totalClubs": 1,
"totalEvents": 1,
"totalBlogs": 0,
"totalMentors": 0,
"roleCounts": [
{"role": "STUDENT", "_count": {"role": 1}},
{"role": "ADMIN", "_count": {"role": 1}}
],
"recentRegistrations": []
}
}Role groups have no explicit ordering and need not include roles with no users. Each recent registration contains id, userId, eventId, registeredAt, event: { title }, and user: { firstName, lastName }, ordered by registration time descending. Blog totals include all statuses. There is no testimonial total or total-registration field. The queries run with Promise.all, not a shared transactional snapshot. Error: 500 Error fetching platform analytics.
Update a user
/users/:idAuthenticated · Role restrictedUpdate profile fields or club membership without changing the user's role.
Path id must be a nonempty string. Optional body fields are firstName/lastName (nonempty strings), email (email string), and clubId (string or null). An empty object passes validation. The controller updates truthy names/email and includes clubId whenever it is not undefined. A nonempty club ID must exist; empty string passes Zod but attempts an invalid foreign-key assignment. clubId: null clears membership.
PUT /users/illustrative-user-id
Authorization: Bearer <admin-jwt>
Content-Type: application/json
{"firstName":"Alex","clubId":null}HTTP 200: { "user": ... , "message": "User updated successfully" }. Here user contains all User model scalars (id, email, password, googleId, avatar, firstName, lastName, role, clubId, createdAt, updatedAt) plus club: { name } or null.
Errors: 400 validation or User ID is required; 404 User not found; 500 Error updating user, including email uniqueness or club foreign-key failures.
Change a role
/users/:userId/roleAuthenticated · Role restrictedChange another user's persisted role without updating issued JWTs.
Zod requires nonempty userId and body newRole equal to STUDENT, CLUB_HEAD, or ADMIN. The controller prevents changing the requesting token's own user identifier, including a same-role write. Membership and club leadership are not synchronized.
HTTP 200: { "user": ... , "message": "Role changed successfully" }. The selected user contains id, email, firstName, lastName, role, and club: { name } or null; it does not include clubId or password. Errors: 400 validation, manual Invalid role, or You cannot change your own role; 500 Error changing role, including an unknown target user. There is no dedicated 404 branch here.
Existing tokens retain their role claims; see JWT identity.
Assign club membership
/users/:userId/clubAuthenticated · Role restrictedReplace a user's membership with the specified club.
Zod requires nonempty path userId and body clubId as a nonempty string. This changes membership only: it does not assign CLUB_HEAD, update Club.clubHeadId, or issue a JWT.
HTTP 200: { "user": ... , "message": "User assigned to club successfully" }, with all User scalars plus club: { name }, as in profile update. Errors: 400 validation or Club ID is required; 500 Error assigning user to club, including missing user/club records. No explicit existence pre-check produces a 404.
Remove club membership
/users/:userId/clubAuthenticated · Role restrictedThe registered removal route currently fails parameter validation before reaching its controller.
There is no working success response for this route composition. The controller in isolation would clear membership and return a full user with User removed from club successfully, but that is not a reachable success contract through this route. The independently verified PUT /users/:id operation accepts { "clubId": null }.
Sourcebackend/src/routes/users.ts
Sourcebackend/src/validators/users.ts
Sourcebackend/src/validators/blogs.ts
Delete a user
/users/:idAuthenticated · Role restrictedDelete a user when restrictive related records do not prevent it.
Zod requires nonempty path id; no body is used. The controller checks existence and performs a direct Prisma delete. It has no self-deletion guard corresponding to the role-change guard.
HTTP 200: { "message": "User deleted successfully" }. Errors: 400 validation or User ID is required; 404 User not found; 500 Error deleting user for caught database/runtime failures.
Sourcebackend/src/controllers/userController.ts
Sourcebackend/prisma/migrations/20250829203812_initial_migration/migration.sql
Sourcebackend/prisma/migrations/20250901211052_add_blogs/migration.sql
