Shared Code Strategy
How the storefront and fundraising apps share code through packages/core and the @/ alias.
Shared Code Strategy
Code that more than one app needs lives in packages/core (@jose-madrid/core). Everything
else stays in the app that uses it.
How it resolves
packages/core keeps the same layout the code had in the storefront (lib/, components/,
emails/), and each app's @/ alias looks in the app first, then in core:
"paths": {
"@/*": ["./*", "../../packages/core/*"]
}So import prisma from '@/lib/prisma' works the same in every app, and moving a file between an
app and core needs no import changes. Next.js (Turbopack), tsc and tsx all honour the fallback.
Vitest does not read paths, so vitest.config.ts has a small resolver that tries the same two
roots in the same order.
Shared pages and API routes
Next.js only serves routes from an app's own app/ folder, so a page or API route that two apps
serve (checkout, order confirmation, sign-in, /api/auth/[...nextauth], the checkout APIs)
lives in packages/core/routes/, at its URL path. Each app keeps a one-line file at the
route that re-exports it:
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
export { GET } from '@/routes/api/orders/[orderId]/route'Route segment config (dynamic, runtime, revalidate, maxDuration, …) must be written in
the app's file itself, because Next reads it from the file without running it. Keep it out of
the core module.
Rules
- Core never imports from an app. Its
@/alias points only at itself, andnpm run type-check --workspace=@jose-madrid/corefails if a core file reaches for something that only exists in an app. When a core file needs an app module, move that module into core. - A file belongs in core when two apps import it. Code only one app uses stays in that app.
- Don't put a file at the same path in an app and in core. The app's copy would silently win for that app.
- Declare third-party imports in
packages/core/package.json, at the version the apps use.next,react,next-authand@prisma/clientare peer dependencies, so every app shares one copy (the Prisma client is generated once, fromapps/storefront/prisma/schema.prisma). - Tailwind: each app's
tailwind.config.tslists../../packages/core/{components,lib}incontent, so classes used by core components are generated. - Tests for core code still live in the storefront's
tests/folder and import it through@/. The storefront's coverage report includes core (coverage.allowExternal). Tests for fundraising-only code live inapps/fundraising/tests. - Next.js config that both apps need (security headers, image hosts, server packages) is in
packages/core/next-config.mjs, which each app'snext.config.mjsimports. - Runtime file paths: code in core must not read files relative to
process.cwd()or its own location, because it runs from whichever app imported it.
How is this guide?
Last updated on