> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Constructs

> Explains building reusable CDKTF constructs in TypeScript using a ProjectFolder example to encapsulate resources, enable reuse, and expose resource properties for stacks

This article explains how to create reusable constructs in CDK for Terraform (CDKTF). Constructs let you encapsulate resource creation and logic in reusable, programmatic units—similar in purpose to Terraform modules but with the power of TypeScript.

Arthur wants to reuse his project setups, so he creates a ProjectFolder construct to encapsulate repetitive tasks and to group together the resources we've created so far into a reusable building block.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/q_P6afvbRoHuV6uC/images/CDK-for-Terraform-with-TypeScript/Introduction-to-CDKTF/Constructs/cdktf-projectfolder-construct-solution.jpg?fit=max&auto=format&n=q_P6afvbRoHuV6uC&q=85&s=c555cabd706681020eaa52682f741313" alt="A slide titled &#x22;Creating Constructs in CDKTF – Solution&#x22; showing a stylized person and monitor with code, and the instruction &#x22;Create a construct ProjectFolder to handle repetitive tasks.&#x22; A footer notes the Terraform equivalent: constructs → Terraform modules (HCL)." width="1920" height="1080" data-path="images/CDK-for-Terraform-with-TypeScript/Introduction-to-CDKTF/Constructs/cdktf-projectfolder-construct-solution.jpg" />
</Frame>

## Why use constructs?

* Reuse: package common patterns (folders, files, provider initialization) once and reuse across stacks and projects.
* Composition: compose small constructs into larger systems.
* Type-safety & programmability: use TypeScript for control flow, loops, and conditional logic that would be awkward in HCL modules.
* Direct object references: CDKTF lets you expose resource objects directly (not just primitive outputs), enabling richer composition.

## Minimal ProjectFolder construct (boilerplate)

The example below shows a minimal construct shell that illustrates common TypeScript patterns used in CDKTF constructs.

```typescript theme={null}
import { Construct } from 'constructs';
import { file } from '@cdktf/provider-local';

interface ProjectFolderProps {
  readonly projectName: string;
  readonly projectDirectory: string;
}

export class ProjectFolder extends Construct {
  constructor(scope: Construct, id: string, props: ProjectFolderProps) {
    super(scope, id);

    const { projectName, projectDirectory } = props;
    // Reusable code...
  }
}
```

<Callout icon="lightbulb" color="#1CB2FE">
  Key TypeScript notes:

  * `extends`: creates a class that inherits from a base class (here `Construct`).
  * `super(scope, id)`: calls the parent class constructor to initialize inherited behavior.
  * `readonly`: marks a property as immutable after initialization.
  * Destructuring (`const { projectName, projectDirectory } = props`) is a concise shorthand for extracting properties from an object.
</Callout>

Place construct files in a `constructs` folder. When a file exports a single class, it's common to name the file after that class (for example, `project-folder.ts`).

## Example stack that uses the construct

This stack initializes the `local` provider, sets up base variables (project directory and name), and instantiates the `ProjectFolder` construct. It also demonstrates exposing a value from the construct as a Terraform output.

```typescript theme={null}
import { App, TerraformOutput, TerraformStack } from 'cdktf';
import { LocalProvider, file } from '@cdktf/provider-local';
import { Construct } from 'constructs';
import * as path from 'path';
import { ProjectFolder } from './constructs/project-folder';

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    // Initialize the local provider
    new LocalProvider(this, 'local', {});

    // Base directory and project name
    const projectDirectory = path.join(process.env.INIT_CWD!, 'authors-projects');
    const projectName = 'project-1';

    // Instantiate the reusable construct
    const projectFolder = new ProjectFolder(this, 'project-folder', {
      projectName,
      projectDirectory,
    });

    // Expose the readme content from the construct as a Terraform output
    new TerraformOutput(this, 'readMeContent', {
      value: projectFolder.readmeFile.content,
    });
  }
}

const app = new App();
new MyStack(app, 'cdktf-project-builder');
app.synth();
```

## Move resource creation into the construct

The construct should:

* Define the properties it needs (via `ProjectFolderProps`).
* Create the resources it manages.
* Expose any values or resource references the stack or other constructs might need by assigning them to `readonly` class properties.

Example `constructs/project-folder.ts`:

```typescript theme={null}
import { Construct } from 'constructs';
import { file } from '@cdktf/provider-local';
import * as path from 'path';

interface ProjectFolderProps {
  readonly projectName: string;
  readonly projectDirectory: string;
}

export class ProjectFolder extends Construct {
  // Expose the File resource so other constructs/stacks can access it.
  readonly readmeFile: file.File;

  constructor(scope: Construct, id: string, props: ProjectFolderProps) {
    super(scope, id);

    const { projectName, projectDirectory } = props;
    const basePath = path.join(projectDirectory, projectName);

    // Create a README file resource and assign it to the read-only property.
    this.readmeFile = new file.File(this, 'readme-file', {
      filename: `${basePath}/README.md`,
      content: `# ${projectName}\n\nThis is the ${projectName} project`,
    });
  }
}
```

By assigning the resource to `this.readmeFile` (instead of a local `const`), the stack that instantiates this construct can reference `projectFolder.readmeFile` to access resource attributes (for example, its `content`).

When the construct is instantiated in the stack (as shown earlier), you create a Terraform output from an attribute exposed by the construct:

```typescript theme={null}
// In the stack constructor, after creating the ProjectFolder instance:
new TerraformOutput(this, 'readMeContent', {
  value: projectFolder.readmeFile.content,
});
```

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/q_P6afvbRoHuV6uC/images/CDK-for-Terraform-with-TypeScript/Introduction-to-CDKTF/Constructs/cdktf-expose-projectfolder-readonly-property.jpg?fit=max&auto=format&n=q_P6afvbRoHuV6uC&q=85&s=f8ba40c8ea8bfd4d0d65a02037135365" alt="A presentation slide titled &#x22;Creating Constructs in CDKTF – Solution&#x22; with an icon of a person at a monitor displaying code brackets. The slide includes the instruction: &#x22;Expose a read-only property from ProjectFolder construct.&#x22;" width="1920" height="1080" data-path="images/CDK-for-Terraform-with-TypeScript/Introduction-to-CDKTF/Constructs/cdktf-expose-projectfolder-readonly-property.jpg" />
</Frame>

## Quick reference

| Item             | Purpose                                             | Example / Notes                                                                           |
| ---------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Construct folder | Organize reusable constructs                        | `constructs/project-folder.ts`                                                            |
| Props interface  | Defines inputs required by construct                | `ProjectFolderProps` with `projectName`, `projectDirectory`                               |
| Exposed property | Let stacks reference resources created by construct | `readonly readmeFile: file.File`                                                          |
| Terraform output | Export values from the stack                        | `new TerraformOutput(this, 'readMeContent', { value: projectFolder.readmeFile.content })` |

## Pattern benefits

* Encapsulate resource creation in a reusable construct.
* Expose meaningful, read-only properties (resource objects and attributes) for use by the stack or other constructs.
* Keep stacks declarative while composing constructs programmatically.
* Unlike HCL modules, CDKTF allows direct object references and richer composition patterns via TypeScript.

<Callout icon="lightbulb" color="#1CB2FE">
  Best practice: Keep constructs small and focused. Expose only the properties that other stacks or constructs need to keep the API surface minimal and easier to maintain.
</Callout>

## Links and references

* CDK for Terraform: [https://developer.hashicorp.com/terraform/cdktf](https://developer.hashicorp.com/terraform/cdktf)
* Constructs programming model: [https://github.com/aws/constructs](https://github.com/aws/constructs)
* Terraform modules (HCL): [https://www.terraform.io/language/modules](https://www.terraform.io/language/modules)
* Node.js path module (used for `path.join`): [https://nodejs.org/api/path.html](https://nodejs.org/api/path.html)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/cdk-for-terraform-with-typescript/module/948c0a82-faa1-4f16-83d1-8ee8df2336b3/lesson/d7c50a2d-2293-4c68-a052-52a1fe00d47a" />
</CardGroup>
