

# Integrating with existing infrastructure
<a name="existing-infrastructure"></a>

 AWS Blocks is designed to work with existing AWS infrastructure. Whether you have an existing CDK application, pre-deployed resources, or a mix of both, you can adopt AWS Blocks incrementally without rewriting your infrastructure.

## Choosing a pattern
<a name="existing-patterns-overview"></a>

 AWS Blocks provides four patterns for integrating with existing infrastructure. Choose based on your situation:


| Pattern | Effort | Use when | Local support | 
| --- | --- | --- | --- | 
| CDK in AWS Blocks | Low | No Block exists for the resource you need | ❌ Manual | 
|  `fromExisting`  | Lowest | A Block exists and you have a pre-deployed resource | ✅ Automatic | 
| Custom Block | Medium | You’re repeating the same resource pattern in multiple places | ✅ Automatic | 
| Vendorize | Low | A first-party Block is almost right but needs CDK changes | ✅ Automatic | 

## Pattern 1: CDK in AWS Blocks
<a name="existing-cdk-in-blocks"></a>

Use raw CDK constructs alongside Blocks. AWS Blocks gives you two shapes for this:

 ** `BlocksStack` **: A standalone CloudFormation stack containing the AWS Blocks Lambda and API Gateway. Use for new projects or when you want Blocks isolated in its own deployment unit.

 ** `BlocksBackend` **: A CDK Construct you drop into an existing stack. Use when you already have stacks and want Blocks to live alongside your other resources.

Both expose a `.handler` (the Lambda function) that you can grant permissions to and inject environment variables into.

### Example: BlocksStack with an SQS queue
<a name="example-blocksstack-with-an-sqs-queue"></a>

```
// aws-blocks/index.cdk.ts
import * as cdk from 'aws-cdk-lib';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { BlocksStack } from '@aws-blocks/blocks/cdk';

const app = new cdk.App();
const stack = await BlocksStack.create(app, 'my-app', {
  backendHandlerPath: './index.handler.ts',
  backendCDKPath: './index.ts',
});

const queue = new sqs.Queue(stack, 'work-queue');
queue.grantSendMessages(stack.handler);
stack.handler.addEnvironment('QUEUE_URL', queue.queueUrl);
```

```
// aws-blocks/index.ts
import { Scope, ApiNamespace } from '@aws-blocks/blocks';
import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';

const scope = new Scope('my-app');
const sqs = new SQSClient({});

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async enqueue(payload: Record<string, unknown>) {
    await sqs.send(new SendMessageCommand({
      QueueUrl: process.env.QUEUE_URL!,
      MessageBody: JSON.stringify(payload),
    }));
    return { ok: true };
  },
}));
```

### Example: BlocksBackend inside an existing stack
<a name="example-blocksbackend-inside-an-existing-stack"></a>

Use a static factory method because `BlocksBackend.create()` is asynchronous and constructors cannot use `await`:

```
// my-existing-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { BlocksBackend } from '@aws-blocks/blocks/cdk';

export class MyApiStack extends cdk.Stack {
  static async create(scope: cdk.App, id: string, props?: cdk.StackProps) {
    const stack = new MyApiStack(scope, id, props);

    // Your existing resources
    const queue = new sqs.Queue(stack, 'work-queue');

    // Drop Blocks in as a Construct
    const blocks = await BlocksBackend.create(stack, 'BlocksApi', {
      backendHandlerPath: './aws-blocks/index.handler.ts',
      backendCDKPath: './aws-blocks/index.ts',
    });

    queue.grantSendMessages(blocks.handler);
    blocks.handler.addEnvironment('QUEUE_URL', queue.queueUrl);
    return stack;
  }
}
```

### Tradeoffs
<a name="tradeoffs"></a>
+ ✅ Full access to any AWS resource with plain CDK and SDK
+ ❌ No local implementation. `npm run dev` will call real AWS unless you stub the SDK yourself
+ ❌ No type-level guarantee that environment variables exist

If you find yourself repeating this pattern, consider creating a [custom Block](custom-building-blocks.md) instead.

## Pattern 2: fromExisting
<a name="existing-from-existing"></a>

Some Blocks can wrap a pre-deployed AWS resource. The Block provides its typed API and local implementation, but skips provisioning. You keep ownership of the resource.

Supported Blocks:


| Block | Factory method | Wraps | 
| --- | --- | --- | 
|  `KVStore`  |  `KVStore.fromExisting(tableName)`  | Existing DynamoDB table | 
|  `DistributedTable`  |  `DistributedTable.fromExisting(tableName)`  | Existing DynamoDB table | 
|  `FileBucket`  |  `FileBucket.fromExisting(bucketName)`  | Existing S3 bucket | 
|  `Database`  |  `Database.fromExisting({ …​ })`  | Existing RDS instance | 
|  `AuthCognito`  |  `AuthCognito.fromExisting(userPoolId)`  | Existing Cognito User Pool | 

### Example
<a name="example"></a>

```
// aws-blocks/index.ts
import { Scope, KVStore, ApiNamespace } from '@aws-blocks/blocks';

const scope = new Scope('my-app');

const sessions = new KVStore(scope, 'sessions', {
  table: KVStore.fromExisting('my-legacy-sessions-table'),
});

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async getSession(token: string) {
    return sessions.get(token);
  },
}));
```

### Tradeoffs
<a name="tradeoffs-2"></a>
+ ✅ Local development still works. `npm run dev` uses in-memory storage, not the real table
+ ✅ IAM permissions are granted automatically to the AWS Blocks Lambda
+ ✅ Same typed API as a AWS Blocks-managed resource
+ ❌ Limited to the Block’s API surface. If you need features the BB doesn’t expose, use Pattern 1
+ ❌ Cross-account resources require manual IAM (fall back to Pattern 1)

## Pattern 3: Custom Block
<a name="existing-custom-bb"></a>

When you’re repeating Pattern 1 for the same resource type, wrap it in a custom Block. This gives you a typed API, a local implementation, and reusability.

See [Creating custom Blocks](custom-building-blocks.md) for a complete guide to authoring Blocks.

### When to use
<a name="when-to-use"></a>
+ You have ≥2 callers using the same resource pattern
+ You want `npm run dev` to work without AWS for this resource
+ You want type-safe call sites instead of raw `process.env` \+ SDK

## Pattern 4: Vendorize
<a name="existing-vendorize"></a>

If a first-party Block is *almost* right but needs CDK changes you can’t get upstream quickly, you can eject its source into your project:

```
npx @aws-blocks/vendorize bb-kv-store ./packages/bb-kv-store
```

This copies the Block source into your monorepo. You now own it. Modify the CDK, runtime, or local implementation as needed.

### Tradeoffs
<a name="tradeoffs-3"></a>
+ ✅ Full control over the Block implementation
+ ✅ Local implementations, types, and API surface all still work
+ ❌ You’re responsible for maintenance. Upstream updates require manual re-sync
+ ❌ Only use when a custom wrapping BB (Pattern 3) or upstream PR won’t solve the problem