D2 Basics Tutorial
Table of contents
What is D2? Is it that robot from star wars?
No! D2 is a diagram scripting language that turns text to diagrams. It stands for Declarative Diagramming. Declarative, as in, you describe what you want diagrammed, it generates an image (SVG)
For example, you provide this input on the right, and you get back the output on the left.
Getting Started
To install D2 on you local machine you can use a script or an installer
- Use script to install D2
# With --dry-run the install script will print the commands it will use
# to install without actually installing so you know what it's going to do.
curl -fsSL https://d2lang.com/install.sh | sh -s -- --dry-run
# If things look good, install for real.
curl -fsSL https://d2lang.com/install.sh | sh -s --
Install from source using go
go install oss.terrastruct.com/d2
For windows installation you can check out this link https://github.com/terrastruct/d2/releases for releases and installers
To check if D2 is installed correctly run command d2 in your terminal you should see something like this
Hello World D2
Let's start with the basics of D2 i.e a hello world example
- Create a file named hello.d2 in any editor of your choice and paste this text in that file
x -> y: hello world
- Navigate to that file in your terminal and run
d2 -w hello.d2 out.svg
will start a web server and serve an SVG file like this
Shapes
What are shapes? basically in every diagram that we need we need some shapes to be linked. For example in the hello world example we needed 2 boxes "x" and "y" to be linked to each other, these boxes are called as shapes in D2
Naming of shapes can be pretty simple below are a few examples of shapes
imAShape
im_a_shape
im a shape
i'm a shape
# notice that one-hyphen is not a connection
# whereas, `a--shape` would be a connection
a-shape
Avoid using double hyphen "--" while naming as that will create a connection instead.
You can also use semicolons to define multiple shapes on the same line for example this code
SQLite; Cassandra
Will generate
By default, a shape's label is the same as the shape's key. But if you want it to be different, assign a new label like
pg: PostgreSQL
will generate a shape pg with label "PostgreSQL"
By default, a shape's type is rectangle
. To specify otherwise, provide the field shape
pg: PostgreSQL
pg.shape: cloud
Will generate a cloud shape instead
Here i have listed all the available shapes and how they look in the below image
pg1: cloud
pg1.shape: cloud
pg2: rectangle
pg2.shape: rectangle
pg3: square
pg3.shape: square
pg4: page
pg4.shape: page
pg5: parallelogram
pg5.shape: parallelogram
pg6: document
pg6.shape: document
pg7: cylinder
pg7:shape: cylinder
pg8: queue
pg8.shape: queue
pg9: package
pg9.shape: package
pg10: step
pg10.shape: step
pg11: callout
pg11.shape: callout
pg12: stored_data
pg12.shape: stored_data
pg13: person
pg13.shape: person
pg14: diamond
pg14.shape: diamond
pg15: oval
pg15.shape: oval
pg17: circle
pg17.shape: circle
pg18: hexagon
pg18.shape: hexagon
pg19: cloud
pg19.shape: cloud
pg19: text
pg19.shape: text
pg20: code
pg20.shape: code
pg21: class
pg21.shape: class
pg22: sql_table
pg22.shape: sql_table
pg23: image
pg23.shape: image
pg23.icon: https://icons.terrastruct.com/infra/019-network.svg
pg24: sequence_diagram
pg24.shape: sequence_diagram
Connections
Hyphens/arrows in between shapes define a connection. For example
Write Replica Canada <-> Write Replica Australia
Read Replica <- Master
Write Replica -> Master
Read Replica 1 -- Read Replica 2
There are 4 valid ways to define a connection:
-- Creates a connection between shapes
-> Creates a uni directional connection example a -> b
<- Creates a uni directional connection example b -> a
<-> Creates a bi directional connection
Connection labels
Use colon to give a label to a connection for example
Read Replica 1 -- Read Replica 2: Kept in sync
Connections must reference a shape's key, not its label.
#shape be with label Backend
be: Backend
#shape fe with label Frontend
fe: Frontend
# This would create new shapes
Backend -> Frontend
# This would define a connection over existing shapes
be -> fe
Repeated connections
Repeated connections do not override existing connections. They declare new ones.
# Connection from Database to S3 with label "backup"
Database -> S3: backup
# Connection from Database to S3
Database -> S3
# Connection from Database to S3 with label "backup"
Database -> S3: backup # Will create another connection
Will generate something like this
Connection chaining
For readability, it may look more natural to define multiple connection in a single line.
# The label applies to each connection in the chain.
Instance1 -> EC2 <- Instance2: Hosted By
Connection Cycles
We can also do chained connection cycles
Stage One -> Stage Two -> Stage Three -> Stage Four
Stage Four -> Stage One: repeat
Arrowheads
To override the default arrowhead shape or give a label next to arrowheads, define a special shape on connections named source-arrowhead
and/or target-arrowhead
.
# Defined a shape with label
a: The best way to avoid responsibility is to say, "I've got responsibilities"
# Defined b shape with label
b: Whether weary or unweary, O man, do not rest
# Defined c shape with label
c: I still maintain the point that designing a monolithic kernel in 1991 is a
# Defined uni connection between a and b shape with label and custom connection arrowhead
a -> b: To err is human, to moo bovine {
source-arrowhead: 1
target-arrowhead: * {
shape: diamond
}
}
# Defined bi connection between b and c shape with label and custom connection arrowhead
b <-> c: "Reality is just a crutch for people who can't handle science fiction" {
source-arrowhead.label: 1
target-arrowhead: * {
shape: diamond
style.filled: true
}
}
# Defined d shape with label
d: A black cat crossing your path signifies that the animal is going somewhere
d -> a -> c
ARROWHEAD OPTIONS
triangle
(default)arrow
(like triangle but pointier)diamond
- Can be further styled as
style.filled: true
.
- Can be further styled as
circle
- Can be further styled as
style.filled: true
.
- Can be further styled as
cf-one
,cf-one-required
(cf stands for crows foot)cf-many
,cf-many-required
Sample Example
a -> b: triangle {
source-arrowhead: 1
target-arrowhead: * {
shape: triangle
}
}
c -> d: arrow {
source-arrowhead: 1
target-arrowhead: * {
shape: arrow
}
}
e -> f: circle {
source-arrowhead: 1
target-arrowhead: * {
shape: circle
}
}
g -> h: cf-one {
source-arrowhead: 1
target-arrowhead: * {
shape: cf-one
}
}
i -> j: cf-one-required {
source-arrowhead: 1
target-arrowhead: * {
shape: cf-one-required
}
}
k -> l: cf-many {
source-arrowhead: 1
target-arrowhead: * {
shape: cf-many
}
}
m -> n: cf-many-required {
source-arrowhead: 1
target-arrowhead: * {
shape: cf-many-required
}
}
Containers
Containers are shapes that can contain other shapes within them for example
server
# Declares a shape inside of another shape
server.process
# Can declare the container and child in same line
parent.child
# Since connections can also declare keys, this works too
apartment.Bedroom.Bathroom -> office.Spareroom.Bathroom: Portal
The above code will generate output like this
Nested syntax
You can avoid repeating containers by creating nested maps.
clouds: {
aws: {
load_balancer -> api
api -> db
}
gcloud: {
auth -> db
}
gcloud -> aws
}
The above code will generate an output like
Container labels
There are two ways define container labels.
1. Shorthand container labels
gcloud: Google Cloud {
...
}
2. Reserved keyword label
gcloud: {
label: Google Cloud
...
}
Example
clouds: {
aws: AWS {
load_balancer -> api
api -> db
}
gcloud: Google Cloud {
auth -> db
}
gcloud -> aws
}
users -> clouds.aws.load_balancer
users -> clouds.gcloud.auth
ci.deploys -> clouds
Reference parent
Sometimes you want to reference something outside of the container from within. The underscore (_
) refers to parent.
christmas: {
presents
}
birthdays: {
presents
_.christmas.presents -> presents: regift
_.christmas.style.fill: "#ACE1AF"
}
Subscribe to my newsletter
Read articles from Mayank Kapri directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Mayank Kapri
Mayank Kapri
What do you want to do before you die? Explore the world or explore who you are?