Migrating a CDK Construct to projen and jsii

CDK constructs are a great way to combine best practices and simplify your infrastructure code. Have you ever written your own CDK construct? Writing a construct is easy using the CDK CLI. But soon you’ll discover the hard parts: Keeping all dependencies updated; Aligning the CDK dependency versions to not have version conflicts; And publishing your CDK construct to multiple repositories. projen makes the CDK construct setup and maintenance a lot easier. And jsii helps to release your TypeScript CDK construct to Java, Python and C#. Here is a short tutorial about migrating a CDK construct to projen and jsii.

I have provided a beginner’s step-by-step guide about getting started with projen and jsii. This will help you exploring typical options for projen and explains the process of publishing your CDK Construct to repositories like NPM, Maven, PyPi, and NuGet using jsii. I recommend to read it if you have no prior experience with projen or jsii.

About projen and jsii

projen is a tool to write your project configuration using code instead of managing it yourself. It was created to help you writing CDK constructs. You only define your project configuration in a .projenrc.js file and it will generate your project files for you. This includes a package.json and other configuration files for eslint or GitHub Actions. It is designed to automate all the boring project setup steps.

jsii is the technology behind the AWS CDK that allows you to write CDK Constructs in TypeScript/JavaScript and compile them to other languages like Java or Python. There’s a good AWS blog post about how it works.

Setup Your Construct Project

Let’s start to migrate your existing CDK Construct to projen. You have probably created your CDK construct using the following cdk command:

cd my-cdk-construct
cdk init lib --language=typescript

That command creates various folders and files with pre-defined defaults writing a CDK construct using TypeScript. For example, it initializes your project with a lib and test folder as well as an example construct file and a related test file. Finally, you can run your tests using Jest with npm run test. This is the basis of the next steps, even though you might have added further lib files, tests or tools like ESLint or similar.

Please note that you can only use projen and jsii with TypeScript/JavaScript as the source language at the moment. This means, you can not create a CDK construct in Java and publish it to NPM.

Migrating Your CDK Construct to projen

💡 Before you start migrating your CDK construct to projen, you might want to open this blog post about converting your CDK construct to using projen by Matthew Bonig in another browser tab. His article also covers some interesting facts and small errors he has experienced in the process, so they might help you as well.

Before you execute any command, make sure you have a clean Git status. This ensures that you can easily revert any undesired changes.

The following commands will initialize your project with projen. It will create a file called .projenrc.js containing a default projen configuration. Then it will automatically execute this file for you to generate further files and folders based on your projen configuration:

cd my-cdk-construct
npx projen new awscdk-construct

After executing the commands, you’ll see lots of changes in your Git status. For example, projen expects source files in src whereas a CDK construct initialized via the CDK CLI expects them to live in lib. Also, there will be a folder called .projen containing configuration files for projen. You don’t need to look into all of the new files immediately. projen is managing the contents of files like GitHub Actions or TypeScript configurations based on the settings you define in .projenrc.js.

Updating Your Project Files with projen

After migrating your CDK construct to projen, you should always follow this process to update project files generated by projen:

  1. Make according changes in .projenrc.js.
  2. Run npx projen and projen will synthesize the changes to all project files. 💡 Hint: create an alias like pj in your command line to avoid typing npx projen over and over again.
  3. Never manually edit the generated files because projen will overwrite them the next time!

If you have used tools like eslint or Dependabot before, you’ll probably see changes in their related files as well. If you don’t like the defaults, you can always change the settings in .projenrc.js. However, you’ll probably don’t have to change that much since projen is applying common settings for eslint. I only noticed two changes compared to my settings: include an uppercase ‘i’ (= I) in interface names and make its properties readonly. Everything else was just related to some minor style adjustments.

💡 Since I don’t know all varieties of CDK construct setups, you might run into other errors after adding projen. If you have any problems you can’t solve by yourself, feel free to reach out to me or send your question to the CDK developers Slack.

Important General Settings

Now it’s a good time to have a look at the most important settings you can set in .projenrc.js. I’ll outline them below and explain them in case it’s not too obvious.

projectTypeDefine if this project is a library or app. For a library (i.e. CDK construct), you should use ProjectType.LIB.
packageManagerDefine which package manager you want to use, e.g. Yarn or NPM. Set the value like NodePackageManager.NPM.
cdkVersionWith this property you are defining the CDK version you want to use for all CDK dependencies. Due to the nature of CDK, it’s important to align the numbers.
cdkDependenciesEnter all the CDK dependencies that you are using in your construct. You probably had them defined in your package.json file before but now you need to add them here. Add them like ['@aws-cdk/core', '...']. Since you specify the CDK version with cdkVersion, you don’t need to set it here.
bundledDepsList of dependencies you want to bundle with your construct. This might be necessary in case you use some constructs like NodejsFunction for a Lambda function. It will build your Lambda function’s code only when the Lambda function is used in a CDK stack. And then those dependencies need to be available.
depsAny other regular dependencies that your construct is using.
peerDepsA list of peer dependencies. projen will automatically add all @aws-cdk dependencies to the list of peer dependencies, so you don’t need to define them here. But you can add any other dependencies here that you’d like to have in the resulting peerDependencies of the package.json file.
packageNameBy default, projen will take the name property as the name in package.json. However, it might be necessary to overwrite it here.
Should be self explanatory. You can define a list of strings matching files or folders you’d like to ignore for Git or NPM.

Important: Don’t forget to run npx projen each time you make a change in .projenrc.js. Otherwise projen won’t synthesize the config changes to your project files.

Release Settings

You have to consider that by default projen assumes you want to publish your CDK construct to NPM. Hence, it sets up GitHub Actions that perform the relevant tasks for you, like bundling, running the tests and releasing the artifact. If you want to skip this for now, consider adjusting the following settings:

releaseBranchesA list of branch names. The default is ['main'] but you can adjust it to whatever branches you need. If you set this to an empty array, then you can only trigger a release by manually starting the release workflow (in case you enabled to add a release workflow, see below).
releaseEveryCommitIf set to true and a commit is added on any of the branches defined in releaseBranches, then a release is triggered.
releaseScheduleOptional: cron expression to regularly release a new version.
releaseToNpmIf a new version should be released to NPM using GitHub Actions.
releaseWorkflowIf you want to have a release workflow using GitHub Actions.

Again, don’t forget to run npx projen to update your project files after changing the projen settings in .projenrc.js.

Location of Lambda Function Code

In case you are creating a Lambda function in your CDK construct, you might wonder where you should put your Lambda function code and how you bundle it. When using projen, I’d say a good approach is to keep all the Lambda function code in a subfolder next to your CDK construct source files. For example, the code could go into src/lambda. This ensures that projen picks up the files and includes them in the artifact that’s being released to NPM. You can read my other blog post if you want to know more ways about how to bundle your Lambda function code in a CDK construct.

Building Your Construct

Now that you have finally adjusted the necessary projen settings and fixed any code or linting issues, you can verify that your code compiles. You can use commands like npm run build or npm run test to verify your code. (Use Yarn instead if you kept projen‘s default setting of packageManager) Is your code compiling? Great! If not, did you forget anything or did I miss to cover anything here? Let me know about it!

After a green build, you are ready to commit your changes and push them to your remote Git repository. Then, the GitHub actions configured under .github/actions will trigger and build (+ release) your CDK construct 🚀

Custom Build or Workflow Steps

Before migrating your CDK construct to projen, you probably had some custom scripts or commands in your project. For example, you prepared certain things for a Lambda function or your CDK construct in general. With projen you can add custom build and workflow steps as well. Here is how you can do it in .projenrc.js:

const project = new AwsCdkConstructLibrary({...});

// add a build step:
project.buildTask.exec('echo "Hello World"');

// add a job to the GitHub Action file of release.yml:
  example: {
    name: 'Example Job',
    'runs-on': 'ubuntu-latest',
    steps: [{...}],

As soon as update the project files by running npx projen again, you’ll see changes in .projen/tasks.json and .github/actions/release.yml.

Publishing to Multiple Repositories Using jsii

After you have finally migrated your code, it’s time to add some magic to your CDK construct. The magic comes by a combination of projen and jsii. Their biggest advantage is that you can make your CDK construct available not only to NPM but also Maven, PyPi and NuGet by just setting a few settings.

As described above, projen provides a few release settings. Use them to publish your CDK construct to NPM. If you want to publish your CDK construct to Maven, PyPi or NuGet, then consider the following settings:

publishToMavenMavenAllows you to configure the Java package, group id and artifact id.
publishToPypiPyPiAllows you to configure the dist name and module.
publishToNugetNuGetAllows you to configure the namespace and package id.

Enabling these settings (and running npx projen again 😉) will create appropriate steps in the release.yml GitHub Action. You can find detailed steps for each repository in my guide about creating a CDK construct using projen and jsii on GitHub. Besides that, you don’t need to do anything. Just commit your changes and wait for the magic to happen!

Successful CDK construct release using projen and jsii.
A successful release using projen and jsii. Example taken from projen-test.


I hope you were successful in migrating your existing CDK construct to projen and jsii. In my opinion it offers a really good way to manage your project and hide quite a few complicated steps. Also, shipping to various repositories works like a charm!

Are you running into any errors? Comment below and let me see if I can help you 😊