DEV Community

Kenta Goto for AWS Heroes

Posted on • Edited on

AWS CDK Introduces Mixins: A Major Feature for Flexible Construct Composition (Developer Preview)

AWS CDK has newly introduced Mixins as a major feature that will become the core of future CDK development, currently available as a Developer Preview.

This feature is expected to significantly change how CDK will be used in the future.


CDK Version Used in This Article (Updated on February 3, 2026)

The CDK versions used in this article are as follows:

  • CDK: aws-cdk-lib: 2.237.0
  • Mixins module: @aws-cdk/mixins-preview: 2.237.0-alpha.0
  • CDK CLI: aws-cdk: 2.1104.0

At the time of publication, this article used v2.229.0 when Mixins was released, but due to several breaking changes, the version has been upgraded to v2.237.0 and the sample code in this article has been updated accordingly. Additionally, new features have been added, so those have been included as well.


What are Mixins

As of November 2025, Mixins is in Developer Preview. Please be aware that the specification and behavior may change significantly in the future.

Overview of Mixins

Mixins is a mechanism for applying composable abstractions to any Construct, regardless of whether it's an L1 or L2 Construct.

It was introduced in CDK v2.229.0, but as of November 2025, it is still in Developer Preview. Not all features are fully available yet, and more functionality and improvements are planned to be added in the future.

This article explains the overview and usage of Mixins in a more accessible way, based on the content from the Mixins README and RFC.

  • README
  • RFC
    • As of November 25, 2025, the RFC has not been merged yet, so the PR link is provided above.
    • The RFC is likely not to be merged yet as more features are planned to be added.

Characteristics of Mixins

Mixins enables the partial application of abstractions to L1 Constructs or custom constructs that were traditionally done with L2 Constructs, allowing users to introduce only the features they need without using L2 Constructs.

This makes it possible to selectively introduce only the necessary features, even when some abstractions of L2 Constructs are attractive but others are not desired.

Additionally, when new features are added to AWS services, new properties are immediately reflected in L1 Constructs, but it takes time for them to be reflected in L2 Constructs. By using Mixins, users can easily and safely apply new properties to L2 Constructs (without using escape hatches) before those properties are added to L2 Constructs.

In other words, Mixins reduces the gap between L1 and L2 Constructs, enabling more flexible construct design.

Furthermore, Mixins also provides a mechanism to abstractly apply common patterns such as encryption across various AWS services.


Installation

Since Mixins is still in Developer Preview, it is provided as the @aws-cdk/mixins-preview package.

npm install -D @aws-cdk/mixins-preview
Enter fullscreen mode Exit fullscreen mode

Note

If you encounter errors when running cdk deploy or cdk synth after introducing Mixins in an existing CDK project (TypeScript), first check your tsconfig.json.

The newer version of CDK CLI (2.1033.0) generates the following configuration, so if your project's configuration differs, aligning it may resolve the issue.

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": [
      "es2022"
    ],
    ...,
    ...,
    "skipLibCheck": true,
}
Enter fullscreen mode Exit fullscreen mode

How to Use Mixins

Mixins are applied using the Mixins.of() method or the with() method of each construct.

The with() method becomes available by importing the @aws-cdk/mixins-preview/with module in the file where you use it (import '@aws-cdk/mixins-preview/with').

NOTE: The .with() method is currently available only in JavaScript and TypeScript. The import is only required during the preview period. Once the API is stable, the .with() method will be available by default on all constructs in all languages.

import * as cdk from 'aws-cdk-lib';
import '@aws-cdk/mixins-preview/with';
import { CfnBucket } from 'aws-cdk-lib/aws-s3';
import { Mixins } from '@aws-cdk/mixins-preview/core';
import { BucketVersioning, AutoDeleteObjects } from '@aws-cdk/mixins-preview/aws-s3/mixins';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'CdkSampleStack');

const bucket = new CfnBucket(stack, 'MyBucketApply');
Mixins.of(bucket) // Mixins
  .apply(new BucketVersioning()) // Apply BucketVersioning to bucket
  .apply(new AutoDeleteObjects()); // Apply AutoDeleteObjects to bucket

new CfnBucket(stack, 'MyBucketWith')
  .with(new BucketVersioning()) // Apply BucketVersioning with Construct's with method
  .with(new AutoDeleteObjects()); // Apply AutoDeleteObjects with Construct's with method
Enter fullscreen mode Exit fullscreen mode

You can also apply Mixins to all resources under a specific scope, similar to Aspects.

import * as cdk from 'aws-cdk-lib';
import '@aws-cdk/mixins-preview/with';
import { BucketVersioning } from '@aws-cdk/mixins-preview/aws-s3/mixins';
import { Mixins, ConstructSelector } from '@aws-cdk/mixins-preview/core';
import { Construct } from 'constructs';
import { CfnBucket } from 'aws-cdk-lib/aws-s3';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'CdkSampleStack');
const construct = new Construct(stack, 'MyConstruct');

// Apply to all resources under construct
Mixins.of(construct).apply(new BucketVersioning());

// Apply only to specific resource types under construct
Mixins.of(construct, ConstructSelector.resourcesOfType(CfnBucket.CFN_RESOURCE_TYPE_NAME)) // Limited to CfnBucket
  .apply(new BucketVersioning());

// Apply only to resources matching specific ID patterns under construct
Mixins.of(construct, ConstructSelector.byId('.*-prod-.*')) // Limited to resources whose ID contains "-prod-"
  .apply(new ProductionSecurityMixin()); // ProductionSecurityMixin is an example of custom Mixins
Enter fullscreen mode Exit fullscreen mode

Types of Mixins

There are several types of Mixins patterns available:

  • Cross-Service Mixins
  • Resource-Specific Mixins
  • L1 Property Mixins
  • Log Delivery Mixins

Cross-Service Mixins

These are Mixins that can be applied across multiple AWS services.

For example, EncryptionAtRest is a Mixin that enables encryption across AWS resources that support it.

NOTE: The EncryptionAtRest Mixin is not yet included in the current CDK release, but it is introduced in the README examples. It is expected to be introduced soon, so this article uses that example as well.

import { Mixins } from '@aws-cdk/mixins-preview/core';

const bucket = new s3.CfnBucket(scope, 'Bucket');
Mixins.of(bucket).apply(new EncryptionAtRest());

const logGroup = new logs.CfnLogGroup(scope, 'LogGroup');
Mixins.of(logGroup).apply(new EncryptionAtRest());
Enter fullscreen mode Exit fullscreen mode

Resource-Specific Mixins

Resource-specific Mixins are also available for each AWS service (resource).

For example, there are AutoDeleteObjects, which automatically deletes objects using a custom resource Lambda for S3 buckets, BucketVersioning, which enables versioning, and BucketPolicyStatementsMixin, which adds IAM policy statements to the bucket policy.

By applying these Mixins to L1 Constructs, you can introduce the same abstractions as L2 Constructs without using L2 Constructs.

While versioning can be easily enabled with just L1 Constructs, it is convenient when applying to multiple resources at once, similar to Aspects.

import { Mixins } from '@aws-cdk/mixins-preview/core';

const bucket = new s3.CfnBucket(scope, 'Bucket');
Mixins.of(bucket).apply(new AutoDeleteObjects());

const bucket = new s3.CfnBucket(scope, 'Bucket');
Mixins.of(bucket).apply(new BucketVersioning());

const bucket = new s3.CfnBucket(scope, 'Bucket');
const bucketPolicy = new s3.CfnBucketPolicy(scope, "BucketPolicy", {
  bucket: bucket,
  policyDocument: new iam.PolicyDocument(),
});
Mixins.of(bucketPolicy).apply(new BucketPolicyStatementsMixin([
  new iam.PolicyStatement({
    actions: ["s3:GetObject"],
    resources: ["*"],
    principals: [new iam.AnyPrincipal()],
  }),
]));
Enter fullscreen mode Exit fullscreen mode

L1 Property Mixins

These are Mixins that allow you to easily apply L1 properties that have not yet been introduced to L2 Constructs.

For example, by passing a Mixin called CfnBucketPropsMixin, which applies L1 properties, to the with() method of an L2 Construct, you can apply L1 properties that have not yet been introduced to L2 Constructs in a typed manner.

Traditionally, applying L1 properties to L2 Constructs required using escape hatches, which had the problem of lacking type safety. Using Mixins makes it easier to apply them in a typed manner.

import * as cdk from 'aws-cdk-lib';
import '@aws-cdk/mixins-preview/with';
import { CfnBucketPropsMixin } from '@aws-cdk/mixins-preview/aws-s3/mixins';
import { Bucket } from 'aws-cdk-lib/aws-s3';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'CdkSampleStack');

const bucket = new Bucket(stack, 'Bucket').with(
  new CfnBucketPropsMixin({
    versioningConfiguration: { status: 'Enabled' },
    publicAccessBlockConfiguration: {
      blockPublicAcls: true,
      blockPublicPolicy: true,
    },
  }),
);
Enter fullscreen mode Exit fullscreen mode

L1 Property Mixins also provide two merge strategies: MERGE and OVERRIDE.

  • MERGE (default): Merge the specified properties into the existing properties
  • OVERRIDE: Overwrite the existing properties with the specified properties
import * as cdk from 'aws-cdk-lib';
import '@aws-cdk/mixins-preview/with';
import { CfnBucketPropsMixin } from '@aws-cdk/mixins-preview/aws-s3/mixins';
import { Mixins } from '@aws-cdk/mixins-preview/core';
import { PropertyMergeStrategy } from '@aws-cdk/mixins-preview/mixins';
import { CfnBucket } from 'aws-cdk-lib/aws-s3';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'CdkSampleStack');

const bucket = new CfnBucket(stack, 'Bucket');
bucket.versioningConfiguration = { status: 'Enabled' };

// MERGE
// versioningConfiguration becomes: { status: 'Enabled', newProperty: 'Disabled' }
// -> status: 'Enabled' remains
Mixins.of(bucket).apply(
  new CfnBucketPropsMixin(
    { versioningConfiguration: { newProperty: 'Disabled' } },
    { strategy: PropertyMergeStrategy.MERGE },
  ),
);

// OVERRIDE
// versioningConfiguration becomes: { newProperty: 'Disabled' }
// -> status: 'Enabled' is removed
Mixins.of(bucket).apply(
  new CfnBucketPropsMixin(
    { versioningConfiguration: { newProperty: 'Disabled' } },
    { strategy: PropertyMergeStrategy.OVERRIDE },
  ),
);
Enter fullscreen mode Exit fullscreen mode

While the two types of Mixins mentioned earlier are implemented individually in the CDK repository, L1 Property Mixins are automatically generated for all L1 Constructs in the CDK repository.

Log Delivery Mixins

Mixins for Vended logs delivery are also provided.

The following is an example of CfnDistributionLogsMixin, which is specific to CloudFront Distribution, but Vended logs Mixins are provided for several other AWS services as well (CfnUserPoolLogsMixin, CfnVPCLogsMixin, CfnLoadBalancerLogsMixin, etc.).

import * as cdk from 'aws-cdk-lib';
import '@aws-cdk/mixins-preview/with';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { CfnDistributionLogsMixin } from '@aws-cdk/mixins-preview/aws-cloudfront/mixins';
import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
import { S3BucketOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
import { LogGroup } from 'aws-cdk-lib/aws-logs';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'CdkSampleStack');

const bucket = new Bucket(stack, 'MyBucket');
const distribution = new Distribution(stack, 'Distribution', {
  defaultBehavior: {
    origin: S3BucketOrigin.withOriginAccessControl(bucket),
  },
});

// Create log destination
const logGroup = new LogGroup(stack, 'DeliveryLogGroup');

// Configure log delivery using the mixin
distribution.with(CfnDistributionLogsMixin.CONNECTION_LOGS.toLogGroup(logGroup));
Enter fullscreen mode Exit fullscreen mode

Log Delivery Mixins are also automatically generated for each supported AWS service in the CDK repository.


Error Handling

requireAll and requireAny

When Mixins are applied using the apply() method, the processing is skipped for resources that do not support that Mixin.

If you want to check whether there are any resources under the scope to which the Mixin is applied that do not support the Mixin, use the requireAll() or requireAny() methods.

The requireAll() method requires all resources under the scope to support the Mixin. If even one resource does not support it, an error will be thrown.

The requireAny() method requires at least one resource under the scope to support the Mixin. If no resources support it, an error will be thrown.

import { Mixins } from '@aws-cdk/mixins-preview/core';

// Skip if there are resources that do not support EncryptionAtRest under scope
Mixins.of(scope).apply(new EncryptionAtRest());

// Throw an error if any resource under scope does not support BucketVersioning
Mixins.of(scope).requireAll().apply(new BucketVersioning());

// Apply if at least one resource under scope supports BucketVersioning
Mixins.of(scope).requireAny().apply(new BucketVersioning());
Enter fullscreen mode Exit fullscreen mode

report and selectedConstructs

The report and selectedConstructs methods are also provided for performing manual assertions based on the results of Mixin applications.

report is used to retrieve information about resources to which Mixins were successfully applied. The return value is an array of the MixinApplication interface, allowing you to confirm which Mixin was applied to which Construct.

selectedConstructs is used to retrieve the list of Constructs that were filtered as Mixin application targets by the second argument of Mixins.of() (or if unspecified). The return value is an array of the IConstruct interface, allowing you to check which Constructs were targeted before the Mixin was applied.

import * as cdk from 'aws-cdk-lib';
import '@aws-cdk/mixins-preview/with';
import { BucketVersioning } from '@aws-cdk/mixins-preview/aws-s3/mixins';
import { Mixins } from '@aws-cdk/mixins-preview/core';
import { Queue } from 'aws-cdk-lib/aws-sqs';
import { Bucket, CfnBucket } from 'aws-cdk-lib/aws-s3';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'CdkSampleStack');

new Bucket(stack, 'MyBucket');
new Queue(stack, 'MyQueue');

// Get an application report for manual assertions
const report = Mixins.of(stack).apply(new BucketVersioning()).report;

report.forEach(({ construct, mixin }) => {
  cdk.Annotations.of(stack).addInfoV2(
    `mixins-report:${mixin.constructor.name}`,
    `Applied mixin ${mixin.constructor.name} to construct ${construct.node.path}`,
  );
});

// Get the list of constructs that were selected by the selector
const constructs = Mixins.of(stack).apply(new BucketVersioning()).selectedConstructs;

constructs.forEach((construct) => {
  cdk.Annotations.of(stack).addInfoV2(
    `mixins-selected:${construct.node.path}`,
    `Selected construct: ${construct.node.path}`,
  );
});
Enter fullscreen mode Exit fullscreen mode

In the case of the above code, you will get output like the following when running cdk deploy:

❯ npx cdk deploy
[Info at /CdkSampleStack] Applied mixin BucketVersioning to construct CdkSampleStack/MyBucket/Resource [ack: mixins-report:BucketVersioning]
[Info at /CdkSampleStack] Selected construct: CdkSampleStack [ack: mixins-selected:CdkSampleStack]
[Info at /CdkSampleStack] Selected construct: CdkSampleStack/MyBucket [ack: mixins-selected:CdkSampleStack/MyBucket]
[Info at /CdkSampleStack] Selected construct: CdkSampleStack/MyBucket/Resource [ack: mixins-selected:CdkSampleStack/MyBucket/Resource]
[Info at /CdkSampleStack] Selected construct: CdkSampleStack/MyQueue [ack: mixins-selected:CdkSampleStack/MyQueue]
[Info at /CdkSampleStack] Selected construct: CdkSampleStack/MyQueue/Resource [ack: mixins-selected:CdkSampleStack/MyQueue/Resource]
Enter fullscreen mode Exit fullscreen mode

Creating Custom Mixins

You can easily create custom Mixins by implementing the IMixin interface.

You can also create one with some of IMixin already implemented by extending the Mixin abstract class.

NOTE: It is recommended to always extend the Mixin abstract class rather than implementing IMixin directly. While the experience is similar, extending Mixin will make future transitions easier for authors.

The following example is a custom Mixin that enables versioning for S3 buckets.

import { Mixin, Mixins } from '@aws-cdk/mixins-preview/core';

// or class CustomVersioningMixin implements IMixin {
class CustomVersioningMixin extends Mixin {
  supports(construct: any): boolean {
    return construct instanceof s3.CfnBucket;
  }

  applyTo(bucket: any): void {
    bucket.versioningConfiguration = {
      status: 'Enabled',
    };
  }
}

const bucket = new s3.CfnBucket(scope, 'MyBucket');
Mixins.of(bucket).apply(new CustomVersioningMixin());
Enter fullscreen mode Exit fullscreen mode

Differences from Aspects

Mixins are similar to Aspects in that they modify properties of resources in a specific scope, but they differ in timing of application.

Aspects are not executed at call time, but are executed later in the synthesis process of the CDK application (in the Prepare phase after Construct creation is complete in the CDK application lifecycle).

In other words, while Aspects are executed after all Constructs defined in the CDK application have been created, Mixins are executed immediately when they are called.

  • Aspects
const construct = new Construct(scope, 'MyConstruct');
new Bucket(construct, 'MyBucket1');

// Executed after Constructs below this are created
Aspects.of(construct).add(new CustomVersioningAspect());

// Aspects are also applied to MyBucket2
new Bucket(construct, 'MyBucket2');
Enter fullscreen mode Exit fullscreen mode
  • Mixins
const construct = new Construct(scope, 'MyConstruct');
new Bucket(construct, 'MyBucket1');

// Executed at this point
Mixins.of(construct).apply(new CustomVersioningMixin());

// Mixins are not applied to MyBucket2
new Bucket(construct, 'MyBucket2');
Enter fullscreen mode Exit fullscreen mode

This means that Mixins are a form of imperative programming, while Aspects are a form of declarative programming.

According to the Mixins RFC, it is recommended to use Mixins to make changes and to use Aspects to validate behaviors.

We recommend to use Mixins to make changes, and to use Aspects to validate behaviors.

There are also other differences between Mixins and Aspects, as described below:

With mixins, you make explicit decisions about each construct's capabilities. With aspects, you set policies that the CDK applies automatically during synthesis. Mixins give you precise control and type safety, while aspects provide broad governance and compliance enforcement.

  • Mixins
    • You can make explicit decisions about each Construct's capabilities
    • Provides precise control and type safety
  • Aspects
    • You can set policies that the CDK applies automatically during synthesis
    • Provides broad governance and compliance enforcement

The RFC also mentions that a mechanism will be introduced to convert between Aspects and Mixins, but as of v2.229.0, this feature has not yet been introduced.

// Applies the aspect immediately
Mixins.of(scope).apply(Mixin.fromAspect(new TaggingAspect({ Environment: 'prod' })));

// Delays application of the Mixin to the synthesis phase
Aspects.of(scope).add(Aspect.fromMixin(new EncryptionAtRest()));
Enter fullscreen mode Exit fullscreen mode

EventBridge Event Patterns

The Mixins package also provides a mechanism for generating EventBridge event patterns in a type-safe manner. (While this feature itself is not a Mixin, it is included in the same package.)

This feature works with both L1 and L2 Constructs.

For example, for S3 bucket events, you can use the BucketEvents class to generate event patterns for S3 bucket object creation events in a type-safe way.

import * as cdk from 'aws-cdk-lib';
import '@aws-cdk/mixins-preview/with';
import { BucketEvents } from '@aws-cdk/mixins-preview/aws-s3/events';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import { Function } from 'aws-cdk-lib/aws-lambda';
import { Bucket, CfnBucket } from 'aws-cdk-lib/aws-s3';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'CdkSampleStack');
const fn = new Function(stack, 'Function', { ... });

// Works with L2 constructs
const bucket = new Bucket(stack, 'Bucket');

// BucketEvents from Mixins
const bucketEvents = BucketEvents.fromBucket(bucket);

new events.Rule(stack, 'Rule', {
  eventPattern: bucketEvents.objectCreatedPattern({
    object: { key: events.Match.wildcard('uploads/*') },
    eventMetadata: {
      region: events.Match.prefix('us-'),
      version: ['0'],
    },
  }),
  targets: [new targets.LambdaFunction(fn)],
});

// Also works with L1 constructs
const cfnBucket = new CfnBucket(stack, 'CfnBucket');

// BucketEvents from Mixins
const cfnBucketEvents = BucketEvents.fromBucket(cfnBucket);

new events.CfnRule(stack, 'CfnRule', {
  state: 'ENABLED',
  eventPattern: cfnBucketEvents.objectCreatedPattern({
    object: { key: events.Match.wildcard('uploads/*') },
    eventMetadata: {
      region: events.Match.prefix('us-'),
      version: ['0'],
    },
  }),
  targets: [{ arn: fn.functionArn, id: 'L1' }],
});
Enter fullscreen mode Exit fullscreen mode

These event patterns are automatically generated in the CDK repository for EventBridge events available in the AWS Event Schema Registry.

For example, for S3 events, the following patterns are available:

  • objectCreatedPattern() - Object creation events
  • objectDeletedPattern() - Object deletion events
  • objectTagsAddedPattern() - Object tagging events
  • awsAPICallViaCloudTrailPattern() - CloudTrail API call events

Conclusion

Mixins is an important feature that will become the core of future CDK development.

Although it is still in Developer Preview and there are still insufficient features and behaviors, it is worth paying attention to as it has the potential to significantly change how CDK will be used in the future.

Top comments (0)