Skip to content

CDN Deployment

The CdnDeployment construct automates the configuration of Statista's internal CDN to route traffic to your application. It creates a custom CloudFormation resource that writes routing configuration to AWS Systems Manager Parameter Store in the CDN account, enabling path-based traffic routing from the CDN to your application.

[!NOTE] This construct was previously available in @pit-shared/remix-tools/cdk but has been moved to @pit-shared/cdk/cdn-deployment for better dependency management. The old import path is still supported but deprecated.

Requirements

  • Your application must implement the CdnDeploymentTarget interface (provide a URL and CDN secret)
  • The application must validate the CDN secret on all incoming requests to ensure traffic originates from the CDN
  • Must specify the target environment (production, prod, or stage)
  • Must provide a basePath for routing (e.g., /my-app)

Basic Usage Example

import { CdnDeployment } from '@pit-shared/cdk/cdn-deployment'

declare const scope: import('aws-cdk-lib').Stack

new CdnDeployment(scope, 'cdn', {
    environmentName: 'prod',
    basePath: '/my-app',
    app: {
        url: 'https://my-app.example.com',
        cdnSecret: 'shared-secret-value',
    },
})

Usage with Custom App Paths

If you only want specific paths under your basePath to be routed (rather than all paths), use the appPaths property. The basePath is still required but won't be used as a routing entry when appPaths is specified.

import { CdnDeployment } from '@pit-shared/cdk/cdn-deployment'

declare const scope: import('aws-cdk-lib').Stack

new CdnDeployment(scope, 'cdn', {
    environmentName: 'prod',
    basePath: '/my-app',
    appPaths: ['/my-app/api', '/my-app/public', '/my-app/assets'],
    app: {
        url: 'https://my-app.example.com',
        cdnSecret: 'shared-secret-value',
    },
})

Usage with Datadog Integration

Connect the CDN deployment with Datadog to monitor the custom resource Lambda function.

import { CdnDeployment } from '@pit-shared/cdk/cdn-deployment'
import { Datadog } from '@pit-shared/cdk/datadog'

declare const scope: import('aws-cdk-lib').Stack

const datadog = new Datadog(scope, 'datadog', {
    apiKey: 'api-key',
    env: 'prod',
    service: 'my-app',
    team: 'architects',
    cluster: 'other',
    costCategory: 'operational',
    repositoryUrl: 'github.com/my-org/my-repo',
    version: '1.0.0',
})

new CdnDeployment(scope, 'cdn', {
    environmentName: 'prod',
    basePath: '/my-app',
    datadog,
    app: {
        url: 'https://my-app.example.com',
        cdnSecret: 'shared-secret-value',
    },
})

Usage in default Environments

import { CdnDeployment } from '@pit-shared/cdk/cdn-deployment'

declare const scope: import('aws-cdk-lib').Stack

new CdnDeployment(scope, 'cdn', {
    environmentName: 'stage',
    basePath: '/my-app',
    app: {
        url: 'https://my-app-stage.example.com',
        cdnSecret: 'shared-secret-value',
    },
})

Integration with RemixApp

RemixApp implements the CdnDeploymentTarget interface, so you can pass it directly to CdnDeployment.

import { RemixApp } from '@pit-shared/remix-tools/cdk'
import { CdnDeployment } from '@pit-shared/cdk/cdn-deployment'

declare const scope: import('aws-cdk-lib').Stack

const app = new RemixApp(scope, 'remix', {
    root: '.',
    fargate: {
        dns: {
            domainName: 'example.com',
            hostedZoneId: '1234',
            zoneName: 'example.com',
            skipCName: true,
        },
        network: {
            vpcId: 'vpc-1234567890abcdef0',
            internetGatewayId: 'igw-1234',
        },
    },
})

new CdnDeployment(scope, 'cdn', {
    environmentName: 'prod',
    basePath: '/my-remix-app',
    app: app,
})

Integration with FargateApp

FargateApp provides an asCdnTarget() method to enable CDN deployment integration.

[!NOTE] FargateApp requires calling .asCdnTarget() to use with CdnDeployment, unlike RemixApp which can be passed directly. This is because FargateApp is a general-purpose construct that doesn't always require CDN integration.

import { FargateApp } from '@pit-shared/cdk/fargate'
import { CdnDeployment } from '@pit-shared/cdk/cdn-deployment'
import * as ecs from 'aws-cdk-lib/aws-ecs'

declare const scope: import('aws-cdk-lib').Stack

class MyFargateApp extends FargateApp {
    protected getContainerImage(): ecs.ContainerImage {
        return ecs.ContainerImage.fromAsset('.')
    }
}
const app = new MyFargateApp(scope, 'fargate', {
    dns: {
        domainName: 'example.com',
        hostedZoneId: '1234',
        zoneName: 'example.com',
    },
    network: {
        vpcId: 'vpc-1234567890abcdef0',
        internetGatewayId: 'igw-1234',
    },
})

new CdnDeployment(scope, 'cdn', {
    environmentName: 'prod',
    basePath: '/my-fargate-app',
    app: app.asCdnTarget(),
})

API

CdnDeploymentProps

/**
 * The app to publish.
 *
 * @see {CdnDeploymentTarget}
 */
app: CdnDeploymentTarget
/**
 * The applications basePath (e.g. /outlook)
 */
basePath: `/${string}`
/**
 * The applications paths to register (e.g. /outlook).
 *
 * These app paths are required if not all paths under the basePath should
 * be routed into the application. Still the `basePath` has to be
 * configured.
 * If the app paths are set, the `basePath` is not used as a routing entry.
 */
appPaths?: `/${string}`[]
/**
 * This prop could be used to force update deployment data.
 * If it's not set it defaults to 0. Whenever the version changes,
 * a deployment is enforced. So set it to one and increment to
 * force updates.
 *
 * This parameter should rarely be necessary to use. Use with caution.
 *
 * @default 0
 */
version?: number
/**
 * The log group to use, otherwise a new one is created.
 */
logGroup?: logs.ILogGroup
/**
 * Integrate this custom resource with datadog
 */
datadog?: Datadog

CdnDeploymentTarget

/**
 * URL to route requests to.
 */
url: string
/**
 * Secret shared between the CDN and the application.
 */
cdnSecret: string

Migration Guide

If you're currently importing from @pit-shared/remix-tools/cdk, update your imports:

// ❌ Deprecated (still works)
import { CdnDeployment } from '@pit-shared/remix-tools/cdk'

// ✅ Current
import { CdnDeployment } from '@pit-shared/cdk/cdn-deployment'