Pagination and waiters utilities for handling large result sets and waiting for asynchronous operations to complete. This module provides helper functions for working with SES operations that may return paginated results or require polling.
Handle large result sets that are returned across multiple API calls.
/**
* Configuration for SES pagination operations
*/
interface SESPaginationConfiguration extends PaginationConfiguration {
client: SESClient;
}
/**
* Paginate through list of verified identities
* @param config - Pagination configuration with SES client
* @param input - List identities input parameters
* @returns AsyncIterable paginator for identities
*/
function paginateListIdentities(
config: SESPaginationConfiguration,
input: ListIdentitiesCommandInput
): Paginator<ListIdentitiesCommandOutput>;
/**
* Paginate through custom verification email templates
* @param config - Pagination configuration with SES client
* @param input - List templates input parameters
* @returns AsyncIterable paginator for templates
*/
function paginateListCustomVerificationEmailTemplates(
config: SESPaginationConfiguration,
input: ListCustomVerificationEmailTemplatesCommandInput
): Paginator<ListCustomVerificationEmailTemplatesCommandOutput>;Usage Example:
import {
SESClient,
paginateListIdentities,
paginateListCustomVerificationEmailTemplates
} from "@aws-sdk/client-ses";
const client = new SESClient({ region: "us-east-1" });
// Paginate through all identities
const identityPaginator = paginateListIdentities(
{ client },
{ IdentityType: "EmailAddress", MaxItems: 10 }
);
console.log("All verified email addresses:");
for await (const page of identityPaginator) {
if (page.Identities) {
page.Identities.forEach(identity => {
console.log(`- ${identity}`);
});
}
}
// Paginate through custom verification templates
const templatePaginator = paginateListCustomVerificationEmailTemplates(
{ client },
{ MaxResults: 5 }
);
console.log("\nCustom verification email templates:");
for await (const page of templatePaginator) {
if (page.CustomVerificationEmailTemplates) {
page.CustomVerificationEmailTemplates.forEach(template => {
console.log(`- ${template.TemplateName}: ${template.TemplateSubject}`);
});
}
}Working with pagination configuration and custom processing.
// Example of using pagination with custom configuration
import {
SESClient,
paginateListIdentities,
ListIdentitiesCommandInput
} from "@aws-sdk/client-ses";
const client = new SESClient({ region: "us-east-1" });
async function getAllIdentitiesWithDetails() {
const paginationConfig = {
client,
pageSize: 50, // Custom page size
};
const input: ListIdentitiesCommandInput = {
IdentityType: "Domain", // Only domains
};
const identities: string[] = [];
const paginator = paginateListIdentities(paginationConfig, input);
try {
for await (const page of paginator) {
if (page.Identities) {
identities.push(...page.Identities);
console.log(`Loaded ${page.Identities.length} identities (total: ${identities.length})`);
}
}
return identities;
} catch (error) {
console.error("Error paginating identities:", error);
throw error;
}
}
// Usage
const allDomains = await getAllIdentitiesWithDetails();
console.log(`Found ${allDomains.length} verified domains`);Wait for asynchronous operations to complete with built-in retry logic.
/**
* Waiter configuration for SES operations
*/
interface WaiterConfiguration<T> {
client: T;
maxWaitTime?: number;
minDelay?: number;
maxDelay?: number;
}
/**
* Waits for identity verification to complete (deprecated)
* @param params - Waiter configuration
* @param input - Identity verification input
* @returns Promise that resolves when identity is verified
* @deprecated Use waitForIdentityExists instead
*/
function waitForIdentityExists(
params: WaiterConfiguration<SESClient>,
input: GetIdentityVerificationAttributesCommandInput
): Promise<WaiterResult>;
/**
* Waits for identity verification to complete
* @param params - Waiter configuration
* @param input - Identity verification input
* @returns Promise that resolves when identity is verified
* @throws WaiterTimeoutError if verification doesn't complete within maxWaitTime
*/
function waitForIdentityExists(
params: WaiterConfiguration<SESClient>,
input: GetIdentityVerificationAttributesCommandInput
): Promise<WaiterResult>;Usage Example:
import {
SESClient,
VerifyEmailIdentityCommand,
waitForIdentityExists,
WaiterTimeoutError
} from "@aws-sdk/client-ses";
const client = new SESClient({ region: "us-east-1" });
async function verifyEmailAndWait(emailAddress: string) {
console.log(`Starting verification for ${emailAddress}`);
// Start verification process
const verifyCommand = new VerifyEmailIdentityCommand({
EmailAddress: emailAddress,
});
await client.send(verifyCommand);
console.log("Verification email sent. Waiting for completion...");
try {
// Wait for verification to complete
const waiterResult = await waitForIdentityExists(
{
client,
maxWaitTime: 300, // 5 minutes
minDelay: 3, // 3 seconds minimum delay
maxDelay: 30, // 30 seconds maximum delay
},
{
Identities: [emailAddress],
}
);
if (waiterResult.state === "SUCCESS") {
console.log(`✅ ${emailAddress} verified successfully!`);
return true;
} else {
console.log(`❌ Verification failed for ${emailAddress}`);
return false;
}
} catch (error) {
if (error instanceof WaiterTimeoutError) {
console.log(`⏰ Verification timed out for ${emailAddress}`);
console.log("The user may not have clicked the verification link yet.");
return false;
} else {
console.error("Unexpected error during verification wait:", error);
throw error;
}
}
}
// Usage
const success = await verifyEmailAndWait("user@example.com");
if (success) {
console.log("Email is now ready for sending!");
}Complete example of domain verification with DNS setup and waiting.
import {
SESClient,
VerifyDomainIdentityCommand,
waitForIdentityExists
} from "@aws-sdk/client-ses";
async function verifyDomainWithWaiter(domain: string) {
const client = new SESClient({ region: "us-east-1" });
console.log(`Starting domain verification for ${domain}`);
// 1. Start domain verification
const verifyCommand = new VerifyDomainIdentityCommand({
Domain: domain,
});
const verifyResult = await client.send(verifyCommand);
console.log("DNS Setup Required:");
console.log(`Add this TXT record to ${domain}:`);
console.log(`Name: _amazonses.${domain}`);
console.log(`Value: ${verifyResult.VerificationToken}`);
console.log("\nWaiting for DNS propagation and verification...");
// 2. Wait for verification (this may take a while for domains)
try {
const waiterResult = await waitForIdentityExists(
{
client,
maxWaitTime: 900, // 15 minutes for DNS propagation
minDelay: 10, // 10 seconds minimum delay
maxDelay: 60, // 60 seconds maximum delay
},
{
Identities: [domain],
}
);
if (waiterResult.state === "SUCCESS") {
console.log(`✅ Domain ${domain} verified successfully!`);
// 3. Optionally set up DKIM
console.log("Setting up DKIM for better deliverability...");
const { VerifyDomainDkimCommand } = await import("@aws-sdk/client-ses");
const dkimCommand = new VerifyDomainDkimCommand({ Domain: domain });
const dkimResult = await client.send(dkimCommand);
console.log("DKIM Setup Required:");
console.log("Add these CNAME records:");
dkimResult.DkimTokens?.forEach((token, index) => {
console.log(`${token}._domainkey.${domain} -> ${token}.dkim.amazonses.com`);
});
return true;
} else {
console.log(`❌ Domain verification failed for ${domain}`);
return false;
}
} catch (error) {
console.error("Error during domain verification:", error);
return false;
}
}
// Usage
const domainVerified = await verifyDomainWithWaiter("example.com");
if (domainVerified) {
console.log("Domain is ready for sending emails!");
}interface PaginationConfiguration {
client: SESClient;
pageSize?: number;
}
interface Paginator<T> extends AsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}interface WaiterResult {
state: WaiterState;
reason?: string;
}
type WaiterState = "SUCCESS" | "FAILURE" | "RETRY" | "TIMEOUT";
class WaiterTimeoutError extends Error {
name: "TimeoutError";
constructor(message: string);
}Process multiple operations with proper error handling and rate limiting.
// Example utility for batch operations (not part of AWS SDK)
interface BatchOperationOptions {
batchSize: number;
delayBetweenBatches: number;
maxRetries: number;
}
async function batchVerifyIdentities(
client: SESClient,
identities: string[],
options: BatchOperationOptions = {
batchSize: 5,
delayBetweenBatches: 1000,
maxRetries: 3,
}
) {
const results: Array<{ identity: string; success: boolean; error?: string }> = [];
// Process in batches to respect rate limits
for (let i = 0; i < identities.length; i += options.batchSize) {
const batch = identities.slice(i, i + options.batchSize);
const batchPromises = batch.map(async (identity) => {
let attempts = 0;
while (attempts < options.maxRetries) {
try {
const isEmail = identity.includes('@');
if (isEmail) {
const { VerifyEmailIdentityCommand } = await import("@aws-sdk/client-ses");
await client.send(new VerifyEmailIdentityCommand({
EmailAddress: identity,
}));
} else {
const { VerifyDomainIdentityCommand } = await import("@aws-sdk/client-ses");
await client.send(new VerifyDomainIdentityCommand({
Domain: identity,
}));
}
return { identity, success: true };
} catch (error) {
attempts++;
if (attempts >= options.maxRetries) {
return {
identity,
success: false,
error: error instanceof Error ? error.message : String(error)
};
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
}
}
});
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
// Delay between batches
if (i + options.batchSize < identities.length) {
await new Promise(resolve => setTimeout(resolve, options.delayBetweenBatches));
}
}
return results;
}
// Usage
const identities = ["user1@example.com", "user2@example.com", "example.com"];
const results = await batchVerifyIdentities(client, identities);
console.log("Batch verification results:");
results.forEach(result => {
if (result.success) {
console.log(`✅ ${result.identity}: Verification started`);
} else {
console.log(`❌ ${result.identity}: ${result.error}`);
}
});import {
SESClient,
VerifyDomainIdentityCommand,
VerifyDomainDkimCommand,
waitForIdentityExists,
paginateListIdentities
} from "@aws-sdk/client-ses";
const client = new SESClient({ region: "us-east-1" });
async function completeVerificationSetup(domain: string) {
console.log("=== Complete SES Domain Setup ===\n");
// 1. Check if already verified
console.log("1. Checking existing verification status...");
const identityPaginator = paginateListIdentities(
{ client },
{ IdentityType: "Domain" }
);
let alreadyVerified = false;
for await (const page of identityPaginator) {
if (page.Identities?.includes(domain)) {
alreadyVerified = true;
break;
}
}
if (alreadyVerified) {
console.log(`✅ ${domain} is already verified`);
} else {
// 2. Start verification
console.log("2. Starting domain verification...");
const verifyResult = await client.send(new VerifyDomainIdentityCommand({
Domain: domain,
}));
console.log(`📋 DNS Record Required:`);
console.log(` Type: TXT`);
console.log(` Name: _amazonses.${domain}`);
console.log(` Value: ${verifyResult.VerificationToken}`);
// 3. Wait for verification
console.log("\n3. Waiting for DNS propagation and verification...");
console.log(" (This may take several minutes)");
try {
await waitForIdentityExists(
{ client, maxWaitTime: 900, minDelay: 15, maxDelay: 60 },
{ Identities: [domain] }
);
console.log(`✅ ${domain} verified successfully!`);
} catch (error) {
console.log(`⏰ Verification timed out. Check DNS record and try again later.`);
return false;
}
}
// 4. Set up DKIM
console.log("\n4. Setting up DKIM authentication...");
const dkimResult = await client.send(new VerifyDomainDkimCommand({
Domain: domain,
}));
console.log("📋 DKIM CNAME Records Required:");
dkimResult.DkimTokens?.forEach((token, index) => {
console.log(` ${index + 1}. ${token}._domainkey.${domain} -> ${token}.dkim.amazonses.com`);
});
console.log(`\n🎉 Setup complete for ${domain}!`);
console.log("Next steps:");
console.log("1. Add the DKIM CNAME records to your DNS");
console.log("2. Enable DKIM signing once DNS propagates");
console.log("3. Configure Mail-From domain (optional)");
console.log("4. Set up bounce/complaint handling");
return true;
}
// Usage
completeVerificationSetup("mycompany.com").catch(console.error);