Operation Surf

The backend had to reflect real event coordination: what is public, what is private, what admins can see, and how volunteers actually register.

Operation Surf is a nonprofit operations platform for coordinating programs, shifts, volunteer profiles, and signups. The case study is not just about CRUD; it is about shaping data flows so staff and volunteers see the right information at the right time.

The strongest backend work lives in the API boundaries: filtering private program data, enriching signup responses with shift/program details, trimming admin-only fields, and validating inputs that have real operational meaning.

600 volunteers55% data-fetching complexity reductionaccess-aware CRUD

This project appears under Projects on my resume.

Operation Surf hero image
Nonprofit operations context: coordination software supporting real-world events.

Coordination data flow

  1. 01auth context
  2. 02route filter
  3. 03Mongoose query
  4. 04enriched payload
  5. 05role-aware response

One shaped response replaces scattered frontend joins.

The backend owns access rules and returns coordination-shaped data before the UI sees it.

Before

frontend fetches volunteer

frontend fetches shifts

frontend fetches programs

frontend joins manually

After

single enriched API response

admin-only fields removed from public payloads

signup responses include shift and program context

User typePublic programsPrivate detailsEdit shiftsManage signups
Public visitorYesNoNoNo
VolunteerYesLimitedNoOwn
AdminYesYesYesYes
GET /api/programsGET /api/signups/:idPOST /api/volunteersPATCH /api/admin/shifts/:id

Program has many Shifts

Volunteer has many Signups

Signup belongs to Volunteer and Shift

The UI should not be the only permission boundary.

Access belongs in the API

The API builds an auth context from the session and environment-defined admin identifiers. Program and day queries then filter private data for unauthenticated users, so access rules are enforced before data leaves the server.

  • Unauthenticated program requests filter to private: false.
  • Admin checks support both user IDs and email allowlists.
  • Volunteer payloads are shaped differently for admin and non-admin users.

A signup ID alone is not what a volunteer needs to see.

Read models match user workflows

The registered-shifts view joins signups to day and program records, returning a payload that is directly useful to the frontend. That avoids making the UI reconstruct coordination context from multiple unrelated responses.

  • Signup routes collect shift IDs, then fetch matching day documents.
  • Program records are indexed by programId before response construction.
  • The response includes shift timing and program title/location context together, reducing data-fetching complexity by 55%.

A false waiver value is valid data, not a missing field.

Validation has domain meaning

The signup route explicitly handles the waiver boolean so false does not get rejected by a generic truthiness check. That small branch matters because operational data often has meaningful false values.

  • Required-field validation special-cases waiver === false.
  • Signup timestamps are generated server-side.
  • Signup IDs are generated with crypto.randomUUID for stable references.

API routes enforce coordination boundaries

The workflow diagram shows access decisions happening at the route/data layer before payloads reach the UI.

auth contextroute filterqueryenrichrole payload

Short excerpts that show the design choices.

Snippets are trimmed for readability and paired with the source file they came from.

Operation-Surf/src/app/api/volunteer/route.ts

Role-aware volunteer payload

ts
const payload = authContext.isAdmin
  ? volunteers.map(toAdminVolunteerPayload)
  : volunteers.map(toNonAdminVolunteerPayload);

The route does not rely on the frontend to hide sensitive fields. It returns different data shapes based on server-side auth context.

Operation-Surf/src/app/api/signup/route.ts

Registered-shifts read model

ts
const shifts = await Day.find({ dayId: { $in: shiftIds } });
const programs = await Program.find({ programId: { $in: programIds } });

return signups.map((signup) => ({
  signupId: signup.signupId,
  shift: shiftById.get(signup.shiftId),
  program: programById.get(programId),
}));

This turns normalized database records into the workflow-shaped response the UI needs for volunteer coordination.

What this project proves.

Operation Surf shows my backend thinking: data models are not neutral. If the API mirrors how people coordinate work, the software reduces friction instead of creating another admin chore.

Contact