Skip to main content
Documentation

Components

Components are reusable, parameterized infrastructure modules. They encapsulate resources, inputs, and outputs into a single unit.

Component Type Declaration

component WebServer {
    // Inputs - configurable parameters
    input string hostname
    input number port      = 8080

    // Internal resources
    resource EC2.Instance server {
        name               = hostname
    }

    // Outputs - exposed values
    output string endpoint = "${hostname}:${port}"
}

Component Instantiation

// Define the component type
component WebServer {
    input string hostname
    input number port = 8080
}

// Create instances
component WebServer main {
    hostname          = "localhost"
    port              = 3000
}

component WebServer api {
    hostname          = "api.example.com"
    port              = main.port  // Reference another instance
}

Inputs

Inputs are configurable parameters for components:

component Database {
    input string   name                  // Required input (no default)
    input number   port       = 5432           // Optional input (with default)
    input string[] allowedIps = []       // Typed array input
    input boolean  sslEnabled = true     // Boolean input
}

Input Decorators

component Server {
    @description("The server hostname")
    @nonEmpty
    input string hostname

    @minValue(1)
    @maxValue(65535)
    input number port = 8080

    @sensitive
    input string apiKey
}

Outputs

Outputs expose values from components:

component WebServer {
    input string hostname
    input number port      = 8080

    resource EC2.Instance server {
        name               = hostname
    }

    output string endpoint = "${hostname}:${port}"          // Simple output
    output string serverId = server.id                       // Output from resource
    output string url      = "https://${hostname}:${port}"      // Computed output
}

Accessing Component Properties

Define a component, then instantiate it:

component WebServer {
    input string hostname
    input number port      = 8080
    output string endpoint = "http://${hostname}:${port}"

    resource VM.Instance server {
        name               = hostname
    }
}

Create an instance and access its properties:

component WebServer main {
    hostname = "localhost"
    port     = 3000
}

var host     = main.hostname    // "localhost"
var p        = main.port        // 3000
var url      = main.endpoint    // "http://localhost:3000"

// Resources are private (not accessible from outside)
// main.server  // ERROR: resources are private

Why Resources Are Private

Resources are private to enforce clean component APIs, enable safe refactoring, and follow the principle of "expose behavior, not implementation."

Prevents Tight Coupling

Without this restriction, consumers could write:

// BAD: Tight coupling to internal structure
var ip   = webServer.server.privateIp
var disk = webServer.server.rootVolume.size

This creates fragile dependencies on internal implementation. Components force authors to explicitly define what's public:

ElementVisibilityPurpose
inputPublicConfiguration parameters
outputPublicComputed/exposed values
resourcePrivateInfrastructure implementation

Enables Safe Refactoring

The component author can change internal resources without breaking consumers:

// v1: Uses VM
component WebServer {
    resource VM.Instance server { ... }
    output string endpoint = server.publicIp
}

// v2: Switched to container - consumers unaffected
component WebServer {
    resource ECS.Service service { ... }
    output string endpoint = service.loadBalancerUrl
}

If main.server was allowed, refactoring would break all callers.

Encapsulation (Information Hiding)

Components expose a public contract (inputs/outputs) while hiding implementation details (resources). This is the same principle as private fields in OOP.

component WebServer {
    input number port      = 8080
    output string endpoint = "http://${server.publicIp}:${port}"

    // Implementation detail - could change without breaking consumers
    resource VM.Instance server {
        size               = "t2.micro"
    }
}

Consumers only care about port (what they provide) and endpoint (what they get back). They shouldn't depend on whether there's a VM, container, or Lambda underneath.

Component Decorators

@count

@count(3)
component WebServer replica {
    hostname = "server-${count}"
}
// Creates: replica-0, replica-1, replica-2

@dependsOn

component Database db { ... }

@dependsOn(db)
component WebServer api {
    databaseHost = db.endpoint
}

@tags

@tags({ Environment: "prod", Team: "platform" })
component WebApp main {
    name = "main-app"
}

Nested Components

component Frontend {
    input string name

    component WebServer server {
        hostname      = "${name}.example.com"
    }

    output string url = server.endpoint
}

component Application {
    component Frontend web {
        name          = "web"
    }

    component Backend api {
        name          = "api"
    }
}

Next: Learn about Schemas - structured data types for resources.