Skip to content

LikeC4 API

You can access and traverse your architecture model programmatically using the LikeC4 Model API.

Ensure you have likec4 in your dependencies:

npm i likec4

You can initiate LikeC4 API from a directory with source files or from a string with DSL source.

Recursively search and parse source files:

import { LikeC4 } from 'likec4'
const likec4 = await LikeC4.fromWorkspace('/path/to/workspace')

Method also accepts options:

| Property | Description | | -----------------| --------------------------------------------------------------------------------------------------- | | printErrors | if model is invalid, errors are reported to the logger (default true) | | throwIfInvalid | return rejected promise if model is invalid (default false) | | logger | Whenever to use default (console), vite logger or your custom implementation
Disable with false | | graphviz | wasm (default) or binary - use local binaries of Graphviz (“dot”) or bundled WASM | | watch | Whether to watch for changes in the workspace. (default false) | | mcp | Whether to start MCP server.
- false - do not start MCP server (default)
- "stdio" - use stdio transport,
- {"port": number} - use http transport on specified port |

Parse from the string:

import { LikeC4 } from "likec4"
const likec4 = await LikeC4.fromSource(`
specification {
element system
element user
}
model {
customer = user 'Customer'
cloud = system 'System'
}
views {
view index {
include *
}
}
`)

If you initialized LikeC4 with watch mode or enabled MCP server, you should dispose it:

import { LikeC4 } from 'likec4'
const likec4 = await LikeC4.fromWorkspace('/path/to/workspace', {
watch: true,
mcp: { port: 33335 },
})
// Cleanup resources
await likec4.dispose()

LikeC4 is automatically disposed with await using declaration:

import { LikeC4 } from 'likec4'
async function() {
await using likec4 = await LikeC4.fromWorkspace('/path/to/workspace', {
watch: true,
mcp: { port: 33335 },
})
// ...
// likec4 is disposed automatically
}

When the model is initialized, you can use the following methods to query and traverse it.

Two types of model (with similar API):

  • LikeC4Model.Computed - includes computed views (from predicates), fast, synchronous, enough to traverse but not ready for rendering.
  • LikeC4Model.Layouted - extends computed model with layout data (dimensions, positions), that is needed for rendering.
import { LikeC4 } from "likec4"
const likec4 = await LikeC4.fromSource(`....`)
// Validation errors
console.log(likec4.getErrors())
// Traverse the model
const model = likec4.computedModel()
// Get elements of some kind
const elements = model.elementsOfKind('kind1')
// Use where operator to filter elements:
// kind is 'kind1' and (tag is 'tag2' or tag is not 'tag3')
const elements = model.elementsWhere({
and: [
{ kind: 'kind1' },
{
or: [
{ tag: 'tag2' },
{
tag: {
neq: 'tag3',
},
},
],
},
],
})
// Get views that include the element
model
.element('cloud.backend.api')
.views()
// Get source elements of incoming relationships (filter by tags)
model
.element('cloud.backend.api')
.incoming() // relationships incoming to the element
.filter(r => r.isTagged('http')) // filter by tags
.map(r => r.source) // get source elements

To get layouted model:

import { LikeC4 } from "likec4"
const likec4 = await LikeC4.fromSource(`....`)
const model = await likec4.layoutedModel()
const diagram = model.view('index')
// Working with metadata (including array values)
const element = model.element('cloud.backend.api')
// Get all metadata
const allMetadata = element.getMetadata()
console.log(allMetadata)
// Output: { version: '3.2.1', tags: ['backend', 'gateway'], regions: ['us-east-1', 'eu-west-1'] }
// Get specific metadata field (could be string or string[])
const tags = element.getMetadata('tags')
if (Array.isArray(tags)) {
console.log(`Element has ${tags.length} tags: ${tags.join(', ')}`)
} else if (tags) {
console.log(`Element has single tag: ${tags}`)
}
// Filter elements by array metadata values
const elementsWithBackendTag = model.elements().filter(el => {
const tags = el.getMetadata('tags')
return Array.isArray(tags)
? tags.includes('backend')
: tags === 'backend'
})

Model API provides methods to query and traverse the whole model.

interface LikeC4Model {
/**
* Returns the root elements of the model.
*/
roots(): Element[];
/**
* Returns all elements in the model.
*/
elements(): Element[];
/**
* Returns a specific element by its FQN.
*/
element(id: Fqn): Element;
/**
* Returns all relationships in the model.
*/
relationships(): Relationship[];
/**
* Returns a specific relationship by its ID.
*/
relationship(id: RelationID): Relationship;
/**
* Returns all views in the model.
*/
views(): ReadonlyArray<LikeC4ViewModel>;
/**
* Returns a specific view by its ID.
*/
view(viewId: ViewID): LikeC4ViewModel;
/**
* Returns the parent element of given element.
* @see ancestors
*/
parent(element: ElementOrFqn): Element | null;
/**
* Get all children of the element (only direct children),
* @see descendants
*/
children(element: ElementOrFqn): Element[];
/**
* Get all sibling (i.e. same parent)
*/
siblings(element: ElementOrFqn): Element[];
/**
* Get all ancestor elements (i.e. parent, parent’s parent, etc.)
* (from closest to root)
*/
ancestors(element: ElementOrFqn): Element[];
/**
* Get all descendant elements (i.e. children, children’s children, etc.)
*/
descendants(element: ElementOrFqn): Element[];
/**
* Incoming relationships to the element and its descendants
* @see incomers
*/
incoming(element: ElementOrFqn, filter?: 'all' | 'direct' | 'to-descendants'): Relationship[];
/**
* Source elements of incoming relationships
*/
incomers(element: ElementOrFqn, filter?: 'all' | 'direct' | 'to-descendants'): Element[];
/**
* Outgoing relationships from the element and its descendants
* @see outgoers
*/
outgoing(element: ElementOrFqn, filter?: 'all' | 'direct' | 'from-descendants'): Relationship[];
/**
* Target elements of outgoing relationships
*/
outgoers(element: ElementOrFqn, filter?: 'all' | 'direct' | 'from-descendants'): Element[];
}

Check sources for methods - LikeC4Model

Elements can have metadata with both single string values and string arrays. The API provides convenient methods to access and work with this metadata:

import { LikeC4 } from "likec4"
const likec4 = await LikeC4.fromSource(`
specification {
element service
element application
}
model {
api = service 'API Gateway' {
metadata {
version '3.2.1'
maintainer 'Platform Team'
tags ['backend', 'gateway', 'microservice']
regions ['us-east-1', 'eu-west-1']
critical true
}
}
frontend = application 'Frontend' {
metadata {
framework 'React'
features ['auth', 'dashboard', 'reports']
team_members ['alice', 'bob', 'carol']
release_branch 'main'
}
}
}
`)
const model = likec4.computedModel()
// Get element and access metadata
const api = model.element('api')
// Check if element has any metadata
if (api.hasMetadata()) {
console.log('API has metadata')
// Get all metadata as an object
const metadata = api.getMetadata()
console.log('All metadata:', metadata)
// Get specific metadata fields
const version = api.getMetadata('version') // string: '3.2.1'
const tags = api.getMetadata('tags') // string[]: ['backend', 'gateway', 'microservice']
const regions = api.getMetadata('regions') // string[]: ['us-east-1', 'eu-west-1']
// Handle array metadata values
if (Array.isArray(tags)) {
console.log(`API has ${tags.length} tags:`)
tags.forEach(tag => console.log(` - ${tag}`))
// Check if specific value exists in array
if (tags.includes('backend')) {
console.log('API is tagged as backend service')
}
}
// Handle mixed metadata types
const handleMetadataValue = (key: string, value: string | string[] | undefined) => {
if (Array.isArray(value)) {
return `${key}: [${value.join(', ')}]`
} else if (value) {
return `${key}: ${value}`
}
return `${key}: undefined`
}
console.log(handleMetadataValue('version', version))
console.log(handleMetadataValue('tags', tags))
console.log(handleMetadataValue('regions', regions))
}
// Advanced filtering using metadata
const backendServices = model.elements()
.filter(element => {
const tags = element.getMetadata('tags')
return Array.isArray(tags) ? tags.includes('backend') : tags === 'backend'
})
const multiRegionServices = model.elements()
.filter(element => {
const regions = element.getMetadata('regions')
return Array.isArray(regions) && regions.length > 1
})
// Group elements by metadata values
const elementsByFramework = new Map<string, typeof model.elements>()
for (const element of model.elements()) {
const framework = element.getMetadata('framework')
if (typeof framework === 'string') {
if (!elementsByFramework.has(framework)) {
elementsByFramework.set(framework, [])
}
elementsByFramework.get(framework)!.push(element)
}
}
// Collect all unique tags from all elements
const allTags = new Set<string>()
for (const element of model.elements()) {
const tags = element.getMetadata('tags')
if (Array.isArray(tags)) {
tags.forEach(tag => allTags.add(tag))
} else if (typeof tags === 'string') {
allTags.add(tags)
}
}
console.log('All unique tags:', Array.from(allTags).sort())

API provides methods to query and traverse deployment model.

import { LikeC4 } from "likec4"
const likec4 = await LikeC4.fromSource(`....`)
const model = likec4.computedModel()
// Get deployment model
const deployment = model.deployment
// Get elements of some kind
for (const instance of deployment.instancesOf('cloud.backend.api')) {
// ...
}

View model API provides methods to query and traverse elements and relationships that are included in the view.

import { LikeC4 } from "likec4"
const likec4 = await LikeC4.fromSource(`....`)
const model = likec4.computedModel()
for (const view of model.views()) {
if (view.isDynamicView()) {
// ...
}
}

Type-safe builder available from @likec4/core/builder (and likec4/model/builder).
Builder can be used to create model programmatically and supports two styles:

import { Builder } from "@likec4/core/builder"
const m = Builder
.specification({
elements: {
actor: {
style: {
shape: 'person',
},
},
system: {},
component: {},
},
relationships: {
likes: {},
},
tags: ['tag1', 'tag2', 'tag1'],
})
.model(({ actor, system, component, relTo, rel }, _) =>
_(
actor('alice'),
actor('bob'),
rel('alice', 'bob', {
tags: ['tag1'], // you get code completion for tags
kind: 'likes', // code completion for kind
}),
system('cloud', { tags: ['tag1', 'tag2'] }).with(
component('backend').with(
component('api'),
component('db'),
// code completion for relationships
rel('cloud.backend.api', 'cloud.backend.db')
),
component('frontend').with(
relTo('cloud.backend.api')
),
),
)
)
.views(({ view, viewOf, $include, $style }, _) =>
_(
view('index', 'Index').with(
$include('cloud.*'),
),
viewOf('ui', 'cloud.ui').with(
// code completion for predicates
$include('* -> cloud.**'),
$style('cloud.ui', { color: 'red' }),
),
)
)
.toLikeC4Model()

You can mix both styles, depending on your preference and use cases.