@memberjunction/codegen-lib
v2.129.0
Published
Library used by the CodeGen executable to generate code for the MemberJunction platform. Contains a reusable object model that can be called by any other server-side application/library.
Keywords
Readme
@memberjunction/codegen-lib
🚀 The most sophisticated code generation engine you've ever seen - automatically transforms your database schema into a complete, type-safe, full-stack application with AI-powered intelligence.
What Makes This Badass?
MemberJunction's CodeGen doesn't just generate boilerplate code. It's an AI-powered, metadata-driven architecture that creates bulletproof, production-ready applications from your database schema with zero manual intervention.
🧠 AI-Powered Intelligence
- CHECK Constraint Translation: Our AI automatically translates complex SQL CHECK constraints into perfect TypeScript union types and Zod validation schemas
- Smart Type Inference: Analyzes relationships and generates contextually appropriate Angular form controls (dropdowns, search boxes, checkboxes)
- Intelligent Naming: AI-driven naming conventions ensure your generated code follows best practices
⚡ Synchronization Across Everything
Watch your database changes instantly propagate through your entire stack:
Database Schema Change → TypeScript Entities → Angular Forms → SQL Procedures → GraphQL SchemaOne command. Complete synchronization. Zero breaking changes.
🎯 What Gets Generated (Automatically)
- TypeScript Entity Classes with full type safety and validation
- Angular Form Components with proper field types and validation
- SQL Stored Procedures for all CRUD operations
- Database Views with optimized joins and indexing
- GraphQL Schemas and resolvers
- Zod Validation Schemas from SQL constraints
- Complete API Endpoints with type-safe parameters
Installation
npm install @memberjunction/codegen-libThe Magic in Action
From This SQL Constraint:
ALTER TABLE [AIPrompt]
ADD [PromptRole] nvarchar(20) NOT NULL
CONSTRAINT [CK_AIPrompt_PromptRole] CHECK ([PromptRole] IN (N'System', N'User', N'Assistant', N'SystemOrUser'))To This TypeScript (Automatically):
PromptRole: z.union([
z.literal('System'),
z.literal('User'),
z.literal('Assistant'),
z.literal('SystemOrUser')
]).describe('Determines how the prompt is used in conversation...')To This Angular Form (Automatically):
<mj-form-field
[record]="record"
FieldName="PromptRole"
Type="dropdownlist" // AI chose dropdown based on constraint
[EditMode]="EditMode"
></mj-form-field>To This SQL Procedure (Automatically):
CREATE PROCEDURE [spCreateAIPrompt]
@PromptRole nvarchar(20),
-- 20+ other parameters auto-generated
AS BEGIN
-- Complete CRUD logic with validation
ENDAll from ONE schema change. All type-safe. All production-ready.
Quick Start - Watch The Magic
import { initializeConfig, runCodeGen } from '@memberjunction/codegen-lib';
// Initialize configuration
await initializeConfig();
// Generate your entire application stack
await runCodeGen();
// That's it. Seriously.Your database schema just became:
- ✅ 295+ TypeScript entity classes with full validation
- ✅ Complete Angular UI with smart form controls
- ✅ All SQL stored procedures for every operation
- ✅ GraphQL API with type-safe resolvers
- ✅ Perfect type safety across your entire stack
Core Capabilities
🏗️ Entity Subclass Generation
Generates bullet-proof TypeScript classes from your database schema:
// Auto-generated from your schema
export class AIPromptEntity extends BaseEntity {
// 30+ properties with perfect types
PromptRole: 'System' | 'User' | 'Assistant' | 'SystemOrUser';
// AI-powered validation from CHECK constraints
validate(): ValidationResult {
return this.validateWithZod(AIPromptSchema);
}
}🎨 Angular Component Generation
Creates production-ready Angular forms with intelligent field types:
// Auto-detects relationships and creates search components
<mj-form-field
FieldName="CategoryID"
Type="textbox" // Smart field type selection
LinkType="Record" // Auto-detected relationship
LinkComponentType="Search" // AI chose search over dropdown
></mj-form-field>🗃️ SQL Script Generation
Generates optimized database objects with best practices:
-- Auto-generated indexes for performance
CREATE INDEX IDX_AUTO_MJ_FKEY_AIPrompt_CategoryID
ON [AIPrompt] ([CategoryID]);
-- Complete CRUD procedures with validation
CREATE PROCEDURE [spCreateAIPrompt]
@PromptRole nvarchar(20) -- Validated against CHECK constraint
-- Full implementation auto-generated🌲 Automatic Recursive Hierarchy Support
CodeGen automatically detects self-referential foreign keys and generates Root{FieldName} columns in base views using efficient recursive CTEs. This enables instant root node lookup for hierarchical data structures with zero overhead when not selected.
How It Works:
For any table with a self-referential foreign key (like ParentTaskID → Task.ID), CodeGen automatically:
- Detects the recursive relationship - Identifies foreign keys where
RelatedEntityID === entity.ID - Generates a recursive CTE - Creates SQL that traverses the hierarchy to find the root
- Adds Root columns - Exposes
Root{FieldName}in the base view (e.g.,RootParentTaskID) - Zero-overhead when unused - SQL optimizer eliminates the CTE when column not selected
Example - Task Hierarchy:
CREATE TABLE [Task] (
[ID] uniqueidentifier PRIMARY KEY,
[ParentTaskID] uniqueidentifier FOREIGN KEY REFERENCES [Task]([ID]),
[Name] nvarchar(255)
);CodeGen Automatically Generates:
CREATE VIEW [vwTasks]
AS
WITH
CTE_RootParentTaskID AS (
-- Anchor: rows with no parent (root nodes)
SELECT
[ID],
[ID] AS [RootParentTaskID]
FROM
[__mj].[Task]
WHERE
[ParentTaskID] IS NULL
UNION ALL
-- Recursive: traverse up the hierarchy
SELECT
child.[ID],
parent.[RootParentTaskID]
FROM
[__mj].[Task] child
INNER JOIN
CTE_RootParentTaskID parent ON child.[ParentTaskID] = parent.[ID]
)
SELECT
t.*,
CTE_RootParentTaskID.[RootParentTaskID] -- Auto-generated root column
FROM
[__mj].[Task] AS t
LEFT OUTER JOIN
CTE_RootParentTaskID
ON
t.[ID] = CTE_RootParentTaskID.[ID]Benefits:
- ✅ Automatic Detection - No configuration needed, works for any recursive FK
- ✅ Multiple Recursive FKs - Handles tables with multiple self-referential relationships
- ✅ SQL Optimizer Magic - CTE only executes when
RootParentTaskIDis selected - ✅ Always Correct - No stale data (unlike computed columns or triggers)
- ✅ TypeScript Integration - Root fields automatically appear in entity classes
- ✅ Naming Convention - Consistent
Root{FieldName}pattern across all entities
Use Cases:
- Organizational Charts -
Employee.ManagerID→RootManagerIDfinds CEO - Task Hierarchies -
Task.ParentTaskID→RootParentTaskIDfinds root project - Category Trees -
Category.ParentCategoryID→RootParentCategoryIDfinds top level - Comment Threads -
Comment.ParentCommentID→RootParentCommentIDfinds original post - Bill of Materials -
Part.ParentPartID→RootParentPartIDfinds top-level assembly
Performance Note:
The CTE approach is ideal for read-heavy workloads (typical in business applications). The SQL optimizer completely eliminates the CTE from the execution plan when the root column isn't selected, meaning zero overhead for queries that don't need hierarchy information.
🎯 Smart Delete Procedures with Cascade Handling
Our generated delete procedures are production-grade with intelligent handling of:
1. Result Feedback - Know What Happened
-- Returns NULL for all PKs when no record found
IF @@ROWCOUNT = 0
SELECT NULL AS [CustomerID], NULL AS [OrderID]
ELSE
SELECT @CustomerID AS [CustomerID], @OrderID AS [OrderID]2. Cascade Deletes via Stored Procedure Calls
Instead of basic DELETE statements, we generate cursor-based cascade operations that respect your business logic:
-- BAD: Direct DELETE (bypasses business logic)
DELETE FROM OrderItems WHERE OrderID = @OrderID
-- GOOD: Cursor-based SP calls (respects custom logic)
DECLARE @RelatedItemID INT
DECLARE cascade_delete_OrderItem_cursor CURSOR FOR
SELECT [ItemID] FROM [OrderItems] WHERE [OrderID] = @OrderID
OPEN cascade_delete_OrderItem_cursor
FETCH NEXT FROM cascade_delete_OrderItem_cursor INTO @RelatedItemID
WHILE @@FETCH_STATUS = 0
BEGIN
-- Calls YOUR stored procedure, enabling N-level cascades
EXEC [spDeleteOrderItem] @RelatedItemID
FETCH NEXT FROM cascade_delete_OrderItem_cursor INTO @RelatedItemID
END
CLOSE cascade_delete_OrderItem_cursor
DEALLOCATE cascade_delete_OrderItem_cursorBenefits:
- ✅ Respects custom delete logic in related entities
- ✅ Enables multi-level cascades (if OrderItem has its own cascades)
- ✅ Maintains referential integrity through proper SP calls
- ✅ Clear result feedback - NULL means no record deleted
3. Nullable Foreign Key Handling
For nullable FKs, we update via stored procedures too:
-- Fetch all fields, update only the FK to NULL
DECLARE cascade_update_Customer_cursor CURSOR FOR
SELECT * FROM [Customers] WHERE [RegionID] = @RegionID
-- In cursor loop:
SET @UpdateRegionID = NULL -- Only FK changes
EXEC [spUpdateCustomer] @CustomerID, @Name, @Email, @UpdateRegionID4. Configuration Error Detection
CodeGen warns you about misconfigured cascade scenarios:
-- WARNING: Orders has non-nullable FK to Customer but doesn't allow delete API
-- This will cause a referential integrity violationThe warnings appear both in generated SQL and console output during generation.
🔄 Smart Update Procedures with Result Validation
Our update procedures also provide clear feedback when operating on non-existent records:
-- Check if update affected any rows
IF @@ROWCOUNT = 0
-- Return empty result set (maintains column structure)
SELECT TOP 0 * FROM [vwCustomer] WHERE 1=0
ELSE
-- Return the updated record with calculated fields
SELECT * FROM [vwCustomer] WHERE [CustomerID] = @CustomerIDWhy This Matters:
- Empty result set = Record not found (update failed)
- Record returned = Update successful
- Maintains schema = Calling code doesn't break
- Includes calculated fields = Get the latest computed values
🌐 GraphQL Schema Generation
Creates type-safe GraphQL APIs from your entities:
type AIPrompt {
id: ID!
promptRole: PromptRoleEnum! # Auto-generated from CHECK constraint
category: AIPromptCategory # Auto-resolved relationships
}
enum PromptRoleEnum {
SYSTEM
USER
ASSISTANT
SYSTEMORUSER
}🔬 Database Schema Introspection
Reverse-engineers your entire database into metadata:
const schemaInfo = await analyzeSchema(connection);
// Discovers tables, relationships, constraints, indexes
// Feeds AI engine for intelligent code generationAdvanced Features That Blow Minds
🎨 AI-Powered Form Layout Generation
CodeGen uses AI to automatically organize entity fields into semantic categories with icons, creating intuitive form layouts without manual configuration.
What It Does:
- Field Categorization - Groups fields into domain-specific categories (e.g., "Billing Address", "Pricing and Charges", "System Metadata")
- Category Icons - Assigns Font Awesome icons to each category for visual navigation
- Category Descriptions - Generates tooltip descriptions for UX enhancement
- Entity Importance Analysis - Determines if entities should appear in navigation for new users
- Smart Display Names - Converts technical field names to user-friendly labels (e.g.,
BillToAddress1→ "Billing Address Line 1")
Entity Importance Detection:
The AI uses FK ratio analysis to classify entities:
| Entity Type | FK Ratio | Example | DefaultForNewUser | |-------------|----------|---------|-------------------| | Primary | 10-30% | Contact, Order, Deal | ✅ Yes | | Supporting | 20-40% | OrderItem, Address | Sometimes | | Reference/Type | 0-20% | OrderStatus, ContactType | ❌ No | | Junction | 40-80% | UserRole, ContactAccount | ❌ No |
Stability Guarantees:
The system enforces category stability to prevent unnecessary churn on existing entities:
What's Preserved (Never Changed by AI):
- ✅ Existing category names - AI cannot rename "Personal Info" to "Personal Details"
- ✅ Existing category icons - Icons set by admins or previous runs are preserved
- ✅ Existing category descriptions - Descriptions are only added, never modified
What AI Can Do:
- ✅ Assign NEW fields to existing categories
- ✅ Assign NEW fields to NEW categories (when no existing category fits)
- ✅ Move existing fields between EXISTING categories (with discretion)
- ❌ Move existing fields to NEW categories (blocked - prevents renaming)
Enforcement Example:
Field 'Email' is in category 'Contact Info'
LLM suggests moving to 'Communication Details' (new category)
→ REJECTED: Cannot move existing field to new category
→ Field stays in 'Contact Info'Control Flags on EntityField:
AutoUpdateCategory- If FALSE, field's category is lockedAutoUpdateDisplayName- If FALSE, display name is lockedAutoUpdateIsNameField- If FALSE, name field designation is locked
DefaultForNewUser - Only for New Entities:
The DefaultForNewUser flag is only set when an entity is first created, not on subsequent updates. This ensures:
- Admins retain full control over navigation visibility
- CodeGen won't override manual admin decisions
- Existing entity configurations remain stable
Storage Format:
Category information is stored in EntitySetting with two formats for compatibility:
New Format (FieldCategoryInfo):
{
"Billing Address": {
"icon": "fa fa-file-invoice",
"description": "Address for invoice delivery and billing correspondence"
},
"System Metadata": {
"icon": "fa fa-cog",
"description": "System-managed audit and tracking fields"
}
}Legacy Format (FieldCategoryIcons) - maintained for backwards compatibility:
{
"Billing Address": "fa fa-file-invoice",
"System Metadata": "fa fa-cog"
}🤖 AI-Powered CHECK Constraint Translation
Our AI doesn't just copy constraints - it understands intent:
-- Complex constraint
CHECK ([Status] IN ('Draft', 'Published', 'Archived')
AND [PublishedAt] IS NOT NULL WHEN [Status] = 'Published')Becomes perfect TypeScript:
Status: z.union([z.literal('Draft'), z.literal('Published'), z.literal('Archived')])
.refine((status, ctx) => {
if (status === 'Published' && !this.PublishedAt) {
ctx.addIssue({ code: 'custom', message: 'Published items must have PublishedAt' });
}
})🔄 Real-Time Synchronization
Change your database schema → Everything updates automatically:
- Flyway migration executes
- CodeGen detects changes
- Regenerates affected code
- Type safety maintained across entire stack
- Zero manual intervention
🚀 Performance Optimization
- Intelligent caching prevents unnecessary regeneration
- Incremental updates for changed entities only
- Optimized SQL with proper indexing strategies
- Lazy loading for large schema datasets
🔒 Enterprise-Grade Security
- Parameterized queries in all generated SQL
- Input validation at every layer
- SQL injection protection built-in
- Type-safe APIs prevent runtime errors
Configuration
Create a .memberjunctionrc file:
{
"memberjunction": {
"database": {
"server": "localhost",
"database": "YourDatabase",
"trustedConnection": true
},
"directories": {
"output": "./generated",
"entities": "./generated/entities",
"actions": "./generated/actions",
"angular": "./generated/angular",
"sql": "./generated/sql"
},
"ai": {
"enabled": true,
"provider": "openai" // Powers constraint translation
}
}
}Real-World Example
Starting with a simple table:
CREATE TABLE [Customer] (
[ID] uniqueidentifier PRIMARY KEY DEFAULT newsequentialid(),
[Name] nvarchar(255) NOT NULL,
[Status] nvarchar(20) CHECK ([Status] IN ('Active', 'Inactive', 'Suspended')),
[CreatedAt] datetimeoffset DEFAULT getutcdate()
);One CodeGen run produces:
TypeScript Entity (175 lines)
export class CustomerEntity extends BaseEntity {
Status: 'Active' | 'Inactive' | 'Suspended';
// + complete validation, save methods, relationships
}Angular Component (89 lines)
@Component({
template: `Complete form with validation and smart controls`
})
export class CustomerDetailsComponent {
// Ready for production use
}SQL Procedures (200+ lines)
-- spCreateCustomer, spUpdateCustomer, spDeleteCustomer
-- Complete with validation and error handlingGraphQL Schema (45 lines)
type Customer {
# Complete type-safe schema
}Total: 500+ lines of production code from 6 lines of SQL.
API Reference
Core Functions
// Generate everything at once
await runCodeGen();
// Generate specific components
await generateEntitySubClasses(options);
await generateAngularEntityCode(options);
await generateSQLScripts(options);
await generateGraphQLServerCode(options);Entity Subclass Generation
import { generateEntitySubClasses } from '@memberjunction/codegen-lib';
const result = await generateEntitySubClasses({
outputDirectory: './generated/entities',
generateLoader: true,
generateCustomEntityClasses: true,
aiEnhanced: true, // Enable AI features
incrementalMode: true, // Only update changed entities
validateGenerated: true // Compile-check generated code
});Action Subclass Generation
import { generateActionSubClasses } from '@memberjunction/codegen-lib';
const result = await generateActionSubClasses({
outputDirectory: './generated/actions',
generateLoader: true
});GraphQL Server Generation
import { generateGraphQLServerCode } from '@memberjunction/codegen-lib';
await generateGraphQLServerCode({
outputDirectory: './generated/graphql',
entities: entityMetadata
});SQL Code Generation
import { generateSQLScripts } from '@memberjunction/codegen-lib';
await generateSQLScripts({
outputDirectory: './generated/sql',
includeStoredProcedures: true,
includeViews: true
});Angular Component Generation
import { generateAllAngularEntityCode } from '@memberjunction/codegen-lib';
await generateAllAngularEntityCode({
outputDirectory: './generated/angular',
entities: entityMetadata
});Performance Stats
On a typical MemberJunction database with 150+ tables:
- Entity Generation: 2.3 seconds
- Angular Components: 4.7 seconds
- SQL Procedures: 1.8 seconds
- Total Stack Generation: <10 seconds
For 295 entity classes and thousands of generated files.
Integration with MemberJunction Ecosystem
Works seamlessly with:
@memberjunction/core- Entity framework@memberjunction/ai- AI-powered features@memberjunction/angular-explorer- UI framework@memberjunction/graphql-dataprovider- API layer@memberjunction/sqlserver-dataprovider- Data access
Advanced Features
Custom Templates
You can provide custom templates for code generation:
import { setCustomTemplate } from '@memberjunction/codegen-lib';
setCustomTemplate('entity', myCustomEntityTemplate);Schema Analysis
import { analyzeSchema } from '@memberjunction/codegen-lib';
const schemaInfo = await analyzeSchema(databaseConnection);
// Work with schema informationProgress Tracking
import { onProgress } from '@memberjunction/codegen-lib';
onProgress((status) => {
console.log(`Progress: ${status.message} (${status.percentage}%)`);
});Force Regeneration with Smart Filtering
Need to regenerate specific SQL objects without schema changes? Our force regeneration feature gives you surgical precision over what gets regenerated:
// In mj.config.cjs
forceRegeneration: {
enabled: true,
// Filter to specific entities using SQL WHERE clause
entityWhereClause: "SchemaName = 'CRM' AND __mj_UpdatedAt >= '2025-06-24'",
// Granular control over what gets regenerated
baseViews: true, // Regenerate base views
spCreate: false, // Skip create procedures
spUpdate: true, // Regenerate update procedures
spDelete: false, // Skip delete procedures
indexes: true, // Regenerate foreign key indexes
fullTextSearch: false // Skip full-text search components
}Common Scenarios:
Regenerate views for recently modified entities:
forceRegeneration: {
enabled: true,
entityWhereClause: "__mj_UpdatedAt >= '2025-06-24 22:00:00'",
baseViews: true
}Regenerate all stored procedures for a specific schema:
forceRegeneration: {
enabled: true,
entityWhereClause: "SchemaName = 'Sales'",
allStoredProcedures: true
}Regenerate specific SQL object for a single entity:
forceRegeneration: {
enabled: true,
entityWhereClause: "Name = 'Customer'",
spUpdate: true // Just regenerate the update procedure
}Regenerate everything (no filtering):
forceRegeneration: {
enabled: true,
// No entityWhereClause = regenerate for ALL entities
allStoredProcedures: true,
baseViews: true,
indexes: true
}How It Works:
- Entity Filtering: The
entityWhereClauseruns against the Entity metadata table to select which entities qualify - Type Filtering: Individual flags control which SQL object types get regenerated
- Smart Combination: Only regenerates the intersection (selected entities AND selected types)
- Error Handling: Invalid WHERE clauses stop execution with clear error messages
Error Handling
The library provides comprehensive error handling:
try {
await runCodeGen();
} catch (error) {
if (error.code === 'CONFIG_NOT_FOUND') {
// Handle missing configuration
} else if (error.code === 'DB_CONNECTION_FAILED') {
// Handle database connection errors
}
}Why This Changes Everything
Before MemberJunction CodeGen:
- Weeks of manual entity creation
- Inconsistent validation logic
- Type mismatches between layers
- Manual Angular form creation
- Brittle SQL procedures
- Schema changes break everything
After MemberJunction CodeGen:
- 10 seconds to regenerate entire stack
- Perfect type safety across all layers
- AI-powered intelligent code generation
- Zero manual intervention
- Production-ready from day one
Best Practices
- Configuration Management - Use environment-specific configuration files
- Output Organization - Keep generated code in separate directories
- Version Control - Consider excluding generated files from version control
- Regular Updates - Regenerate code when metadata changes
- Custom Extensions - Extend generated classes rather than modifying them
Contributing
When contributing to this package:
- Test with real schemas - We generate production apps
- Maintain AI accuracy - Constraint translation must be perfect
- Performance matters - Large schemas must generate quickly
- Type safety is sacred - Never compromise type correctness
License
This package is part of the MemberJunction ecosystem and follows the same licensing terms.
Ready to experience the future of application development?
npm install @memberjunction/codegen-libYour database schema deserves better than manual code generation. Give it the AI-powered, production-ready, full-stack treatment it deserves.
