Fed up with endless boilerplate hunting? Same here! After spending way too much time searching for the "perfect" TypeScript setup, I decided to create my own opinionated monorepo boilerplate with exactly what I need. Now structured as a monorepo with shared packages for better code reusability! No bloat, just pure developer happiness with my preferred configuration. Use it if you like it!
This monorepo boilerplate comes pre-configured with:
- Deno 2 Workspace: Modern runtime with built-in TypeScript support and workspace management
- Monorepo Structure: Main backend application at root with organized packages
- Config Package: Centralized configuration and environment management
- Shared Package: Common utilities and math functions
- Backend Application: Main application at root using shared packages
- Native TypeScript: No compilation step needed, runs TypeScript natively
- Deno Lint: Built-in linting with Deno's linter
- Deno Test: Native testing framework with workspace support
- Type-safe Environment: Safe environment variable validation and typing
- Secure by Default: Explicit permissions model
ts-boilerplate/
├── backend/ # Backend application (main app)
│ ├── src/
│ │ ├── mod.ts # Main application entry point
│ │ ├── calculator.ts
│ │ └── calculator.test.ts
│ └── deno.json # Backend configuration
├── packages/
│ ├── config/ # Configuration package
│ │ ├── app_config.ts # Application configuration
│ │ ├── env.ts # Environment variables
│ │ ├── mod.ts # Configuration exports
│ │ └── deno.json # Config package configuration
│ └── shared/ # Shared utilities package
│ ├── helpers/ # Helper utilities
│ │ ├── logger.ts
│ │ ├── secret.ts
│ │ └── mod.ts
│ ├── math/ # Math calculation functions
│ │ ├── sum.ts
│ │ ├── sum.test.ts
│ │ └── mod.ts
│ ├── mod.ts # Main exports
│ └── deno.json # Shared package configuration
├── deno.json # Workspace configuration
├── LICENSE
├── renovate.json
└── README.md # This file
- Deno 2 (latest version)
- Clone this repository:
git clone https://github.com/vincentdchn/typescript-boilerplate.git
cd typescript-boilerplate
- Configure environment:
Create a .env
file in the root directory and configure it:
# Application Configuration
APP_KEY=your-32-character-or-longer-base64-key-here-replace-this-value
NODE_ENV=development
Generate a secure APP_KEY (at least 32 characters) and update it in your .env
file.
- Run the development server:
deno task dev
dev
: Run the backend in development modestart
: Run the backend in production modetest
: Run all tests across packagestest:watch
: Run all tests in watch modelint
: Lint all packagesfmt
: Format all packagescheck
: Type check all packages
dev:backend
: Run backend development serverstart:backend
: Start backend in productiontest:shared
: Run shared package teststest:config
: Run config package teststest:backend
: Run backend package testscheck:shared
: Type check shared packagecheck:config
: Type check config packagecheck:backend
: Type check backend package
The config package handles all configuration concerns:
- Environment Variables: Type-safe environment variable validation using safe-env
- App Configuration: Application-wide configuration settings
- Validation: Runtime validation of configuration values
import { config, env } from "@config";
console.log(`Environment: ${env.NODE_ENV}`);
console.log(`Redacted keyword: ${config.logger.redactedKeyword}`);
The shared package contains common utilities used across the monorepo:
- Math Functions:
sum
,multiply
,subtract
,divide
- Logger: Configured logging utility
- Secret: Secure value wrapper for sensitive data
- Bootstrap: Application initialization utilities
import { multiply, sum } from "@shared/math";
import { logger, Secret } from "@shared";
const result = sum(5, 3);
logger.info(`Result: ${result}`);
The main backend application demonstrates how to use the shared packages:
- Uses shared math functions for calculations
- Implements a Calculator service
- Uses configuration from the config package
- Shows logger usage with secrets from shared utilities
- Located at the root for easy development and deployment
Run all tests:
deno task test
Run tests for specific packages:
deno task test:shared
deno task test:backend
Run tests in watch mode:
deno task test:watch
Lint all packages:
deno task lint
Format all packages:
deno task fmt
Type check all packages:
deno task check
- Create a new directory under
packages/
for shared utilities - Add a
deno.json
configuration - Update the root
deno.json
workspace array - Configure imports in backend to use the new package
For additional applications, create them at the root level alongside backend/
The imports are configured at the workspace root to access all packages:
{
"imports": {
"@shared": "./packages/shared/mod.ts",
"@config": "./packages/config/mod.ts"
}
}
Then import in your TypeScript files:
// Math functions from shared package
import { sum } from "@shared/math";
import { logger } from "@shared/helpers";
// Configuration from config package
import { config, env } from "@config";
// Or use specific imports from sub-modules
import { env } from "@config/env";
import { config } from "@config/app";
The root deno.json
defines the workspace structure and provides unified task management across all packages.
Enhanced type safety with:
- Strict TypeScript configuration across all packages
- Runtime environment validation
- No implicit any
- Unchecked indexed access protection
The environment configuration is handled in the shared package with type-safe validation:
- Runtime Validation: Environment variables are validated at startup
- Type Safety: Full TypeScript support for environment variables
- Schema Definition: Clear schema definition in shared package
- Development Safety: Automatic .env file loading
- IDE Support: Full autocomplete support for environment variables
Required environment variables:
APP_KEY
: A base64 string of at least 32 charactersNODE_ENV
: Either 'development' or 'production' (defaults to 'development')
This boilerplate uses a monorepo structure with Deno 2 for several advantages:
- Code Reusability: Shared utilities across multiple packages
- Type Safety: Full TypeScript support across package boundaries
- Unified Tooling: Single configuration for linting, formatting, and testing
- Native TypeScript: No build step required, TypeScript runs directly
- Secure by Default: Explicit permissions for file system, network, and environment access
- Standard Library: Comprehensive standard library with consistent APIs
- Modern JavaScript: Built-in support for modern JavaScript features
- Simplified Dependency Management: Clear package dependencies within the workspace
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Open a Pull Request
This project is licensed under the MIT License.