Memberships — Code Map
API
| File | Purpose |
|---|---|
apps/api/src/memberships/memberships.module.ts | Wires MembershipsController, InvitationsController, MembershipsService. Imports UsersModule + EventEmitter. |
apps/api/src/memberships/memberships.controller.ts | Org-scoped CRUD: list, detail, stats, invite, resend, revoke, bulk-invite, send-invitation, update. |
apps/api/src/memberships/invitations.controller.ts | Bearer-auth POST /invitations/accept-pending (throttled 10/min). |
apps/api/src/memberships/memberships.service.ts | All business logic + Clerk invitation orchestration + per-process 30s membership cache. |
apps/api/src/memberships/membership-events.ts | MEMBERSHIP_ACTIVATED event name + MembershipActivatedEvent payload type. |
apps/api/src/memberships/dto/invite-member.dto.ts | {email, role}. |
apps/api/src/memberships/dto/bulk-invite.dto.ts | {membershipIds: uuid[]}. |
apps/api/src/memberships/dto/list-members-query.dto.ts | search/status/role/page/limit/sortBy/sortOrder/hasClerkAccount/profileComplete. |
apps/api/src/memberships/dto/update-member.dto.ts | {role?, status?, profile?: UpdateProfileDto}. |
Routes
| Method | Path | Handler |
|---|---|---|
| GET | /organizations/:orgId/members | listMembers |
| GET | /organizations/:orgId/members/me/stats | getMyStats |
| GET | /organizations/:orgId/members/:membershipId | getMemberDetail |
| GET | /organizations/:orgId/members/:membershipId/stats | getMemberStats |
| PATCH | /organizations/:orgId/members/:membershipId | updateMembership |
| POST | /organizations/:orgId/members/bulk-invite | bulkInvite |
| POST | /organizations/:orgId/members/:membershipId/send-invitation | sendMemberInvitation |
| GET | /organizations/:orgId/invitations | listInvitations |
| POST | /organizations/:orgId/invitations | createInvitation (single) |
| POST | /organizations/:orgId/invitations/:invitationId/resend | resendInvitation |
| DELETE | /organizations/:orgId/invitations/:invitationId | revokeInvitation |
| POST | /invitations/accept-pending | acceptPendingInvitations (bearer) |
Web
| Component / Route | Description |
|---|---|
apps/web/src/app/[lang]/(protected)/dashboard/members/page.tsx + members-content.tsx | Paginated member list, filters, search. |
apps/web/src/app/[lang]/(protected)/dashboard/members/[id]/... | Member detail. |
apps/web/src/components/members/bulk-invite-dialog.tsx | UI for bulk-invite. |
apps/web/src/components/members/card-registration-dialog.tsx | Card-registration flow (payment provider tie-in). |
apps/web/src/components/onboarding/invite-members-form.tsx | The onboarding-time invite list, hits createInvitation per email. |
i18n: members.*, members.invite.* in apps/web/src/i18n/dictionaries/. |
Events
| Event | Producer | Consumers (known) |
|---|---|---|
MEMBERSHIP_ACTIVATED | acceptPendingInvitations, OrganizationLeadsService.convertLead | Forms fan-out (onboarding compliance forms), analytics tracking, future welcome emails / push (per code comments). |
DB tables
| Table | Used as |
|---|---|
memberships | Owned. |
invitations | Owned (alongside Clerk invitation). |
users | Joined for list/detail/search. |
organizations | Joined for tier check (platform_tier). |
bookings, class_sessions, class_types, programs | Joined for computeMemberStats. |
Shared schemas
MembershipResponse,MembershipRole,MembershipStatus,MembershipPaymentStatus—libs/shared/src/lib/schemas/membership.schema.ts.isStaffRole(role)— boolean helper that returns true forowner|admin|coach.
Clerk integration
clerk.invitations.createInvitation({emailAddress, redirectUrl, ignoreExisting:true})on create, resend, bulk-invite, send-member-invitation.clerk.invitations.revokeInvitation(clerkInvitationId)best-effort on revoke and resend.- No Clerk org membership API used — invitation acceptance ties to FitKit memberships only.
Tests
| File | What it covers |
|---|---|
apps/api/src/memberships/memberships.service.unit.spec.ts | Invitation flow, acceptance, last-owner protection, tier limit, bulk-invite skip reasons. |
| Web integration | apps/web/src/app/[lang]/(protected)/dashboard/members/members-page.int.spec.tsx, members-ribbon.int.spec.tsx. |