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 likec4pnpm add likec4yarn add likec4bun add likec4You can initiate LikeC4 API from a directory with source files or from a string with DSL source.
From workspace
Section titled “From workspace”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 |
From source
Section titled “From source”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 * } }`)Dispose
Section titled “Dispose”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 resourcesawait 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.
Example
Section titled “Example”import { LikeC4 } from "likec4"
const likec4 = await LikeC4.fromSource(`....`)
// Validation errorsconsole.log(likec4.getErrors())
// Traverse the modelconst model = likec4.computedModel()
// Get elements of some kindconst 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 elementmodel .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 elementsTo 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 metadataconst 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 valuesconst elementsWithBackendTag = model.elements().filter(el => { const tags = el.getMetadata('tags') return Array.isArray(tags) ? tags.includes('backend') : tags === 'backend'})LikeC4Model
Section titled “LikeC4Model”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
Working with Element Metadata
Section titled “Working with Element Metadata”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 metadataconst api = model.element('api')
// Check if element has any metadataif (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 metadataconst 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 valuesconst 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 elementsconst 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())LikeC4DeploymentModel
Section titled “LikeC4DeploymentModel”API provides methods to query and traverse deployment model.
import { LikeC4 } from "likec4"
const likec4 = await LikeC4.fromSource(`....`)const model = likec4.computedModel()
// Get deployment modelconst deployment = model.deployment
// Get elements of some kindfor (const instance of deployment.instancesOf('cloud.backend.api')) { // ...}LikeC4ViewModel
Section titled “LikeC4ViewModel”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()) { // ... }}Model Builder
Section titled “Model Builder”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()import { Builder } from "@likec4/core/builder"
// Get composition functions for given specificationconst { model: { model, actor, system, component, rel, relTo, }, views: { view, viewOf, views, $include, $style, }, builder,} = Builder.forSpecification({ elements: { actor: { style: { shape: 'person', }, }, system: {}, component: {}, }, relationships: { likes: {}, }, tags: ['tag1', 'tag2', 'tag1'],})
const b1 = builder.with( model( actor('alice'), actor('bob'), rel('alice', 'bob', { tags: ['tag1'], kind: 'likes', }), system('cloud', { tags: ['tag1', 'tag2'] }).with( component('backend').with( component('api'), component('db'), rel('cloud.backend.api', 'cloud.backend.db') ), component('frontend').with( relTo('cloud.backend.api') ), ), ))
const b2 = b1.with( views( view('index', 'Index').with( $include('cloud.*'), ), viewOf('ui', 'cloud.ui').with( $include('* -> cloud.**'), $style('cloud.ui', { color: 'red' }), ), )).toLikeC4Model()You can mix both styles, depending on your preference and use cases.