Server Packages
Backend services and data layer: @uwdsc/db, @uwdsc/core, and @uwdsc/admin.
Overview
The packages/server/ directory contains three sub-packages. There is no Prisma; data access uses postgres.js (via @uwdsc/db) and Supabase for auth and storage.
Sub-packages:
- @uwdsc/db β Database connection, Supabase clients, BaseRepository, migrations
- @uwdsc/core β Shared services and repositories used by web and admin apps
- @uwdsc/admin β Admin-only services (e.g. member management for admin dashboard)
Structure
packages/server/
βββ db/ # @uwdsc/db
β βββ src/
β βββ config/ # connection, supabase, baseRepository
β βββ migrations/ # db-migrate SQL files
βββ core/ # @uwdsc/core
β βββ src/
β βββ services/ # Auth, Profile, Application, File, Resume, Team
β βββ repositories/ # Data access (extend BaseRepository)
β βββ ...
βββ admin/ # @uwdsc/admin
βββ src/ # Admin-specific services (e.g. profileService for members)@uwdsc/db
Purpose: Database connection, Supabase clients, base repository, migrations.
Exports:
@uwdsc/db/connectionβ Postgressql(postgres.js)@uwdsc/db/supabaseβcreateSupabaseBrowserClient,createSupabaseServerClient,createSupabaseMiddlewareClient@uwdsc/db/baseRepositoryβBaseRepository(abstract class with protectedthis.sql)
Tech: postgres.js (Supabase Transaction Pooler), Supabase JS/SSR, db-migrate. Run migrations from root: pnpm migrate, pnpm migrate:down, pnpm migrate:create <name>.
@uwdsc/core
Purpose: Shared services and repositories used by web and admin apps.
Exports: Service classes and singleton instances: profileService, applicationService, teamService. AuthService and ResumeService require a Supabase client and are created per-request in each appβs lib/services.ts via createAuthService() / createResumeService().
import { profileService, applicationService, teamService } from "@uwdsc/core";
import { AuthService, ResumeService } from "@uwdsc/core";Contents:
- Services: AuthService, ProfileService, ApplicationService, FileService, ResumeService, TeamService
- Repositories: Extend
BaseRepositoryfrom@uwdsc/db/baseRepository; usethis.sqlfor queries - Dependencies:
@uwdsc/db,@uwdsc/common
@uwdsc/admin
Purpose: Admin-only services (e.g. profile/member listing and updates for the admin dashboard).
Exports: Admin-specific profileService (and related) for member management.
import { profileService } from "@uwdsc/admin";Used by: apps/admin API routes only. Dependencies: @uwdsc/core, @uwdsc/db, @uwdsc/common.
Usage
In API Routes (web or admin)
// apps/web/app/api/profile/route.ts
import { profileService } from "@uwdsc/core";
export async function GET() {
const profile = await profileService.getProfileByUserId(user.id);
return NextResponse.json(profile);
}// apps/admin/app/api/members/route.ts
import { profileService } from "@uwdsc/admin";
export async function GET() {
const members = await profileService.listMembers();
return NextResponse.json(members);
}Auth and resume (per-request)
Create Supabase-backed services in the app:
// apps/web/lib/services.ts
import { createSupabaseServerClient } from "@uwdsc/db";
import { AuthService, ResumeService } from "@uwdsc/core";
export async function createAuthService() {
const supabase = await createSupabaseServerClient();
return new AuthService(supabase);
}
export async function createResumeService() {
const supabase = await createSupabaseServerClient();
return new ResumeService(supabase);
}Repository layer
Repositories extend BaseRepository and use this.sql (postgres.js):
import { BaseRepository } from "@uwdsc/db/baseRepository";
export class ProfileRepository extends BaseRepository {
async findByUserId(userId: string) {
const rows = await this.sql`SELECT * FROM profiles WHERE id = ${userId}`;
return rows[0] ?? null;
}
}Database migrations
Migrations live in packages/server/db. From repo root:
pnpm migrateβ applypnpm migrate:downβ rollback lastpnpm migrate:create <name>β create new migrationpnpm migrate:checkβ status
Best practices
Do
- Use services from
@uwdsc/coreor@uwdsc/adminin API routes - Use singletons (
profileService,applicationService,teamService) where the service is stateless - Create AuthService/ResumeService per-request via
lib/services.ts - Extend
BaseRepositoryfrom@uwdsc/db/baseRepositoryfor new repositories - Use
ApiResponsefrom@uwdsc/common/utilsin API routes when applicable
Donβt
- Import server packages in React components β use client API functions from
lib/api/ - Put business logic in API routes or repositories
- Access the database directly from API routes
- Assume Prisma β the project uses postgres.js and Supabase
Next steps
- API Architecture β Request flow and layers
- Creating API Endpoints β Step-by-step API guide
- Database Setup β Migrations and postgres.js usage