Soren Lab's Monorepo: The Four Packages Everything Else Depends On

There are four packages without which nothing else can exist. Here's what I learned putting each one together.
@soren/tsconfig: Shared TypeScript settings across every project Without this, every app and package would have its own tsconfig.json that would drift out of sync within weeks. Three JSON presets live here, and everything extends one: no build step, just config. The strict settings caught real bugs during development. noUncheckedIndexedAccess was the most immediately useful: accessing an array by index now gives you T | undefined, forcing you to handle the case where nothing's there. Annoying the first few times, then genuinely valuable. One thing I only discovered by making the mistake: the Next.js preset sets noEmit: true because Next.js handles TypeScript compilation itself. Without it, running tsc generates compiled output that nothing uses. Type checking and compilation are two different things in a Next.js project.
@soren/config: One place for environment variables @soren/config is the only file in the entire monorepo that reads process.env. Everything else calls typed, validated functions. The benefit is that a missing or malformed environment variable crashes the app at startup with a specific error, not several requests later when something finally tries to use it. The validation library gives you both the runtime check and the TypeScript type in one declaration, so they can't drift apart. The server/client file split looks like organization, but it's actually a bundle boundary. If both lived in one file, a client component might accidentally import something that references a secret key, and that key could end up in a browser bundle. Separate files make the line explicit for the bundler. One implementation detail I'm glad I added: a cache reset function for tests. The parsed env is cached at module scope, tests run in the same process, and without a way to clear it between tests, isolation breaks.
@soren/auth Clerk as a dependency we own Nothing in any app imports from Clerk directly; it all goes through this wrapper package. The practical benefit is a single place to update if anything about the Clerk integration changes. The interesting design question was middleware. Each app still writes its own middleware.ts rather than the package shipping one pre-configured version. The reason: which routes are public or protected is different for every app. A shared pre-configured middleware would have to hardcode those rules, and whatever it hardcodes will be wrong for at least one of the apps. The package exports the primitives; apps compose the logic.
@soren/db MongoDB with Next. js-specific patterns Two things about this package are specific to how Next.js handles module execution in development: The connection is stored on the global object rather than in a module-level variable. Hot module reload re-executes module files when they change. A module-level connection resets on every file save, triggering a new database connection. The global persists across HMR cycles. Every Mongoose model uses a registration guard checking if the model already exists before trying to create it. HMR re-executes the registration code, and Mongoose throws if you try to register the same model name twice. The guard short-circuits if the model is already there. Part 2 covers the packages built on top of this foundation: payments, email, the API contract layer, and the UI component library.
New posts, in your inbox
Roughly one a week. Confirm once, unsubscribe anytime.
0 comments
Read next

Soren Lab's Monorepo: Payments, Email, APIs, and UI
Part 1 was the foundation: TypeScript config, environment variables, auth, and the database layer. This is what sits on top: five packages that deliver actual product functionality.

Building My Online Presence as a Dev Creator
I’m starting to take my online presence more seriously as I grow as a developer, creator, and business owner. This post explains the shift from casually posting online to intentionally building platforms like my blog, LinkedIn, Twitter/X, daily.dev, Reddit, and Patreon.