Skip to main content
Tarpit Logo

πŸ₯¦ Simple but Awesome TypeScript DI Framework for Node.js πŸ₯¦

NPM Version Monthly Downloads Node.js Version Code Coverage Bundle Size Last Commit
Build Status TypeScript MIT License Lerna Ask DeepWiki
GitHub Stars GitHub Forks

What is Tarpit?​

Tarpit is a modern Dependency Injection (DI) Framework built specifically for TypeScript and Node.js applications. It provides a powerful platform for building reusable, testable, and maintainable server-side applications with a clean, decorator-based architecture.

Key Features​

  • 🎯 Type-Safe DI: Leverages TypeScript's type system for dependency resolution
  • πŸš€ Decorator-Based: Clean, declarative syntax using TypeScript decorators
  • πŸ“¦ Modular Architecture: Built-in support for modules and component organization
  • πŸ”§ Extensible: Easy to extend with custom providers and modules
  • ⚑ Lightweight: Minimal overhead with focused functionality
  • πŸ§ͺ Testing-Friendly: Built with testability in mind

Core Concepts​

Tarpit's Dependency Injection system is built around three fundamental concepts:

Platform - The application container that manages the entire dependency injection system. It orchestrates module imports, controls the application lifecycle, and serves as the central registry for all services and providers.

Providers - The recipes that tell the DI system how to create and supply dependencies. Providers define how services, values, and factories are registered and resolved, whether through class constructors, factory functions, or pre-existing values.

Injector - The core engine that resolves dependencies by matching injection tokens to providers. It maintains a hierarchical chain of dependency lookups and acts as the runtime dependency resolution mechanism.

These three concepts work together: the Platform registers Providers with the Injector, which then resolves dependencies when services are requested.

Quick Start​

Prerequisites​

Before getting started, ensure you have:

  • Node.js (v14.0.0 or higher)
  • TypeScript (v4.0 or higher)
  • npm or yarn package manager
Try the Examples

You can find complete working examples in the example/ directory of this repository, organized by module.

Installation​

Create a new project directory:

mkdir my-tarpit-app
cd my-tarpit-app

Initialize your project:

npm init -y

Install TypeScript and development dependencies:

# Install TypeScript globally or as dev dependency
npm install -D typescript ts-node @types/node

# Initialize TypeScript configuration
npx tsc --init

Configure TypeScript for decorators in tsconfig.json:

{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}

Install Tarpit HTTP module (includes core dependencies):

npm install @tarpit/http @tarpit/judge @tarpit/config reflect-metadata
Why reflect-metadata?

reflect-metadata is required for TypeScript decorator metadata reflection. Tarpit's dependency injection system uses this to automatically detect constructor parameter types and enable type-safe dependency resolution.

Hello World Example​

Create src/index.ts:

import { load_config } from '@tarpit/config'
import { Platform, TpConfigSchema } from '@tarpit/core'
import { HttpServerModule, TpRouter, Get, PathArgs } from '@tarpit/http'
import { Jtl } from '@tarpit/judge'

@TpRouter('/')
class HelloRouter {

@Get('hello')
async say_hello() {
return { message: 'Hello, Tarpit!' }
}

@Get('user/:id')
async get_user(args: PathArgs<{ id: string }>) {
const user_id = args.ensure('id', Jtl.string)
return { user_id, name: `User ${user_id}` }
}
}

const config = load_config<TpConfigSchema>({
http: { port: 4100 }
})

const platform = new Platform(config)
.import(HttpServerModule)
.import(HelloRouter)
.start()

Run your application:

npx ts-node src/index.ts

Test your endpoints:

# Basic hello endpoint
curl http://localhost:4100/hello

# Parameterized endpoint
curl http://localhost:4100/user/123

This example demonstrates:

  • Basic Routing: Using @TpRouter to define route prefixes
  • HTTP Methods: Using @Get decorator for GET endpoints
  • Path Parameters: Extracting and validating URL parameters
  • JSON Responses: Returning structured data from endpoints

Service Injection Example​

For more complex applications, you'll want to organize your code using dependency injection. Here's how to create injectable services with HTTP routing:

import { load_config } from '@tarpit/config'
import { Platform, TpConfigSchema, TpService } from '@tarpit/core'
import { HttpServerModule, TpRouter, Get, PathArgs } from '@tarpit/http'
import { Jtl } from '@tarpit/judge'

// 1. Declaration - Mark classes as injectable services
@TpService()
class DatabaseService {
connect() {
console.log('Connected to database')
}

query(sql: string) {
console.log(`Executing query: ${sql}`)
return []
}

find_user(id: string) {
console.log(`Finding user with ID: ${id}`)
return { id, name: `User ${id}`, email: `user${id}@example.com` }
}
}

@TpService()
class UserService {
// 2. Dependency will be injected automatically
constructor(private db: DatabaseService) {}

create_user(name: string) {
this.db.connect()
const result = this.db.query(`INSERT INTO users (name) VALUES ('${name}')`)
console.log(`Created user: ${name}`)
return { id: Date.now(), name }
}

get_user(id: string) {
this.db.connect()
return this.db.find_user(id)
}
}

// 3. HTTP Router using injected services
@TpRouter('/api/users')
class UserRouter {
// 4. Service injection in router
constructor(private userService: UserService) {}

@Get('')
async list_users() {
return {
message: 'User list endpoint',
users: ['Alice', 'Bob', 'Charlie']
}
}

@Get(':id')
async get_user(args: PathArgs<{ id: string }>) {
const user_id = args.ensure('id', Jtl.string)
const user = this.userService.get_user(user_id)
return user
}

@Get('hello/:name')
async greet_user(args: PathArgs<{ name: string }>) {
const name = args.ensure('name', Jtl.string)
this.userService.create_user(name)
return {
message: `Hello, ${name}!`,
user: { name, created: true }
}
}
}

async function main() {
// 5. Registration - Register services and router with platform
const config = load_config<TpConfigSchema>({
http: { port: 4100 }
})

const platform = new Platform(config)
.import(HttpServerModule)
.import(DatabaseService)
.import(UserService)
.import(UserRouter)

await platform.start()

console.log('Server started on http://localhost:4100')
console.log('Try these endpoints:')
console.log(' GET http://localhost:4100/api/users')
console.log(' GET http://localhost:4100/api/users/123')
console.log(' GET http://localhost:4100/api/users/hello/Alice')
}

main().catch(console.error)

Run the example:

npx ts-node example/basic/service-injection.ts

Test the API endpoints:

# List all users
curl http://localhost:4100/api/users

# Get a specific user
curl http://localhost:4100/api/users/123

# Create and greet a user
curl http://localhost:4100/api/users/hello/Alice

This example demonstrates:

  • Service Declaration: Using @TpService() to mark classes as injectable
  • Dependency Injection: Automatic injection of dependencies through constructor parameters
  • Router Injection: Injecting services into HTTP routers for API endpoints
  • Service Registration: Importing services and routers into the platform
  • HTTP Integration: Combining dependency injection with REST API endpoints
  • Path Parameters: Extracting and validating URL parameters with type safety

Next Steps​

Ready to dive deeper? Explore our comprehensive documentation:

Core Framework​

HTTP Server Module​

Other Modules​

Complete Example Repository

For hands-on learning, check out the example/ directory with runnable code samples for each module.