Frequently Asked Questions
General
What is fluent-gen-ts?
A code generator that creates type-safe fluent builders from TypeScript interfaces and types, eliminating boilerplate and reducing testing complexity.
Why use builders instead of plain objects?
Benefits:
- Type safety - Catch errors at compile time
- Discoverability - IDE autocomplete shows available methods
- Testability - Easier to create test data
- Maintainability - Interface changes automatically update builders
- Readability - Fluent API is self-documenting
Do I commit generated files to Git?
Recommended: Yes
Pros:
- Better IDE support (no generation needed)
- Faster CI builds
- Historical tracking of changes
- Works without fluent-gen-ts installed
Cons:
- Larger repository size
- Merge conflicts (rare)
Alternative: No (regenerate on demand)
Add to .gitignore
:
/src/builders/
Add to package.json
:
{
"scripts": {
"prebuild": "fluent-gen-ts batch",
"pretest": "fluent-gen-ts batch"
}
}
Usage
How do I handle nested objects?
Nested builders are automatically generated:
interface User {
name: string;
address: Address;
}
interface Address {
street: string;
city: string;
}
// Usage - nested builders are auto-imported
const user1 = user()
.withName('Alice')
.withAddress(address().withStreet('123 Main St').withCity('NYC'))
.build();
Can I extend generated builders?
Yes, but plugins are recommended for reusable logic:
Option 1: Plugins (Recommended)
const plugin = createPlugin('my-plugin', '1.0.0')
.addMethod(method =>
method
.name('asAdmin')
.returns('this')
.implementation('return this.withRole("admin")'),
)
.build();
Option 2: Manual Extension (One-off)
class CustomUserBuilder extends UserBuilder {
asAdmin(): this {
return this.withRole('admin').withIsActive(true);
}
}
How do I handle optional vs required fields?
Optional fields in TypeScript remain optional in builders:
interface User {
id: string; // Required
email?: string; // Optional
}
// Both valid:
user().withId('123').build();
user().withId('123').withEmail('test@example.com').build();
Can I set multiple properties at once?
Yes, pass initial data to the builder:
const baseUser = {
createdAt: new Date(),
isActive: true,
};
const user1 = user(baseUser).withId('123').withName('Alice').build();
Configuration
How are builders organized in the output directory?
Generated builders are created as separate files in the configured output directory:
src/builders/
├── user.builder.ts
├── product.builder.ts
├── order.builder.ts
└── common.ts
Configure the output directory in fluentgen.config.js
:
{
generator: {
outputDir: './src/builders';
}
}
How do I customize file naming?
Use naming strategies:
{
generator: {
naming: {
convention: 'kebab-case', // user-builder.ts
suffix: 'builder'
}
}
}
Available conventions:
camelCase
- userBuilder.tskebab-case
- user-builder.tssnake_case
- user_builder.tsPascalCase
- UserBuilder.ts
How do monorepo configurations work?
fluent-gen-ts auto-detects pnpm, yarn, and npm workspaces:
{
monorepo: {
enabled: true,
dependencyResolutionStrategy: 'auto' // Recommended
}
}
See Monorepo Configuration for details.
Plugins
When should I use plugins vs manual extension?
Use Plugins When:
- Logic applies to multiple builders
- You want to share across projects
- Need automatic application on all types
- Validation, transformation, or import management
Use Manual Extension When:
- One-off customization
- Specific to single builder
- Quick prototype
Why isn't my plugin working?
Common issues:
Missing
.done()
typescript// ❌ Wrong .when(ctx => true) .setValidator('code') .when(ctx => true) // New rule, forgot .done() // ✅ Correct .when(ctx => true) .setValidator('code') .done() // Complete rule .when(ctx => true)
Rule order - Specific before generic
typescript// ❌ Wrong - generic first .when(ctx => ctx.type.isPrimitive()) // Matches everything .when(ctx => ctx.property.name === 'id') // Never reached // ✅ Correct - specific first .when(ctx => ctx.property.name === 'id') .when(ctx => ctx.type.isPrimitive())
Incorrect import paths - Must include
.js
for ESMtypescript// ❌ Wrong .addInternalTypes('../types', ['User']) // ✅ Correct .addInternalTypes('../types.js', ['User'])
See Best Practices for more.
Can plugins access runtime values?
No. Plugins work at code generation time, not runtime.
// ❌ Wrong - Can't access builder state during generation
.addMethod(method => method
.implementation(`
if (this.peek('name') === 'admin') { ... }
`)
)
// ✅ Correct - Use parameters
.addMethod(method => method
.parameter('name', 'string')
.implementation(`
if (name === 'admin') { ... }
`)
)
Performance
Are builders slower than plain objects?
Minimal overhead. Builders add ~1-5% overhead compared to object literals. In practice, this is negligible.
Benchmarks:
- Plain object: ~0.01ms per creation
- Builder: ~0.011ms per creation
- Difference: Unmeasurable in real apps
Should I use builders in production code?
Yes, builders are production-ready:
- Type-safe
- No runtime dependencies
- Minimal overhead
- Used in enterprise applications
How do I optimize large object trees?
Use partial initialization:
// Instead of building everything:
const full = order()
.withCustomer(customer().with...().with...())
.withItems([item().with...(), ...])
.build();
// Build parts separately:
const customer1 = customer().with...().build();
const items = [...]; // Reuse items
const order1 = order()
.withCustomer(customer1)
.withItems(items)
.build();
Troubleshooting
"Cannot find module" errors
Ensure ESM imports use .js
extension:
// ❌ Wrong
import { user } from './builders/user.builder';
// ✅ Correct
import { user } from './builders/user.builder.js';
Generated code has TypeScript errors
Common causes:
- Circular dependencies - Use type imports
- Missing dependencies - Install required packages
- Wrong tsconfig - Ensure
moduleResolution: "bundler"
or"node16"
Run with verbose output:
npx fluent-gen-ts batch --verbose
Types are not found during generation
Check:
- File path is correct
- Type is exported
- tsconfig.json is valid
Use scan to verify:
npx fluent-gen-ts scan "src/**/*.ts"
Integration
How do I integrate with testing frameworks?
// Vitest/Jest
import { user } from '../builders/user.builder.js';
test('should create user', () => {
const testUser = user().withId('test-id').withName('Test').build();
expect(testUser.id).toBe('test-id');
});
Can I use with Prisma/TypeORM/Drizzle?
Yes! Generate builders from your schema types:
Prisma:
import type { User } from '@prisma/client';
// Generate builder for Prisma types
TypeORM:
import { User } from './entities/user.entity.ts';
// Generate builder for entity
Does it work with React/Vue/Angular?
Yes! Builders are framework-agnostic. Use them anywhere:
// React
const [user, setUser] = useState(user().withName('Alice').build());
// Vue
const user = ref(user().withName('Bob').build());
Still Have Questions?
- Troubleshooting Guide - Common issues
- GitHub Discussions - Ask the community
- GitHub Issues - Report bugs