Overview
TheSQSQueue construct creates an Amazon SQS queue with an optional dead-letter queue, FIFO ordering, encryption, and a DLQ-depth CloudWatch alarm. Use it to decouple producers and consumers in event-driven architectures.
Every queue is created with enforceSSL: true, so the queue policy denies any request that does not arrive over TLS.
Import
Add a queue from the CLI
fjall add messaging writes the queue into your application’s infrastructure.ts:
type: "queue", so the statement it writes is a valid MessagingFactory call. Modify an existing queue by name:
Every other property in the tables below is authored by hand in
infrastructure.ts.
Using the Messaging Factory
Insideinfrastructure.ts, add SQS through the MessagingFactory:
Direct construct usage
Properties
A value of
0 for visibilityTimeout, receiveMessageWaitTime or
deliveryDelay is treated as unset, and the AWS default applies. To turn long
polling off explicitly, leave receiveMessageWaitTime out.Factory prop differences
MessagingFactory.build accepts IQueueProps, which is a subset of the construct props with one widened field:
The factory also validates ranges eagerly and throws on an out-of-range
visibilityTimeout, messageRetentionPeriod, deliveryDelay, receiveMessageWaitTime or maxMessageSize, and warns when FIFO-only options are set on a standard queue.
Dead-letter queue behaviour
WhendeadLetterQueue.enabled is true and you do not supply your own queue, Fjall creates a companion DLQ alongside the main queue:
Pair the DLQ with
alertsTopic to get a CloudWatch alarm on DLQ depth. The alarm fires on the maximum of ApproximateNumberOfMessagesVisible over one evaluation period, with a default threshold of 0, so any dead-lettered message pages. Override it with alarms: { dlqDepthThreshold: 5 }, or disable it with alarms: false.
A bring-your-own DLQ passed as deadLetterQueue.queue is never alarmed here. Its owner wires the alarms, which keeps a shared DLQ from collecting one duplicate alarm per referencing queue.
Queue-policy limits
The queue’s single resource policy holds the TLS-only statement Fjall adds plus every grant to a service that pushes to the queue: each EventBridge rule, each SNS subscription, each S3 notification. None of those reaches a queue through an IAM role, so the resource policy is their only grant. A grant to a role, such astasks.grantSendMessages(apiService) or the execution role addSqsEventSource grants a polling Lambda, lands on that role’s own policy and costs nothing here.
SQS caps the queue policy at 20 statements and 8,192 bytes, and refuses the whole policy past either.
From fjall 38 SQSQueue checks the policy when the application synthesises, so an over-budget queue is refused before the deploy instead of rolling the stack back mid-deploy.
The check covers the queue and the dead-letter queue Fjall creates. A dead-letter queue you pass as deadLetterQueue.queue is not checked here. It is checked only when it is itself a Fjall SQSQueue, by that construct.
Each refusal names the queue’s construct path and tells you to split the grants across queues.
An EventBridge rule costs its ARN inside a shared statement rather than a statement of its own, so rules exhaust the byte budget long before the statement count. That byte count is an upper bound. An ARN CloudFormation has not created yet is sized at the longest its partition, region, account and name can be, so the refusal can arrive a few grants before SQS’s own would.
The thresholds above do not apply to a queue created with
encryption: "SSE_KMS". Its delivery grant names the account rather than each rule, so one statement covers however many rules deliver to it. As a rule’s dead-letter queue it still shares the dead-letter statement like any other queue.
A reference the check cannot size counts at CDK’s own ARN estimate of 150 bytes, and the refusal says how many references that applied to. Lower the estimate when those ARNs are shorter, by adding this one key to the context block already in the application’s cdk.json. Leave the rest of that block in place, including @aws-cdk/aws-iam:minimizePolicies:
cdk.json
AWS::SQS::QueuePolicy in place, including a queue with a single rule. No queue or rule is replaced, and the same rules are granted before and after. Regenerate any snapshot that reads a queue policy, and see the changelog for the migration.
Methods
Both the construct and the factory wrapper expose these:
The factory wrapper adds the
IQueueMessaging interface names:
Examples
Standard queue with a DLQ
DLQ alarm on a shared topic
FIFO queue
FIFO queues preserve message order and deduplicate within a five-minute window:Long polling
Long polling cuts empty receive responses and the API calls they cost:Retained queue
Connecting to Lambda
addSqsEventSource takes a CDK IQueue, so pass the underlying queue rather than the Fjall wrapper:
visibilityTimeout on the queue to at least the function’s timeout, otherwise a slow invocation lets the same message be redelivered while it is still in flight.
CloudFormation Outputs
Output keys are built from the construct id converted to PascalCase, sonew SQSQueue(scope, "order-events", {}) produces OrderEventsQueueUrl.
A bring-your-own DLQ emits no DLQ outputs, since the queue is owned by another stack.
Next Steps
Messaging Factory
Create queues, topics, and event buses through one factory
Lambda Function
Process queue messages with Lambda
SNS Topic
Fan out one publish to many subscribers
EventBridge
Route events with rules and schedules