Loading…
Loading…
Momentum is a production-focused software project built by Sadam Hussain using React Native, Expo, Expo Router, and related technologies.
BD tracking platform as a Turborepo monorepo: Drizzle schema types flowing from Neon Postgres through NestJS to the Expo mobile app. Outcome: schema changes break the build, not production.
Tech Stack






Status
Production Ready
Type
Enterprise platform
Architected a full-stack monorepo using Turborepo, sharing TypeScript database schemas (Drizzle ORM), email templates (React Email + Resend), and UI components across the NestJS backend and React Native/Expo mobile client.
Built a high-performance cross-platform mobile application using Expo and React Native Reanimated, maintaining smooth animations through optimized gesture handlers, memoized component trees, and GPU-accelerated blur effects in a custom glassmorphism UI design system.
Developed an enterprise-grade NestJS backend with modular architecture using Dependency Injection, organizing domain logic into focused modules (StatsModule, OpportunitiesModule, AuthModule) with clear service boundaries.
Designed and implemented a serverless Postgres data layer using Neon and Drizzle ORM, ensuring full TypeScript type safety from database schema definition through API response types to mobile client consumption - eliminating runtime type errors and enabling compile-time validation of database queries.
Integrated Better-Auth for multi-tenant session management with custom Drizzle adapter, implementing Role-Based Access Control (RBAC) with distinct permission boundaries for Founders, Admins, and BD professionals.
Built 'Momentum Charts' - custom data visualization components that track business development velocity, pipeline progression, and activity trends using animated, interactive chart rendering optimized for mobile performance.
Implemented frictionless onboarding flows with the 'Capture → Collaborate → Accelerate' methodology, using step-by-step guided experiences with contextual tooltips and progressive feature disclosure.
Designed role-specific dashboard views with aggregate analytics, smart filtering (date ranges, team members, opportunity stages), and drill-down navigation from summary metrics to individual opportunity details.
Built transactional email workflows using React Email templates rendered server-side and delivered through Resend, handling onboarding sequences, session invitations, and activity notifications.
Implemented global state management using Zustand with persistent storage middleware, enabling seamless offline-to-online state transitions and cross-screen data consistency in the mobile application.
Momentum tracks business development velocity for sales teams that live on their phones. The technical bet: a Turborepo monorepo where Drizzle schema types flow from the Neon Postgres database through the NestJS API to the Expo mobile app, so a schema change breaks the build, not production. Custom charts visualize pipeline momentum instead of static snapshots.
The product reimagines business development tracking by focusing on velocity and momentum rather than static pipeline snapshots. Custom "Momentum Charts" visualize how opportunities are moving through the pipeline over time, helping BD professionals and agency leaders identify acceleration patterns, stalled deals, and winning strategies through data visualization rather than spreadsheet analysis.
The mobile-first approach is deliberate: business development happens in meetings, coffee shops, conference halls, and transit - not at desks. Momentum is built so that logging a new contact, updating an opportunity, or checking pipeline status takes seconds from a phone, not minutes from a laptop.
In the high-stakes world of business development, legacy CRM tools are often clunky and data-heavy, designed for reporting to management rather than helping salespeople sell. They feel like overhead - time spent updating a CRM is time not spent building relationships and closing deals.
The core problem: Traditional BD tracking tools optimize for managerial visibility at the expense of field usability. Sales professionals resist using tools that feel like surveillance rather than support, leading to incomplete data, stale pipelines, and reports that don't reflect reality.
Momentum was built to answer a different question: "How can we help you win today?"
Stakeholder perspective:
Constraints that shaped the architecture:
@momentum/db (Drizzle ORM schema and migrations), @momentum/email (React Email templates), @momentum/ui (shared UI primitives), and @momentum/types (shared TypeScript interfaces). Both the NestJS API and Expo mobile app import from these packages, ensuring a single source of truth for data shapes, validation logic, and visual components.@momentum/db package produces compile-time errors in both the API and mobile app if the change isn't handled, catching type mismatches before runtime.@momentum/db before @momentum/api (which depends on it), running lint across all packages in parallel, and caching intermediate build artifacts so that unchanged packages don't rebuild.expo-blur), layered transparency, dynamic gradient borders, and subtle shadow depths. The design system includes themed components (GlassCard, GlassButton, GlassInput) that maintain visual consistency while being composable for different content types.AuthModule (Better-Auth integration, session management), OpportunitiesModule (CRUD, stage transitions, activity logging), StatsModule (aggregate analytics, momentum calculations), TeamsModule (user management, role assignments), and EmailModule (Resend integration, template rendering). Each module encapsulates its own services, controllers, and DTOs.StatsService depends on OpportunitiesService and TeamsService to compute aggregate analytics without duplicating data access logic. This makes services testable in isolation (inject mocks) and composable without tight coupling.drizzle-kit generate) and applied through the CI pipeline. This ensures that schema changes are version-controlled, reviewable in PRs, and applied consistently across environments.@momentum/email package. Templates include onboarding welcome sequences, team invitation emails, activity notifications, and weekly pipeline summaries. Because they're React components, they can be previewed in Storybook during development.EmailModule in NestJS abstracts the Resend API behind a service interface, making it straightforward to add new email types or switch providers.The monorepo structure is the architectural backbone that enables rapid, type-safe iteration:
apps/
api/ → NestJS backend
mobile/ → Expo React Native app
packages/
db/ → Drizzle ORM schema, migrations, query helpers
email/ → React Email templates
ui/ → Shared UI components
types/ → Shared TypeScript interfaces
The key insight is that shared packages aren't just about code reuse - they're about single source of truth. The @momentum/db package defines what an Opportunity looks like at the database level. The NestJS API imports this definition to type its query results. The mobile app imports the same types to type its API response handling. If someone changes the Opportunity schema, TypeScript catches every consumer that needs updating.
Why Turborepo over Nx: Turborepo provides the build caching and task orchestration needed without the configuration overhead of Nx. For a project with two apps and four shared packages, Turborepo's simplicity and speed are the right fit. Nx's advanced features (computation caching across CI, affected commands for large monorepos) aren't needed at this scale.
The glassmorphism design system looks premium but creates real performance challenges on mobile:
expo-blur with carefully managed blur radii and only applies blur to card backgrounds (not full-screen overlays) to keep GPU usage manageable.These constraints are invisible to the user - the UI looks and feels premium - but they represent deliberate performance trade-offs made during design system development.
Drizzle ORM was chosen over Prisma for several architectural reasons:
Better-Auth was chosen over Clerk or Auth0 specifically because it stores authentication data in the application's own database (Neon Postgres) rather than in an external provider's system. This means:
The trade-off is maintenance responsibility - session security, token rotation, and vulnerability patching are now internal concerns rather than delegated to a managed provider.
The most architecturally significant aspect of Momentum is how the Turborepo monorepo structure, combined with Drizzle ORM's TypeScript-first schema definitions, creates end-to-end type safety across the entire stack.
The Problem: In traditional full-stack development, the backend defines database schemas and API contracts, and the frontend defines its own types that hopefully match. Over time, these diverge - the backend adds a field that the frontend doesn't know about, or the frontend expects a field that the backend renamed. These mismatches surface as runtime errors in production, not compile-time errors during development.
The Architecture:
Schema as Source of Truth (@momentum/db):
The Drizzle ORM schema file defines database tables as TypeScript objects. Drizzle infers TypeScript types from these definitions - InsertOpportunity (the type for creating a new record), SelectOpportunity (the type for a queried record), and relation types. These inferred types are exported from the @momentum/db package.
API Consumes Schema Types (apps/api):
The NestJS API imports @momentum/db directly. Service methods return SelectOpportunity (or derived types) without redeclaring the shape. DTOs (Data Transfer Objects) for API endpoints are built from the same schema types, ensuring that request validation matches the database's expectations.
Mobile Consumes the Same Types (apps/mobile):
The Expo mobile app imports @momentum/types (which re-exports relevant types from @momentum/db). API response handling uses these types to parse server responses, and UI components receive typed props that match the database schema. A typo in a field name is caught by TypeScript immediately.
Change Propagation:
When the schema changes (e.g., adding a priority field to opportunities), the flow is:
@momentum/db → generate migrationpriority isn't handled → fix the APIpriority isn't displayed or handled → fix the mobile appThis is the key value: schema changes are atomic across the stack. It's impossible to merge a database migration without also updating the API and mobile app to handle it.
Shared Email Templates:
The @momentum/email package uses React Email to define email templates as React components. The NestJS API's EmailModule imports these components and renders them server-side with data before sending through Resend. Because the templates are TypeScript React components, they have typed props - the API can't render an email template with incorrect data without a compile error.
Why this matters: Type safety across the full stack eliminates an entire class of bugs - the "the API returns X but the client expects Y" bugs that are notoriously difficult to debug because they surface far from their cause. In a small team iterating rapidly on both the API and mobile app, this prevents the slow accumulation of subtle mismatches that larger teams manage through API documentation and contract testing.
Challenge: Glassmorphism blur effects on mobile caused frame drops below 60fps on mid-range Android devices, degrading the premium feel the design system intended to create. Approach: Limited blur application to card-level backgrounds only (not full-screen overlays), capped blur radius at values the mobile GPU handles efficiently, and set a maximum of two overlapping glass surfaces in any single view. Pre-computed gradient textures replace real-time gradient calculation for border effects. Why: The visual premium of glassmorphism only works if it's smooth. A beautiful glass card that janks during scroll is worse than a simple flat card that scrolls at 60fps. The performance constraints are design constraints - they define what's achievable, not what's ideal.
Challenge: Neon serverless Postgres drops connections during cold starts, causing intermittent connection errors in the NestJS API when the database scales from zero. Approach: Configured Neon's connection pooling proxy (which maintains warm connections even when compute scales to zero) and implemented connection retry logic with exponential backoff in the Drizzle client configuration. Health check endpoints warm the database connection before handling user requests. Why: Serverless databases trade consistent connection availability for cost efficiency and scaling simplicity. The retry logic handles the cold start latency transparently, and the connection pooling proxy reduces cold start frequency.
Challenge: Better-Auth's Drizzle adapter needed custom implementation since the official adapter didn't support Neon's serverless connection pooling configuration.
Approach: Built a custom Drizzle adapter for Better-Auth that uses Neon's @neondatabase/serverless driver with WebSocket connections for session operations. The adapter implements Better-Auth's storage interface while handling Neon-specific connection lifecycle.
Why: Better-Auth's value (owning auth data in your own database) requires the adapter to work with your specific database setup. A custom adapter is a one-time investment that enables the "own your data" architecture throughout the application's lifetime.
Challenge: Shared monorepo packages needed to work in both Node.js (NestJS) and React Native (Expo) environments, which have different module resolution, API availability, and bundling requirements. Approach: Structured shared packages with platform-agnostic code only - pure TypeScript types, Drizzle schemas, and React components without Node.js or React Native-specific APIs. Platform-specific code (file system access, native modules) stays in the respective app packages. Package.json exports use conditional exports to provide appropriate entry points for each consumer. Why: Shared packages that contain platform-specific code create bundling nightmares - Node.js crypto modules appearing in the mobile bundle, React Native's metro bundler choking on Node.js imports. Strict platform-agnosticism in shared packages prevents these issues entirely.
Building Momentum reinforced the value of monorepo architectures in full-stack mobile development. The ability to share the Drizzle schema and React Email templates across the stack maintained strict type safety and design consistency while iterating rapidly. It also highlighted how a "mobile-first" approach in enterprise software can significantly improve user engagement compared to legacy desktop-centric tools - when the tool is faster to use than not using it, adoption follows naturally.
Patterns I'd reuse:
What I'd reconsider:
Trade-offs acknowledged: