This guide covers how to extend CDK Utils with new service managers, constructs, and features.
Service managers wrap a specific cloud service. Here's how to add one for AWS.
packages/aws/src/services/my-service/types.ts
/** @category Interface */
export interface MyServiceProps {
name: string
// ... service-specific properties
}
packages/aws/src/services/my-service/main.ts
import { CommonConstruct } from '../../common/index.js'
import { createCfnOutput } from '../../utils/index.js'
import { MyServiceProps } from './types.js'
/**
* Provides operations on AWS MyService.
* - A new instance of this class is injected into {@link CommonConstruct} constructor.
* - If a custom construct extends {@link CommonConstruct}, an instance is available within the context.
* @example
* ```
* import { CommonConstruct } from '@gradientedge/cdk-utils'
*
* class CustomConstruct extends CommonConstruct {
* constructor(parent: Construct, id: string, props: CommonStackProps) {
* super(parent, id, props)
* this.myServiceManager.createResource('MyResource', this, props)
* }
* }
* ```
* @category Service
*/
export class MyServiceManager {
public createResource(
id: string,
scope: CommonConstruct,
props: MyServiceProps
) {
if (!props) throw new Error(`MyService props undefined for ${id}`)
const resourceName = scope.resourceNameFormatter.format(props.name)
// Create the resource using CDK constructs
// ...
createCfnOutput(`${id}Arn`, scope, resourceArn)
return resource
}
}
packages/aws/src/services/my-service/index.ts
export * from './main.js'
export * from './types.js'
Add to packages/aws/src/services/index.ts:
export * from './my-service/index.js'
Add to packages/aws/src/common/construct.ts:
import { MyServiceManager } from '../services/index.js'
export class CommonConstruct extends Construct {
myServiceManager: MyServiceManager
constructor(parent: Construct, id: string, props: CommonStackProps) {
super(parent, id)
// ... existing managers
this.myServiceManager = new MyServiceManager()
}
}
Create packages/aws/test/services/my-service.test.ts following existing test patterns.
packages/aws/src/construct/my-construct/
├── main.ts
├── types.ts
└── index.ts
// types.ts
import { CommonStackProps } from '../../common/index.js'
/** @category Interface */
export interface MyConstructProps extends CommonStackProps {
// construct-specific properties
}
// main.ts
import { CommonConstruct } from '../../common/index.js'
import { Construct } from 'constructs'
import { MyConstructProps } from './types.js'
/**
* Provides a construct to create and deploy MyConstruct.
* @example
* ```
* import { MyConstruct, MyConstructProps } from '@gradientedge/cdk-utils'
* import { Construct } from 'constructs'
*
* class CustomConstruct extends MyConstruct {
* constructor(parent: Construct, id: string, props: MyConstructProps) {
* super(parent, id, props)
* this.initResources()
* }
* }
* ```
* @category Construct
*/
export abstract class MyConstruct extends CommonConstruct {
props: MyConstructProps
protected constructor(parent: Construct, id: string, props: MyConstructProps) {
super(parent, id, props)
this.props = props
}
protected initResources() {
// Resource creation pipeline
}
}
Follow the same pattern as service managers above.
Every exported class, interface, and enum must have:
@example block@category tag (Construct, Service, Common, Interface, Enum, or Constant)Always validate props at the start of service manager methods:
if (!props) throw new Error(`Props undefined for ${id}`)
if (!props.name) throw new Error(`Name undefined for ${id}`)
Always use the formatter for resource names:
const name = scope.resourceNameFormatter.format(props.name)
Use the createCfnOutput utility for important resource identifiers:
import { createCfnOutput } from '../../utils/index.js'
createCfnOutput(`${id}Arn`, scope, resource.resourceArn)
@category tagsCommonStackProps (or cloud-specific equivalent)ResourceNameFormatterindex.ts filespnpm changeset)pnpm validate passes (prettier + lint + tests)