Across the app and the learning platform
1 login
Every message between staff and families
Moderated
Accounts model, not one row per student
Family

The problem

A music school was running on a spreadsheet, a shared calendar, and a lot of text messages. Lessons moved, families had several children enrolled at different levels, and teachers were coordinating with parents through personal phone numbers.

They also ran a separate LearnDash site for course material, which meant every student had two accounts and remembered roughly one of them.

Constraints

  • Minors are involved. Any messaging feature in a school context needs moderation and a record. That is not a nice-to-have.
  • The buyer is not technical. The person approving the work runs a music school. Every decision had to be explainable without jargon, and the finished system had to be operable without me.
  • An existing system had to stay. LearnDash was already in use and already paid for. Replacing it was not on the table.

What I built

A family, not a list of students

The first modelling decision was the one that mattered most. The obvious schema is a table of students. The correct schema for this client is a family account with several students attached, because that is how billing works, how scheduling conflicts arise, and how the parent thinks about it.

Getting this right at the start meant that “move all of the Hendersons to Thursday” was a query rather than a feature request six months later. This is why I model data before choosing anything else.

Moderated messaging

Staff and families can message each other inside the application. Messages pass through moderation rather than going direct, and everything is retained. No personal phone numbers, no channel outside the system, and a record if a question is ever raised.

Role-based access and audit logging

Teachers, administrators, and parents see different things and can do different things. Sensitive actions write an audit entry: who did it, what changed, when. For a school, being able to answer that question later is worth the small cost of writing the rows.

Single sign-on into LearnDash

Rather than replacing the learning platform, I bridged it. A student signs in once and moves into LearnDash without a second login. The application stays the identity provider, which keeps one source of truth for who is enrolled.

This was the most fiddly part of the build and the part the client noticed most, which is often how it goes.

Stack and why

FastAPI and PostgreSQL, with Pydantic models on every endpoint and SQLAlchemy underneath. React for the interface. The API is documented in OpenAPI, which matters more than usual here because the client may hire someone else to extend it, and that person should not have to ask me anything.

What I would do differently

I would have built the scheduling conflict rules as explicit, testable constraints from the first week rather than growing them out of bug reports. They ended up correct, but they were discovered rather than designed, and the tests came after the behaviour instead of describing it.

Next step

Want something like this built?

Tell me what you are trying to solve. I will tell you how I would approach it, what the risky parts are, and roughly what it takes.

  • Replies: Within two working days
  • Based in: the United States
  • Open to: full time, contract, remote or hybrid

Pages

Projects

Writing

Actions