Home / Projects / APMNU — Asosiasi Profesor Muslimat NU
Featured Project
APMNU — Asosiasi Profesor Muslimat NU
Organizational platform for NU's women professors: public site, professor self-service portal, and admin panel in one Nuxt + NestJS monorepo.
Tech Stack
NestJS · Nuxt 4 · Prisma · PostgreSQL · TypeScript · Zod · Turborepo · Tailwind CSS · Docker · GitHub Actions
Architecture Diagram
Click a node for the decision behind it
- A Public Site · Nuxt
- Public site and admin dashboard are two separate Nuxt apps, so none of the admin code ever ships to a visitor reading the directory.
- B Admin Dashboard · Nuxt
- The admin dashboard is its own application rather than a role-gated route inside the public site — lighter public bundle, smaller attack surface. The cost is sharing layout through packages/ui.
- C REST API · NestJS
- One REST API serves both frontends: 90 endpoints across 12 domain modules, with Swagger documentation generated from the same definitions.
- D PostgreSQL · Prisma
- Registration status (PENDING/APPROVED/REJECTED/NEED_REVISION) and membership status (ACTIVE/EMERITUS/DECEASED) are modeled as two separate concepts here. They look alike, and conflating them is exactly how data bugs start.
- E packages/schemas · Zod
- Shared Zod schemas are why business rules never drift: the API and both frontends validate against the exact same definitions instead of three copies of the same rule.
The platform for Asosiasi Profesor Muslimat NU: a public professor directory, a self-service portal for professors, and an admin panel for managing membership and content. I led a team of 4 developers as Project Manager and lead contributor — from turning client needs into PRDs through to documentation handover.
Problem
Records of NU’s women professors were spread across official organizational documents. There was no searchable public directory, no verified member registration flow, and activity announcements depended on disconnected channels. The organization also needed a way for its professors to be reached as speakers without an admin acting as go-between.
Solution
Built a monorepo holding three applications — REST API, public site, and admin dashboard — on a single data schema. Member registration runs through a staged admin review, professors can enrich their own profile once approved, and organizational content is managed by non-technical staff through the admin panel.
Architecture Decisions
- Turborepo with a shared
packages/schemasof Zod schemas — the API and both frontends validate against the exact same definitions, so business rules never drift between applications - The professorship decree (SK Guru Besar) is the single basis for membership verification, which lets professors edit every other field without a second admin approval — less admin load without weakening data validity
- Registration status (
PENDING/APPROVED/REJECTED/NEED_REVISION) is modeled separately from membership status (ACTIVE/EMERITUS/DECEASED) — the two look alike and get conflated, which is exactly how data bugs start - The admin dashboard is a separate application rather than a role-gated route inside the public site — the public bundle stays light and the attack surface stays small
- “Invite as Speaker” uses a direct
mailto:to the professor’s address instead of internal messaging — avoids building an inbox nobody would use - Menu naming is deliberately neutral toward officeholders (“Jejak Intelektual” rather than the chairperson’s name) so it does not go stale when leadership changes
- All three apps ship as separate Docker images pushed to GHCR tagged by commit SHA — deploy and rollback are a tag swap, with no build on the server
- CI uses per-path change detection, so a PR touching one app does not trigger lint, typecheck, and build for all three
Trade-offs
- Choosing
mailto:over internal messaging: loses conversation tracking, but saves an entire module whose need was never demonstrated - Letting professors edit non-decree fields without re-verification: accepts some risk of inaccurate data in exchange for removing the admin bottleneck
- Two separate Nuxt apps mean some layout and components have to be shared through
packages/ui— higher setup cost up front, repaid once the two apps grow in different directions
Features
- Searchable, filterable public professor directory
- Member registration with a staged admin review flow
- QR-based digital member cards
- Self-service portal where professors fill in education history, publications, and academic URLs
- News and organizational content management through a rich text editor
- Member distribution visualization
- Form-based organizational contact page
- Automatic API documentation through Swagger
CV Highlights
- Built an organizational platform from scratch: public site, admin portal, and REST API in one monorepo
- Led a developer team through sprint planning and weekly meetings while acting as the direct bridge to the client
- Turned client needs into PRDs and backlog items in Jira and Linear, then distributed the work across the team
- Led the tracker migration from Linear to Jira
- Set the standards for feature documentation, API conventions (Swagger and Zod), and the release flow the whole team followed
- Drove the migration of initial content and data from official organizational documents, and wrote the user guides for handover
- Set up the CI/CD pipeline from lint and typecheck through image build and automatic deploy to staging and production VPS
Challenges Overcome
- Keeping validation consistent between the API and two frontends without duplicating the rules
- Designing a registration revision flow that reads clearly to both the applicant and the reviewing admin
- Establishing a domain vocabulary the team agreed on, so organizational terms would not get crossed in the code
- Moving the team from Linear to Jira mid-project without losing backlog context
Lessons Learned
- Agreeing on domain vocabulary early prevents a class of bugs that is hard to trace — registration status and membership status look alike right up until both are used wrong in production
- Recording architecture decisions as ADRs means “why was it done this way” can be answered without interrupting the person who wrote it
- Working as PM and developer at once, writing the PRD first forced a clarity that actually made implementation faster