Helper functions for working with Directus data structures, templates, and relationships. These utilities are re-exported from the @directus/utils package.
Extract field names from Directus template strings, commonly used in display templates and computed fields.
/**
* Extract field names from a Directus template string
* @param template - Template string containing field references in {{ field }} format
* @returns Array of field names found in the template
*/
function getFieldsFromTemplate(template?: string | null): string[];Usage Examples:
import { getFieldsFromTemplate } from '@directus/extensions-sdk';
// Extract fields from display template
const template = "{{first_name}} {{last_name}} ({{email}})";
const fields = getFieldsFromTemplate(template);
// Result: ['first_name', 'last_name', 'email']
// Handle complex templates with nested objects
const complexTemplate = "{{user.name}} - {{status}} - {{created_on}}";
const complexFields = getFieldsFromTemplate(complexTemplate);
// Result: ['user.name', 'status', 'created_on']
// Handle empty or null templates
const emptyFields = getFieldsFromTemplate(null);
// Result: []
// Use in display extension
export default defineDisplay({
id: 'my-template-display',
name: 'Template Display',
component: TemplateComponent,
options: [
{
field: 'template',
name: 'Template',
type: 'string',
meta: {
interface: 'input',
note: 'Use {{field_name}} syntax for dynamic values'
}
}
],
types: ['alias'],
setup(props) {
// Automatically determine which fields are needed
const requiredFields = computed(() =>
getFieldsFromTemplate(props.template)
);
return { requiredFields };
}
});Determine the type of relationship between collections based on field configuration.
/**
* Determine the relationship type between collections based on field configuration
* @param options - Object containing relation, collection, and field information
* @param options.relation - The relation configuration object
* @param options.collection - The current collection name or null
* @param options.field - The field name in the current collection
* @returns String indicating the relationship type ('m2o', 'o2m', 'm2a') or null if not relational
*/
function getRelationType(options: {
relation: Relation;
collection: string | null;
field: string;
}): 'm2o' | 'o2m' | 'm2a' | null;
interface Relation {
/** The collection containing the relation field */
collection: string;
/** The field name that holds the relation */
field: string;
/** The related collection, null for Many-to-Any relations */
related_collection: string | null;
/** Additional metadata for the relation */
meta?: {
/** Field name on the "one" side of the relation */
one_field?: string;
/** Field that stores collection name in Many-to-Any relations */
one_collection_field?: string;
/** Allowed collections for Many-to-Any relations */
one_allowed_collections?: string[];
/** Additional relation metadata */
[key: string]: any;
} | null;
}Usage Examples:
import { getRelationType } from '@directus/extensions-sdk';
// Use in interface or layout extensions
export default defineInterface({
id: 'my-relational-interface',
name: 'Relational Interface',
component: RelationalComponent,
types: ['integer'],
relational: true,
setup(props) {
// Get relation information from props or context
const relationType = computed(() =>
getRelationType({
relation: props.relation,
collection: props.collection,
field: props.field
})
);
// Adapt interface behavior based on relationship type
const isOneToMany = computed(() => relationType.value === 'o2m');
const isManyToOne = computed(() => relationType.value === 'm2o');
const isManyToAny = computed(() => relationType.value === 'm2a');
return {
relationType,
isOneToMany,
isManyToOne,
isManyToAny
};
}
});
// Use in layout extensions for relationship-aware displays
export default defineLayout({
id: 'relationship-layout',
name: 'Relationship Layout',
component: RelationshipLayoutComponent,
setup(props) {
const relationType = computed(() =>
getRelationType({
relation: props.relation,
collection: props.collection,
field: props.field
})
);
// Configure layout based on relationship type
const layoutConfig = computed(() => {
switch (relationType.value) {
case 'o2m':
return { showCreate: true, showEdit: true, showDelete: true };
case 'm2o':
return { showCreate: false, showEdit: true, showDelete: false };
case 'm2a':
return { showCreate: true, showEdit: false, showDelete: true };
default:
return { showCreate: false, showEdit: false, showDelete: false };
}
});
return { relationType, layoutConfig };
}
});These utilities are frequently used together in extension development:
import {
getFieldsFromTemplate,
getRelationType,
useCollection,
useItems
} from '@directus/extensions-sdk';
export default defineDisplay({
id: 'smart-relationship-display',
name: 'Smart Relationship Display',
component: SmartRelationshipComponent,
types: ['integer', 'string'],
relational: true,
options: [
{
field: 'display_template',
name: 'Display Template',
type: 'string',
meta: {
interface: 'input',
note: 'Template for displaying related items: {{field_name}}'
}
}
],
setup(props) {
const collection = useCollection();
const items = useItems();
// Determine relationship characteristics
const relationType = computed(() =>
getRelationType({
relation: props.relation,
collection: props.collection,
field: props.field
})
);
const isRelational = computed(() =>
relationType.value !== null && ['m2o', 'o2m', 'm2a'].includes(relationType.value)
);
// Extract required fields from template
const templateFields = computed(() =>
getFieldsFromTemplate(props.display_template)
);
// Fetch related items with only required fields
const fetchRelatedItems = async () => {
if (!isRelational.value || !templateFields.value.length) {
return [];
}
return await items.fetchItems({
collection: collection.related,
fields: templateFields.value,
// ... other parameters
});
};
return {
relationType,
isRelational,
templateFields,
fetchRelatedItems
};
}
});These utilities integrate seamlessly with Directus core functionality:
The utilities are particularly valuable when building extensions that need to: