Code generation tool that creates type-safe PostHog wrappers with embedded JSON schemas. Generate custom analytics libraries for TypeScript and Python with zero runtime overhead.
This is literally hot off the press, is an experiment, and will have rough edges. Please report any issues you encounter!
- 🏗️ Code Generation - Generates custom wrappers with schemas baked in at build time
- 🔒 Type Safety - Full TypeScript/Python type hints from JSON Schema
- 💡 IDE Autocomplete - Event names and properties autocomplete as you type
- ⚡ Zero Runtime Overhead - No schema loading, parsing, or network requests
- 🚀 Instant Validation - Validators are pre-compiled into generated code
- 🌍 Cross-Language - Share schemas between TypeScript and Python
- 📦 Minimal Bundle Size - Only your schemas, no loader code
- 🎯 Flexible Validation - Strict, warning, or disabled modes
- 🔄 Drop-in Replacement - Same API as vanilla PostHog libraries
npm install hogtyped
# or
yarn add hogtyped
pip install hogtyped
Works everywhere through code generation:
- Generated wrapper works in any Node environment
- No filesystem access needed at runtime
- Perfect for serverless/edge functions
- Schemas embedded in your bundle
- No network requests needed
- Works offline
- No special configuration needed
- Schemas are just JavaScript
- Zero cold start overhead
- No external dependencies
Schemas are baked into your code at build time - no runtime loading!
# 1. Install HogTyped
npm install hogtyped
# 2. Initialize (creates example schemas)
npx hogtyped init
# 3. Generate your custom wrapper
npx hogtyped generate
# 4. Use your generated wrapper (src/posthog.generated.ts)
// Everything is baked in - no runtime loading!
import { posthog } from './posthog.generated';
posthog.capture('user_signed_up', {
userId: '123', // ✅ Full autocomplete
email: '[email protected]', // ✅ Type checked
plan: 'growth' // ✅ Validates enums
});
Create a schemas/events.schema.json
file:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"UserSignedUp": {
"type": "object",
"properties": {
"userId": { "type": "string" },
"email": { "type": "string", "format": "email" },
"plan": {
"type": "string",
"enum": ["free", "starter", "growth", "enterprise"]
}
},
"required": ["userId", "email", "plan"]
}
},
"events": {
"user_signed_up": { "$ref": "#/definitions/UserSignedUp" }
}
}
npm run generate:types
import { TypedHogTyped, ValidationMode } from 'hogtyped';
const posthog = new TypedHogTyped('YOUR_API_KEY', {
validationMode: ValidationMode.STRICT,
autoDiscoverSchemas: true
});
// TypeScript provides autocomplete for event names and properties!
posthog.capture('user_signed_up', {
userId: 'user-123', // ✅ Required - TypeScript will error if missing
email: '[email protected]', // ✅ Must be valid email format
plan: 'growth' // ✅ Autocompletes: 'free' | 'starter' | 'growth' | 'enterprise'
});
// ❌ TypeScript Error at compile time + runtime validation
posthog.capture('user_signed_up', {
userId: 'user-123',
email: 'not-an-email', // TypeScript error: Invalid format!
plan: 'invalid-plan' // TypeScript error: Not in enum!
});
What You Get:
- 🎯 Event name autocomplete - IDE suggests available events as you type
- 📝 Property autocomplete - IDE suggests required and optional properties
⚠️ Compile-time errors - TypeScript catches missing/wrong properties before runtime- 📖 Inline documentation - Hover for property descriptions and types
# Generate your Python wrapper
python -m hogtyped generate
# Import the GENERATED wrapper - schemas embedded!
from posthog_generated import posthog
# Full type hints with TypedDict classes
posthog.capture(
distinct_id="user-123",
event="user_signed_up", # Type hints available!
properties={
"userId": "user-123",
"email": "[email protected]",
"plan": "growth" # Validated against enum
}
)
# ❌ Invalid - will raise ValueError in strict mode
posthog.capture(
distinct_id="user-123",
event="user_signed_up",
properties={
"email": "not-an-email" # Missing userId, invalid email!
}
)
Python Type Checking:
# Type check with mypy
mypy your_app.py # Validates event names and properties!
# Initialize HogTyped in your project
npx hogtyped init
# Generate wrapper with default options
npx hogtyped generate
# Custom options
npx hogtyped generate \
--schemas "./schemas/**/*.json" \
--output "./src/analytics.ts" \
--class "Analytics" \
--mode "strict"
# Initialize HogTyped in your project
python -m hogtyped init
# Generate wrapper with default options
python -m hogtyped generate
# Custom options
python -m hogtyped generate \
--schemas "./schemas/**/*.json" \
--output "./lib/analytics.py" \
--class-name "Analytics" \
--mode "strict"
Running npx hogtyped generate
creates a single file with everything:
// src/posthog.generated.ts
// ✅ TypeScript interfaces for all events
export interface UserSignedUpProperties { ... }
export interface ButtonClickedProperties { ... }
// ✅ Embedded schemas (no runtime loading!)
const SCHEMAS = {
'user_signed_up': { /* schema */ },
'button_clicked': { /* schema */ }
};
// ✅ Type-safe wrapper class
export class PostHog {
capture<K extends EventName>(event: K, properties: EventMap[K]): void
// ... all PostHog methods
}
// ✅ Ready-to-use instance
export const posthog = new PostHog();
const { HogTyped } = require('hogtyped');
const posthog = new HogTyped('YOUR_API_KEY', {
autoDiscoverSchemas: true // Finds schemas automatically
});
// Express.js example
app.post('/api/signup', (req, res) => {
try {
posthog.capture('user_signed_up', {
userId: req.body.userId,
email: req.body.email,
plan: req.body.plan
});
res.json({ success: true });
} catch (error) {
res.status(400).json({ error: 'Invalid event data' });
}
});
import { HogTyped, ValidationMode } from 'hogtyped';
const posthog = new HogTyped('YOUR_API_KEY', {
validationMode: ValidationMode.WARNING // Don't break the app
});
function SignupButton() {
const handleSignup = () => {
posthog.capture('user_signed_up', {
userId: user.id,
email: user.email,
plan: 'free'
});
};
return <button onClick={handleSignup}>Sign Up</button>;
}
<script type="module">
import { HogTyped } from 'https://unpkg.com/hogtyped/dist/browser/index.js';
const posthog = new HogTyped('YOUR_API_KEY', {
schemas: '/schemas/events.json' // Load from URL
});
// Wait for schemas to load
await posthog.ready();
document.getElementById('signup').addEventListener('click', () => {
posthog.capture('user_signed_up', { /* ... */ });
});
</script>
Option | Description | Default |
---|---|---|
validationMode |
How to handle validation errors: STRICT , WARNING , or DISABLED |
WARNING |
autoDiscoverSchemas |
Automatically find schema files (Node.js only) | true |
schemasDirectory |
Where to look for schema files | Current directory |
schemas |
Explicit schema file paths or URLs | [] |
sendValidationErrors |
Send validation errors as PostHog events | true |
environment |
Current environment (affects behavior) | development |
onValidationError |
Custom error handler callback | Console error |
- Development: Throws exceptions on validation errors
- Production: Logs errors and sends
$schema_validation_error
event - Use for critical events where data quality is essential
- Logs validation errors to console
- Sends
$schema_validation_warning
event to PostHog - Still sends the original event
- Use for gradual schema adoption
- No validation performed
- Use for performance-critical paths or testing
Use JSON Schema's allOf
to extend the base PostHog event properties:
{
"definitions": {
"CustomEvent": {
"allOf": [
{ "$ref": "./base.schema.json#/definitions/BaseEvent" },
{
"type": "object",
"properties": {
"customField": { "type": "string" }
}
}
]
}
}
}
Organize schemas by feature or domain:
schemas/
├── base.schema.json # Base event properties
├── auth.schema.json # Authentication events
├── billing.schema.json # Billing events
└── feature.schema.json # Feature usage events
HogTyped is designed as a drop-in replacement:
// Before
import posthog from 'posthog-js';
posthog.capture('event', { foo: 'bar' });
// After
import { HogTyped } from 'hogtyped';
const posthog = new HogTyped('YOUR_API_KEY');
posthog.capture('event', { foo: 'bar' }); // Same API!
Generate TypeScript types from your schemas:
npm run generate:types
This creates fully typed interfaces for all your events.
- Define schemas in JSON Schema format
- Auto-generate types (TypeScript)
- Get IDE autocomplete for event names and properties
- Validate at runtime to catch issues early
- Monitor validation errors in PostHog
- Start with WARNING mode and migrate to STRICT gradually
- Use schemas for critical business events first
- Share schemas between frontend and backend for consistency
- Version your schemas and handle breaking changes carefully
- Monitor
$schema_validation_error
events in production
See the /examples
directory for:
- Basic usage in TypeScript and Python
- Node.js server integration
- Browser usage (React, Vue, Vanilla JS)
- Complex nested schemas
- Progressive migration strategies
- Custom validation handlers
- Multi-schema organization
Method 1: npm link (Recommended for CLI testing)
# In the package directory
cd packages/js
npm link
# Now you can use the CLI globally
hogtyped init
hogtyped generate
# To unlink when done
npm unlink -g hogtyped
Method 2: Direct execution
# Build the package first
cd packages/js
npm run build
# Run the CLI directly
node ./bin/hogtyped.js init
node ./bin/hogtyped.js generate
Method 3: Install from local path
# In your test project
npm install ../path/to/hogtyped/packages/js
# Use in your code
import { generateWrapper } from 'hogtyped';
Method 4: npm pack (Test the actual package)
# In packages/js
npm pack # Creates hogtyped-0.1.0.tgz
# In your test project
npm install ../path/to/hogtyped-0.1.0.tgz
Method 1: pip install in editable mode
# In your test project with virtual environment activated
pip install -e /path/to/hogtyped/packages/python
# Now you can use it
python -m hogtyped init
python -m hogtyped generate
# Or in Python code
from hogtyped import generate_wrapper
Method 2: Direct execution
# Run directly from source
cd packages/python
python -m hogtyped init
python -m hogtyped generate
Method 3: Build and install wheel
# In packages/python
python -m build # Creates dist/hogtyped-0.1.0-py3-none-any.whl
# In your test project
pip install /path/to/hogtyped/packages/python/dist/hogtyped-0.1.0-py3-none-any.whl
# Run all tests (from monorepo root)
npm test
# JavaScript tests only
cd packages/js && npm test
# Python tests only
cd packages/python
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
python -m pytest
# Watch mode for JavaScript
cd packages/js && npm run test:watch
Create a test project to try out HogTyped:
# Create test directory
mkdir test-hogtyped && cd test-hogtyped
# Create a simple schema
mkdir schemas
cat > schemas/events.schema.json << 'EOF'
{
"events": {
"button_clicked": {
"type": "object",
"properties": {
"button_id": { "type": "string" },
"page": { "type": "string" }
},
"required": ["button_id"]
}
}
}
EOF
# For TypeScript
npx /path/to/hogtyped/packages/js/bin/hogtyped.js generate
# For Python
python /path/to/hogtyped/packages/python/hogtyped generate
# Check the generated files
cat src/posthog.generated.ts # TypeScript
cat posthog_generated.py # Python
We welcome contributions! Please see our Contributing Guide.
MIT License - see LICENSE for details.