Skip to content

Your first AWS PDK Project

Warning

Some of the steps in this workshop will create resources that may bill your account. If you do not complete the workshop, you may still have AWS resources that are unknowingly charging your account. To ensure your account is clean after completing this workshop, check out the destroying the deployed resources section towards the end of this page.

You've read Getting started with the AWS PDK and set up your development environment for writing AWS PDK projects? Great! Now let's see how it feels to work with the AWS PDK by building a complex PDK project.

In this tutorial, you'll learn about the following:

  • The structure of a PDK project
  • How to use PDK constructs to define a project structure
  • How to synthesize and build PDK projects
  • How to take advantage of build caching and visualize your project dependencies

The standard AWS PDK development workflow is similar to the standard Projen workflow you may already be familiar with:

  1. Bootstrap your project by executing the npx projen new --from @aws/pdk monorepo-[ts/py/java] command
  2. Define your project constructs within the projenrc file
  3. Synthesize your projects by running the npx projen command
  4. Build your projects by running npx projen build
  5. Deploy your infrastructure to the AWS cloud

This tutorial walks you through creating the PDK Project from start to finish. The final application we create will comprise of a React based website, a Smithy API and the supporting CDK infrastructure to deploy it all.

We'll also show how to add a new API operation, implement an API handler, and wire it up in your infrastructure.

Prerequisites

Refer to prerequisites.

Create your project

Each AWS PDK based project should be in its own directory. Create a new directory for your project. Starting in your home directory, or another directory if you prefer, issue the following commands:

mkdir my-project
cd my-project

Now lets bootstrap our project to use the PDK by running the pdk new command. Specify the desired template to bootstrap with based on your desired programming language:

# optional params can be passed in also - lets use PNPM to manage deps :)
npx projen new --from @aws/pdk --package-manager=pnpm monorepo-ts
npx projen new --from @aws/pdk monorepo-py
npx projen new --from @aws/pdk monorepo-java

The npx projen new command creates a number of files and folders inside the my-project directory to help you organize the source code for your AWS PDK based project. It also installs any dependencies on your behalf by default, although this can be suppressed with the optional --no-post argument.

Tip

If you have the pdk CLI installed, you can save some typing and replace npx projen new --from @aws/pdk with pdk new.

Tip

If you have Git installed, each project you create using npx projen new is also initialized as a Git repository. We'll ignore that for now, but it's there when you need it.

Let's take a moment to examine the project structure that is created for you:

As you can see, a lot of files have been created for you and each of these are managed by the PDK/Projen on your behalf. The only file you need to modify is the projenrc file which is repsonsible for synthesizing each of these files.

Warning

It is important that you never modify synthesized files directly as it will result in your changes being overriden the next time you run the npx projen command which will re-synthesize/override them.

Inspecting the projenrc file, we notice that a single construct is instantiated that represents the monorepo itself. Within this construct you can add new dependencies, update tsconfig, npmignore, gitignore, etc. In the default configuration, the following apply:

  • The name of the project takes on the name of the folder it was created in.
  • For typescript, the packageManager is set to PNPM as we passed this optional parameter in whilst running npx projen new.
    • Any parameter listed here can be passed in via the npx projen new command i.e: --name="some-other-name".
  • For python, the moduleName defaults to a snake-cased project name.

You will also notice that the synth() method is called on this instance at the end of the file. When you run the npx projen command, this file will be executed by your runtime and will synthesize this instance which will result in all the files that you see in the previous image.

Info

Whenever you change the projenrc file, make sure you run the npx projen command from the root of your project to ensure your files are synthesized.

For more details on the Monorepo construct, please refer to the Monorepo Developer Guide.

Building your project

We now have a monorepo that can be used to manage projects and perform polyglot builds. Let's now test to make sure the build process works.

To build your project, run the following command from the root of your project npx projen build. This command delegates to NX under the covers which calls the build target for each of your subprojects in dependency-order. Given we don't have any subprojects at present we should see the following output:

npx projen build

👾 build | npx nx run-many --target=build --output-style=stream --nx-bail

 >  NX   No projects with target build for 0 projects were run

 ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Successfully ran target build for 0 projects

In future, when we add new sub-projects to the monorepo - we will see the output from each individual sub-projects build task.

Tip

If you have the pdk CLI installed, you can also build your project by running pdk build

Add a Type-Safe API to your monorepo

At this point, your monorepo project doesn't do anything because the projenrc file only defines the monorepo itself and no other sub-projects which isn't useful. Let's make things more interesting and add a Type-Safe API.

The Type-Safe API construct is available within the AWS PDK library which is installed by default, so there is no need to install another library. We can define the Type-Safe API within the projenrc file as follows:

import { MonorepoTsProject } from "@aws/pdk/monorepo";
import {
    DocumentationFormat,
    Language,
    Library,
    ModelLanguage,
    TypeSafeApiProject,
} from "@aws/pdk/type-safe-api";
import { javascript } from "projen";

// rename variable to monorepo for better readability
const monorepo = new MonorepoTsProject({
    name: "my-project",
    packageManager: javascript.NodePackageManager.PNPM,
    projenrcTs: true,
});

new TypeSafeApiProject({
    parent: monorepo,
    outdir: "packages/api",
    name: "myapi",
    infrastructure: {
        language: Language.TYPESCRIPT,
    },
    model: {
        language: ModelLanguage.SMITHY,
        options: {
        smithy: {
            serviceName: {
            namespace: "com.aws",
            serviceName: "MyApi",
            },
        },
        },
    },
    runtime: {
        languages: [Language.TYPESCRIPT],
    },
    documentation: {
        formats: [DocumentationFormat.HTML_REDOC],
    },
    library: {
        libraries: [Library.TYPESCRIPT_REACT_QUERY_HOOKS],
    },
    handlers: {
        languages: [Language.TYPESCRIPT],
    },
});

monorepo.synth();
from aws_pdk.monorepo import MonorepoPythonProject
from aws_pdk.type_safe_api import *

# rename variable to monorepo for better readability
monorepo = MonorepoPythonProject(
    module_name="my_project",
    name="my-project",
)

TypeSafeApiProject(
    name="myapi",
    parent=monorepo,
    outdir="packages/api",
    model=ModelConfiguration(
        language=ModelLanguage.SMITHY,
        options=ModelOptions(
            smithy=SmithyModelOptions(
                service_name=SmithyServiceName(
                    namespace="com.amazon",
                    service_name="MyAPI"
                )
            )
        )
    ),
    runtime=RuntimeConfiguration(
        languages=[Language.PYTHON]
    ),
    infrastructure=InfrastructureConfiguration(
        language=Language.PYTHON
    ),
    documentation=DocumentationConfiguration(
        formats=[DocumentationFormat.HTML_REDOC]
    ),
    handlers=HandlersConfiguration(
        languages=[Language.PYTHON]
    ),
    library=LibraryConfiguration(
        libraries=[Library.TYPESCRIPT_REACT_QUERY_HOOKS]
    )
)

monorepo.synth()
import software.aws.pdk.monorepo.MonorepoJavaProject;
import software.aws.pdk.type_safe_api.*;
import software.aws.pdk.monorepo.MonorepoJavaOptions;
import java.util.Arrays;

public class projenrc {
    public static void main(String[] args) {
        // rename variable to monorepo for better readability
        MonorepoJavaProject monorepo = new MonorepoJavaProject(MonorepoJavaOptions.builder()
                .name("my-project")
                .build());

        new TypeSafeApiProject(TypeSafeApiProjectOptions.builder()
                .name("myapi")
                .parent(monorepo)
                .outdir("packages/api")
                .model(ModelConfiguration.builder()
                        .language(ModelLanguage.SMITHY)
                        .options(ModelOptions.builder()
                                .smithy(SmithyModelOptions.builder()
                                        .serviceName(SmithyServiceName.builder()
                                                .namespace("com.my.company")
                                                .serviceName("MyApi")
                                                .build())
                                        .build())
                                .build())
                        .build())
                .runtime(RuntimeConfiguration.builder()
                        .languages(Arrays.asList(Language.JAVA))
                        .build())
                .infrastructure(InfrastructureConfiguration.builder()
                        .language(Language.JAVA)
                        .build())
                .documentation(DocumentationConfiguration.builder()
                        .formats(Arrays.asList(DocumentationFormat.HTML_REDOC))
                        .build())
                .library(LibraryConfiguration.builder()
                        .libraries(Arrays.asList(Library.TYPESCRIPT_REACT_QUERY_HOOKS))
                        .build())
                .handlers(HandlersConfiguration.builder()
                        .languages(Arrays.asList(Language.JAVA))
                        .build())
                .build());

        monorepo.synth();
    }
}

As we have now modified the projenrc file, let's synthesize our project by running pdk from the root of the project.

You will now notice that a new packages/api folder will be created within your project which contains all of the API related source code for your configured Type-Safe API. In summary, the packages contained within the packages/api folder have the following functions:

|_ model/ - contains the Interface Definition Language (IDL) where you define your API.
|_ handlers/ - contains the generated lambda stubs for your API operations
|_ generated/
    |_ runtime/ - generated types, client, and server code in the languages you specified
    |_ infrastructure/ - generated infrastructure
    |_ documentation/ - generated documentation in the formats you specified
    |_ library/ - generated libraries if specified
        |_ typescript-react-query-hooks - react hooks to call the API

For more details on these packages, refer to the Type-Safe API Developer Guide.

Now, lets build our API by running npx projen build from the root of our monorepo. You will notice that each package in the monorepo is built in dependency order.

Tip

If you run the npx projen build command again without changing any files, you will notice that the build completes in a fraction of the time (1.7s as per below snippet) as it uses cached results and will only re-build packages that have changed since the last time it was built.

>  NX   Successfully ran target build for 7 projects
Nx read the output from the cache instead of running the command for 7 out of 7 tasks.
npx projen build  1.31s user 0.37s system 96% cpu 1.732 total

Visualize your dependency graph

As your codebase grows, the number of sub-packages will likely increase and it is sometimes useful to be able to visualize your dependencies and task dependencies. To do this, simply run npx projen graph from the root of the monorepo which will open a browser for you.

You will now be able to visualize your project level dependencies (i.e. package a depends on package b) along with your task level (order in which tasks are performed i.e. build) dependencies.

Tip

If you have the pdk CLI installed, you can also display the graph by running pdk graph

Add a React website to your monorepo

Let's now add a React website to our monorepo so that we can make authenticated API calls to our newly created API. To do this, we modify our projenrc file to create a new CloudscapeReactTsWebsite as follows:

import { CloudscapeReactTsWebsiteProject } from "@aws/pdk/cloudscape-react-ts-website";
import { MonorepoTsProject } from "@aws/pdk/monorepo";
import {
    DocumentationFormat,
    Language,
    Library,
    ModelLanguage,
    TypeSafeApiProject,
} from "@aws/pdk/type-safe-api";
import { javascript } from "projen";

const monorepo = new MonorepoTsProject({
    name: "my-project",
    packageManager: javascript.NodePackageManager.PNPM,
    projenrcTs: true,
});

const api = new TypeSafeApiProject({
    parent: monorepo,
    outdir: "packages/api",
    name: "myapi",
    infrastructure: {
        language: Language.TYPESCRIPT,
    },
    model: {
        language: ModelLanguage.SMITHY,
        options: {
        smithy: {
            serviceName: {
            namespace: "com.aws",
            serviceName: "MyApi",
            },
        },
        },
    },
    runtime: {
        languages: [Language.TYPESCRIPT],
    },
    documentation: {
        formats: [DocumentationFormat.HTML_REDOC],
    },
    library: {
        libraries: [Library.TYPESCRIPT_REACT_QUERY_HOOKS],
    },
    handlers: {
        languages: [Language.TYPESCRIPT],
    },
});

new CloudscapeReactTsWebsiteProject({
    parent: monorepo,
    outdir: "packages/website",
    name: "website",
    typeSafeApis: [api],
});

monorepo.synth();
from aws_pdk.monorepo import MonorepoPythonProject
from aws_pdk.cloudscape_react_ts_website import CloudscapeReactTsWebsiteProject
from aws_pdk.type_safe_api import *

monorepo = MonorepoPythonProject(
    module_name="my_project",
    name="my-project",
)

api = TypeSafeApiProject(
    name="myapi",
    parent=monorepo,
    outdir="packages/api",
    model=ModelConfiguration(
        language=ModelLanguage.SMITHY,
        options=ModelOptions(
            smithy=SmithyModelOptions(
                service_name=SmithyServiceName(
                    namespace="com.amazon",
                    service_name="MyAPI"
                )
            )
        )
    ),
    runtime=RuntimeConfiguration(
        languages=[Language.PYTHON]
    ),
    infrastructure=InfrastructureConfiguration(
        language=Language.PYTHON
    ),
    documentation=DocumentationConfiguration(
        formats=[DocumentationFormat.HTML_REDOC]
    ),
    handlers=HandlersConfiguration(
        languages=[Language.PYTHON]
    ),
    library=LibraryConfiguration(
        libraries=[Library.TYPESCRIPT_REACT_QUERY_HOOKS]
    )
)

CloudscapeReactTsWebsiteProject(
    parent=monorepo,
    outdir="packages/website",
    type_safe_apis=[api],
    name="website",
)

monorepo.synth()
import software.aws.pdk.monorepo.MonorepoJavaProject;
import software.aws.pdk.monorepo.MonorepoJavaOptions;
import software.aws.pdk.cloudscape_react_ts_website.CloudscapeReactTsWebsiteProject;
import software.aws.pdk.cloudscape_react_ts_website.CloudscapeReactTsWebsiteProjectOptions;
import software.aws.pdk.type_safe_api.*;
import java.util.Arrays;

public class projenrc {
    public static void main(String[] args) {
        MonorepoJavaProject monorepo = new MonorepoJavaProject(MonorepoJavaOptions.builder()
                .name("my-project")
                .build());

        TypeSafeApiProject api = new TypeSafeApiProject(TypeSafeApiProjectOptions.builder()
                .name("myapi")
                .parent(monorepo)
                .outdir("packages/api")
                .model(ModelConfiguration.builder()
                        .language(ModelLanguage.SMITHY)
                        .options(ModelOptions.builder()
                                .smithy(SmithyModelOptions.builder()
                                        .serviceName(SmithyServiceName.builder()
                                                .namespace("com.my.company")
                                                .serviceName("MyApi")
                                                .build())
                                        .build())
                                .build())
                        .build())
                .runtime(RuntimeConfiguration.builder()
                        .languages(Arrays.asList(Language.JAVA))
                        .build())
                .infrastructure(InfrastructureConfiguration.builder()
                        .language(Language.JAVA)
                        .build())
                .documentation(DocumentationConfiguration.builder()
                        .formats(Arrays.asList(DocumentationFormat.HTML_REDOC))
                        .build())
                .library(LibraryConfiguration.builder()
                        .libraries(Arrays.asList(Library.TYPESCRIPT_REACT_QUERY_HOOKS))
                        .build())
                .handlers(HandlersConfiguration.builder()
                        .languages(Arrays.asList(Language.JAVA))
                        .build())
                .build());

        new CloudscapeReactTsWebsiteProject(
            CloudscapeReactTsWebsiteProjectOptions.builder()
                .parent(monorepo)
                .outdir("packages/website")
                .typeSafeApis(Arrays.asList(api))
                .name("website")
                .build());

        monorepo.synth();
    }
}

As always, given we have modified our projenrc file we need to run the npx projen command from the root to synthesize our new website onto the filesystem.

Once the npx projen command is executed, you should see a new packages/website folder which contains all the source code for your new website. To make sure everything is working, let's build our project by running npx projen build from the root.

We can now test that our website works by running the npx projen dev command from the packages/website directory. You will be greeted with an error message as follows:

This is completely normal as given we have passed in the api into the CloudscapeReactTsWebsite construct, it has automatically been configured to integrate with your API which has not been deployed as of yet, and hence no runtime-config.json is available which contains deployed resource identifiers and url's.

In order to get everything working, we need to deploy everything we have just created which will be covered in the following section.

Creating our AWS infrastructure

We now have all of the application specific code required in order to create a distributed application. The missing link is the infrastructure that will deploy these components into the AWS cloud.

Let's add this infrastructure to the monorepo by modifying our projenrc file to include the Infrastructure construct as follows:

import { CloudscapeReactTsWebsiteProject } from "@aws/pdk/cloudscape-react-ts-website";
import { InfrastructureTsProject } from "@aws/pdk/infrastructure";
import { MonorepoTsProject } from "@aws/pdk/monorepo";
import {
    DocumentationFormat,
    Language,
    Library,
    ModelLanguage,
    TypeSafeApiProject,
} from "@aws/pdk/type-safe-api";
import { javascript } from "projen";

const monorepo = new MonorepoTsProject({
    name: "my-project",
    packageManager: javascript.NodePackageManager.PNPM,
    projenrcTs: true,
});

const api = new TypeSafeApiProject({
    parent: monorepo,
    outdir: "packages/api",
    name: "myapi",
    infrastructure: {
        language: Language.TYPESCRIPT,
    },
    model: {
        language: ModelLanguage.SMITHY,
        options: {
            smithy: {
                serviceName: {
                    namespace: "com.aws",
                    serviceName: "MyApi",
                },
            },
        },
    },
    documentation: {
        formats: [DocumentationFormat.HTML_REDOC],
    },
    library: {
        libraries: [Library.TYPESCRIPT_REACT_QUERY_HOOKS],
    },
    handlers: {
        languages: [Language.TYPESCRIPT],
    },
});

const website = new CloudscapeReactTsWebsiteProject({
    parent: monorepo,
    outdir: "packages/website",
    name: "website",
    typeSafeApis: [api],
});

new InfrastructureTsProject({
    parent: monorepo,
    outdir: "packages/infra",
    name: "infra",
    cloudscapeReactTsWebsites: [website],
    typeSafeApis: [api],
});

monorepo.synth();
from aws_pdk.monorepo import MonorepoPythonProject
from aws_pdk.cloudscape_react_ts_website import CloudscapeReactTsWebsiteProject
from aws_pdk.infrastructure import InfrastructurePyProject
from aws_pdk.type_safe_api import *

monorepo = MonorepoPythonProject(
    module_name="my_project",
    name="my-project",
)

api = TypeSafeApiProject(
    name="myapi",
    parent=monorepo,
    outdir="packages/api",
    model=ModelConfiguration(
        language=ModelLanguage.SMITHY,
        options=ModelOptions(
            smithy=SmithyModelOptions(
                service_name=SmithyServiceName(
                    namespace="com.amazon",
                    service_name="MyAPI"
                )
            )
        )
    ),
    runtime=RuntimeConfiguration(
        languages=[Language.PYTHON]
    ),
    infrastructure=InfrastructureConfiguration(
        language=Language.PYTHON
    ),
    documentation=DocumentationConfiguration(
        formats=[DocumentationFormat.HTML_REDOC]
    ),
    handlers=HandlersConfiguration(
        languages=[Language.PYTHON]
    ),
    library=LibraryConfiguration(
        libraries=[Library.TYPESCRIPT_REACT_QUERY_HOOKS]
    )
)

website = CloudscapeReactTsWebsiteProject(
    parent=monorepo,
    outdir="packages/website",
    type_safe_apis=[api],
    name="website",
)

InfrastructurePyProject(
    parent=monorepo,
    outdir="packages/infra",
    name="infra",
    type_safe_apis=[api],
    cloudscape_react_ts_websites=[website]
)

monorepo.synth()
import software.aws.pdk.monorepo.MonorepoJavaProject;
import software.aws.pdk.monorepo.MonorepoJavaOptions;
import software.aws.pdk.cloudscape_react_ts_website.CloudscapeReactTsWebsiteProject;
import software.aws.pdk.cloudscape_react_ts_website.CloudscapeReactTsWebsiteProjectOptions;
import software.aws.pdk.infrastructure.InfrastructureJavaProject;
import software.aws.pdk.infrastructure.InfrastructureJavaProjectOptions;
import software.aws.pdk.type_safe_api.*;
import java.util.Arrays;

public class projenrc {
    public static void main(String[] args) {
        MonorepoJavaProject monorepo = new MonorepoJavaProject(MonorepoJavaOptions.builder()
                .name("my-project")
                .build());

        TypeSafeApiProject api = new TypeSafeApiProject(TypeSafeApiProjectOptions.builder()
                .name("myapi")
                .parent(monorepo)
                .outdir("packages/api")
                .model(ModelConfiguration.builder()
                        .language(ModelLanguage.SMITHY)
                        .options(ModelOptions.builder()
                                .smithy(SmithyModelOptions.builder()
                                        .serviceName(SmithyServiceName.builder()
                                                .namespace("com.my.company")
                                                .serviceName("MyApi")
                                                .build())
                                        .build())
                                .build())
                        .build())
                .runtime(RuntimeConfiguration.builder()
                        .languages(Arrays.asList(Language.JAVA))
                        .build())
                .infrastructure(InfrastructureConfiguration.builder()
                        .language(Language.JAVA)
                        .build())
                .documentation(DocumentationConfiguration.builder()
                        .formats(Arrays.asList(DocumentationFormat.HTML_REDOC))
                        .build())
                .library(LibraryConfiguration.builder()
                        .libraries(Arrays.asList(Library.TYPESCRIPT_REACT_QUERY_HOOKS))
                        .build())
                .handlers(HandlersConfiguration.builder()
                        .languages(Arrays.asList(Language.JAVA))
                        .build())
                .build());

        CloudscapeReactTsWebsiteProject website = new CloudscapeReactTsWebsiteProject(
            CloudscapeReactTsWebsiteProjectOptions.builder()
                .parent(monorepo)
                .outdir("packages/website")
                .typeSafeApis(Arrays.asList(api))
                .name("website")
                .build());

        new InfrastructureJavaProject(
            InfrastructureJavaProjectOptions.builder()
                .parent(monorepo)
                .outdir("packages/infra")
                .name("infra")
                .typeSafeApis(Arrays.asList(api))
                .cloudscapeReactTsWebsites(Arrays.asList(website))
                .build());

        monorepo.synth();
    }
}

As always, given we have modified our projenrc file we need to run the npx projen command from the root to synthesize our new infrastructure onto the filesystem.

You should now see a packages/infra directory containing all of your pre-configured CDK code to deploy your website and API!

Let's now build all of our code by running npx projen build from the root directory. You should notice that all of your infrastructure now synthesizes by inspecting the cdk.out directory of your packages/infra folder. You will also notice a subfolder cdk.out/cdkgraph which will also contain all of your generated diagrams. If you open any of the diagrams, you should see the following which depicts the infrastructure we are about to deploy to AWS:

Deploying our infrastructure to the AWS cloud

We now have everything we need to deploy our infrastructure. To do so, ensure you have authenticated with AWS and that your aws cli is able to communicate with your desired AWS account & region.

Firstly, if the target account has not been bootstrapped, you will need to do this before proceeding.

Warning

Ensure the role you have assumed has enough permissions to create all of the resources required. For full deploy permissions, you can attach the arn:aws:iam::aws:policy/AdministratorAccess policy to your assumed role, although this is not recommended when deploying into production.

We now can deploy our infrastructure by running the following command:

cd packages/infra
npx projen deploy:dev

Note

The npx projen deploy:dev command attempts a CDK hotswap deployment if possible. In a production setting (for example in a ci/cd pipeline) use the npx projen deploy command to ensure a full CloudFormation deployment is performed.

Once the deployment completes, you should see an output that resembles the following:

Congratulations! You have successfully deployed a website and api to AWS!

To check out your website, navigate to the distribution link in the CDK deployment output above to view your website.

Creating a Cognito User

In order to log in to your website, you first need to create a Cognito user. By default, the UserPool is not set up to allow self-registration (this is configurable). To create a Cognito user, follow these steps:

  1. Navigate to the Cognito AWS console within the account you just deployed to.
  2. Click on the user pool you just created
  3. Click "Create user"
  4. In invitation settings, select "Send an email invitation"
  5. Enter a username
  6. Enter an email address
  7. In temporary password, select "Generate a password"
  8. Click "Create user"

You will now be sent an email with a temporary password. Open the email and have it handy.

  1. In a web browser, navigate to your newly deploy website via the cloudfront url contained within the CDK output.
  2. Enter your username and temporary password then click "Sign in".
  3. Enter a new password, family name and given name(s) then click "Confirm".
  4. Open Authenticator on your phone, then setup a new code by scanning the QR code.
  5. Enter the code displayed on Authenticator for your website and then click "Continue".

Congratulations, you have successfully signed in to your website!

You will notice that an API Explorer is provided for you by default, allowing you to experiment with any API's you have defined as part of your Type Safe API in an authenticated and safe manner (sigv4 signed).

Configuring your local website to communicate with backend services

Now that you have deployed your backend infrastructure, let's update the local website environment to communicate with the API and Cognito User Pool that was just deployed. To do this, simply run the following command from the root of your monorepo:

curl https://`aws cloudformation describe-stacks --query "Stacks[?StackName=='infra-dev'][].Outputs[?contains(OutputKey, 'WebsiteDistributionDomainName')].OutputValue" --output text`/runtime-config.json > packages/website/public/runtime-config.json

Now you can start your dev server by running the following command:

cd packages/website
npx projen dev

Destroying the deployed resources

If you plan to continue the learning series and build the Shopping List application, you can save this step for later.

Now that you're done creating your first PDK project, you can choose to destroy your deployed resources to avoid incurring any costs as follows:

cd packages/infra
npx projen destroy

Enter y to approve the changes and delete the infra-dev stack.

Note

The Cognito UserPool and DynamoDB table that were created were configured with a removalPolicy of RETAIN and as such will not be destroyed by the npx projen destroy command. If you wish to remove these resources, you can do so via the AWS CLI or Console.

Next steps

Congratulations, you have now set up your first PDK based monorepo project! Where do you go from here?


Last update: 2024-12-20