Field Directives
Field directives are special comments that control how individual fields are generated across the entire toolchain: backend DTOs, services, and React Admin components.
Overview
Directives are placed in triple-slash comments (///) immediately above a field:
model User {
id String @id @default(uuid())
/// @tg_format(email)
email String @unique
/// @tg_upload(image)
avatar String?
/// @tg_readonly
ipAddress String?
}
Available Directives
@tg_format
Controls string field formatting and validation.
Syntax:
/// @tg_format(email|url|password|tel)
Supported formats:
email– Email validation and email inputurl– URL validation and URL inputpassword– Password input (hidden)tel– Telephone validation and tel input
Example:
model User {
/// @tg_format(email)
email String @unique
/// @tg_format(url)
website String?
/// @tg_format(password)
password String
/// @tg_format(tel)
phone String?
}
Generated DTO:
export class CreateUserTgDto {
@IsEmail()
@IsNotEmpty()
email: string;
@IsUrl()
@IsOptional()
website?: string;
@IsString()
@MinLength(8)
@IsNotEmpty()
password: string;
@Matches(/^[0-9+()\s-]+$/)
@IsOptional()
phone?: string;
}
Generated Dashboard Input:
<TextInput source="email" type="email" required />
<TextInput source="website" type="url" />
<TextInput source="password" type="password" required />
<TextInput source="phone" type="tel" />
@tg_upload
Enables file upload functionality for string fields that store file paths or URLs.
Syntax:
/// @tg_upload(image|file)
Supported types:
image– Image files with preview (addsaccept="image/*")file– Any file type
Example:
model Product {
id String @id @default(uuid())
name String
/// @tg_upload(image)
thumbnail String?
/// @tg_upload(file)
datasheet String?
}
Generated Dashboard Input:
// For image
<FileInput source="thumbnail" accept="image/*">
<ImageField source="src" title="title" />
</FileInput>
// For file
<FileInput source="datasheet">
<FileField source="src" title="title" />
</FileInput>
Data Provider Behavior:
When a form is submitted:
- The
FileInputvalue (a File object) is detected - The file is uploaded to
POST /uploadendpoint - The response URL replaces the File object
- The model API receives a simple string URL
Backend DTO:
export class CreateProductTgDto {
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@IsOptional()
thumbnail?: string; // Receives URL string
@IsString()
@IsOptional()
datasheet?: string; // Receives URL string
}
Note: You must implement POST /upload endpoint in your backend:
@Controller('upload')
export class UploadController {
@Post()
@UseInterceptors(FileInterceptor('file'))
async upload(@UploadedFile() file: Express.Multer.File) {
// Save file and return URL
return { url: `/uploads/${file.filename}` };
}
}
@tg_readonly
Marks a field as read-only, preventing it from being edited in forms.
Syntax:
/// @tg_readonly
Example:
model User {
id String @id @default(uuid())
name String
email String @unique
/// @tg_readonly
ipAddress String?
/// @tg_readonly
userAgent String?
createdAt DateTime @default(now())
}
Generated Dashboard Input:
<TextInput source="name" required />
<TextInput source="email" type="email" required />
<TextInput source="ipAddress" disabled readOnly />
<TextInput source="userAgent" disabled readOnly />
Data Provider Behavior:
Read-only fields are stripped from create/update payloads:
// User tries to submit
{ name: 'John', email: 'john@example.com', ipAddress: '1.2.3.4' }
// Data provider sends
{ name: 'John', email: 'john@example.com' }
// ipAddress is removed
Use Cases:
- Audit fields (IP address, user agent)
- Computed fields
- System-managed fields
- Historical data
Combining Directives
You can combine multiple directives on the same field:
model User {
/// @tg_format(url)
/// @tg_readonly
profileUrl String?
}
This creates a URL field that is displayed but cannot be edited.
Inline Validation Comments
In addition to field directives, you can add validation through inline comments:
model User {
username String // @min(3) @max(20)
/// @tg_format(email)
email String @unique // @pattern(/^[^\s@]+@/)
bio String? // @max(500)
}
Generated DTO:
export class CreateUserTgDto {
@IsString()
@MinLength(3)
@MaxLength(20)
@IsNotEmpty()
username: string;
@IsEmail()
@Matches(/^[^\s@]+@/)
@IsNotEmpty()
email: string;
@IsString()
@MaxLength(500)
@IsOptional()
bio?: string;
}
See Prisma Setup Guide for all validation options.
Generated Metadata
Field directives are exported to the dashboard at runtime:
File: src/dashboard/src/providers/fieldDirectives.generated.ts
export const fieldDirectives = {
users: {
email: { tgFormat: 'email' },
avatar: { tgUpload: 'image' },
ipAddress: { tgReadOnly: true },
},
products: {
thumbnail: { tgUpload: 'image' },
datasheet: { tgUpload: 'file' },
},
};
This file is auto-generated—never edit it manually. Regenerate with:
tgraph dashboard
Directive Processing
Directives are processed in this order:
- Parse –
PrismaFieldParserextracts directives from comments - Enrich – Directives decorate the
PrismaFieldobject - Generate Backend – DTO and service generators read directive metadata
- Generate Frontend – Component generators create appropriate inputs
- Export Metadata – Runtime metadata is written to
fieldDirectives.generated.ts - Runtime – Data provider enforces upload and read-only behavior
Examples
User Profile with Avatar Upload
// @tg_label(name)
// @tg_form()
model User {
id String @id @default(uuid())
name String // @min(2) @max(100)
/// @tg_format(email)
email String @unique
/// @tg_upload(image)
avatar String?
/// @tg_format(url)
website String?
bio String? // @max(500)
}
Product with Multiple Files
// @tg_form()
model Product {
id String @id @default(uuid())
name String // @min(3) @max(200)
description String
/// @tg_upload(image)
thumbnail String?
/// @tg_upload(image)
gallery String[] // Multiple images
/// @tg_upload(file)
manual String?
price Float
}
Audit Trail
// @tg_form()
model Order {
id String @id @default(uuid())
orderNumber String @unique
amount Float
status Status @default(PENDING)
/// @tg_readonly
ipAddress String?
/// @tg_readonly
userAgent String?
/// @tg_readonly
createdBy String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Best Practices
1. Use @tg_format for Semantic Fields
// Good
/// @tg_format(email)
email String
// Less ideal
email String // No semantic validation
2. Combine @tg_upload with Optional
// Good - uploads are usually optional
/// @tg_upload(image)
avatar String?
// Avoid - required uploads are harder to test
/// @tg_upload(image)
avatar String
3. Mark System Fields as @tg_readonly
// Good
/// @tg_readonly
ipAddress String?
/// @tg_readonly
lastLoginAt DateTime?
// Avoid - users shouldn't edit these
ipAddress String?
4. Document Complex Directives
/// Profile photo uploaded to S3
/// @tg_upload(image)
avatar String?
/// User's personal website
/// @tg_format(url)
website String?
Troubleshooting
Directive Not Applied
Problem: Directive doesn’t affect generation.
Solution: Ensure you use triple-slash comments (///), not double-slash (//):
// Wrong
// @tg_format(email)
email String
// Correct
/// @tg_format(email)
email String
Upload Not Working
Problem: File upload fails or sends File object to API.
Solution:
- Verify
POST /uploadendpoint exists - Check
fieldDirectives.generated.tsincludes your field - Ensure data provider is configured correctly
Read-only Field Still Editable
Problem: Field marked @tg_readonly can still be edited.
Solution:
- Regenerate dashboard:
tgraph dashboard - Verify
fieldDirectives.generated.tscontains the field - Check data provider is using the directive metadata
Next Steps
- Prisma Setup – Learn about model-level directives
- Customization – Extend generated components
- File Uploads Recipe – Complete upload implementation