This is a template for a monorepo that uses best practices for TypeScript, Web Services and React.
It is what @bhouston considers best practice in November 2025.
- Mono-repository using pnpm workspaces
- TypeScript (native compiler preview) for type safety
- Incremental and composite TypeScript configuraiton for speed
- ES Modules for fast builds
- NodeNext node resolution
- React for UI
- Tailwindcss for styling
- Both react and vanilla JS libraries
- Command line, React app, and web server
- Vite for Bundling, CSS Handling, Live Reloading
- SDK defining schema and functions for calling REST API methods
- CLI via @yargs + file structure defined commands (via yargs-file-commands)
- REST API via @fastify + file structure defined routes (via fastify-file-router)
- @TanStack/start for router, SSR, server API
- Fastify for server with file-based router
- Hot reload of React
- Auto service restart for the web server
- Biome for code formatting & linting
- VSCode will auto-format on save and paste
- Vitest for testing with coverage support
- Github action CI
This monorepo uses a shared SDK package (packages/sdk) that provides a single source of truth for API contracts, ensuring type safety and reducing duplication across the entire platform.
Key Benefits:
- Single Source of Truth: API schemas (params, query, body, response) are defined once in the SDK using Zod
- Type Safety: Shared types ensure API, CLI, and frontend stay in sync
- Reduced Duplication: No need to duplicate schemas or API client code
- Consistency: All API calls go through validated SDK functions
How It Works:
-
SDK Defines Schemas: The SDK package (
packages/sdk) defines Zod schemas for all API routes:- Request parameters (path params, query params, body)
- Response types
- Validation rules
-
Fastify API Uses SDK Schemas: The Fastify API (
apps/api) imports schemas from the SDK and uses them for route validation:import { getUserParamsSchema, userSchema } from '@bhouston/sdk'; export const route = defineRouteZod({ schema: { params: getUserParamsSchema, response: { 200: userSchema }, }, // ... });
-
CLI Uses SDK Functions: The CLI tool (
apps/cli) uses SDK functions to make API calls:import { getUser } from '@bhouston/sdk'; const user = await getUser(client, { params: { userName: 'john' } });
-
Frontend Uses SDK Functions: The TanStack Start app (
apps/tan-start-app) uses SDK functions for client-side API calls:import { healthCheck, hello } from '@bhouston/sdk'; await healthCheck(client); const result = await hello(client, { query: { name: 'World' } });
This architecture ensures that when API contracts change, TypeScript will catch mismatches across all consumers (API, CLI, and frontend) at compile time, preventing runtime errors and reducing maintenance burden.
- Clone this repository
- Run
pnpm install
- Run
pnpm testto run all tests - Run
pnpm test:watchfor watch mode during development - Run
pnpm test:coverageto generate test coverage report
- Run
pnpm devto start the hot reload development server & build watchers
- Run
pnpm tsgo, uses incremental composite builds to run fast on large projects
- Run
pnpm buildto build the source (uses tsgo on all projects, then vite build on TanStack server) - Run
pnpm startto run the production server
- Run
pnpm clito run the CLI example - The CLI uses SDK functions to interact with the Fastify API
- Example commands:
pnpm cli health- Check API healthpnpm cli hello --name "World"- Call hello endpointpnpm cli users list- List userspnpm cli users get <userName>- Get user details
packages/sdk/- Shared SDK with Zod schemas and API client functionspackages/common-lib/- Shared utilities and helperspackages/node-lib/- Node.js-specific utilitiespackages/react-lib/- React components and hooksapps/api/- Fastify REST API server (uses SDK schemas for validation)apps/cli/- Command-line interface (uses SDK functions)apps/tan-start-app/- TanStack Start React app (uses SDK functions)
Ben Houston, Sponsored by Land of Assets
