Building Vellum: A Monorepo Content Platform from Scratch
How I structured a full-stack publishing platform across web, mobile, backend, and admin in a single monorepo — and the type-sharing trick that made it all work.
export interface Article {
id: string;
title: string;
excerpt: string;
body: string;
authorId: string;
category: string;
publishedAt: Date;
readingTime: number;
}
Vellum is a full-stack content platform with a web reader, mobile app, NestJS backend, and admin console — all in one monorepo. Here's how I made it work without losing my mind.
The monorepo structure
The key insight was treating the schema as the source of truth. Prisma models generate TypeScript types, and those types flow into the API, web app, and mobile app.
// packages/types/src/index.ts — shared across all surfaces
export interface Article {
id: string;
title: string;
excerpt: string;
body: string;
authorId: string;
category: string;
publishedAt: Date;
readingTime: number;
}
Why shared types matter
When the Article schema changes, the web app, mobile app, and API all get compile errors at the same time. No more "the mobile app is sending a field the API doesn't expect" bugs.
The highlights feed
The short-form video feed uses a swipeable pager with preloading:
// Preload the next 2 videos while the current one plays
const preload = useCallback((index: number) => {
highlights.slice(index, index + 3).forEach((h) => {
if (!loaded.has(h.id)) {
Video.preload(h.videoUrl);
loaded.add(h.id);
}
});
}, [highlights]);
What I'd do differently
Start with the shared types package before building any surface. I built the API first and extracted types later — that was two weeks of refactoring I could have avoided.