SQS
Lightweight, dependency-free fake of AWS SQS that speaks the real modern SQS JSON wire protocol (AWS JSON 1.0, query-compatible), so application code using @aws-sdk/client-sqs can run against it with zero cost and zero side effects.
| Key | Value |
|---|---|
| Port | 4568 |
| Protocol | AWS SQS JSON (AWS JSON 1.0, awsQueryCompatible) over HTTP |
| Compatible client | @aws-sdk/client-sqs (v3) |
| Size | ~80 KB |
| Startup | < 100ms |
| State | In-memory, ephemeral, resettable |
Quick Start
Start the server:
import { SqsServer } from "./services/sqs/src/server.js";
const server = new SqsServer(4568);
await server.start();
// ... use it ...
await server.stop();
Connect with the real AWS SDK client:
import {
SQSClient,
CreateQueueCommand,
SendMessageCommand,
ReceiveMessageCommand,
DeleteMessageCommand,
} from "@aws-sdk/client-sqs";
const sqs = new SQSClient({
region: "us-east-1",
endpoint: "http://127.0.0.1:4568",
credentials: { accessKeyId: "parlel", secretAccessKey: "parlel" },
});
const { QueueUrl } = await sqs.send(new CreateQueueCommand({ QueueName: "jobs" }));
await sqs.send(new SendMessageCommand({ QueueUrl, MessageBody: "hello world" }));
const { Messages } = await sqs.send(
new ReceiveMessageCommand({ QueueUrl, MaxNumberOfMessages: 1 }),
);
console.log(Messages[0].Body); // "hello world"
await sqs.send(
new DeleteMessageCommand({ QueueUrl, ReceiptHandle: Messages[0].ReceiptHandle }),
);
Queue URLs
Created queues return a URL of the form http://127.0.0.1:4568/{accountId}/{queueName}
(default account id 000000000000). The SDK's useQueueUrlAsEndpoint behavior uses
the URL's origin as the request endpoint, so the host/port in the URL must match the
running fake — which it does automatically. Bare queue names are also accepted wherever
a QueueUrl is expected (the last path segment is used as the queue name).
Wire protocol
- Requests are
POST /with headerX-Amz-Target: AmazonSQS.<Operation>andContent-Type: application/x-amz-json-1.0. The body is the JSON-serialized input. - Success responses are
200withContent-Type: application/x-amz-json-1.0and a JSON body containing the output shape. - Errors are non-2xx with a JSON body
{ "__type": "<Code>", "message": "<msg>" }plus the query-compatible headerx-amzn-query-error: <Code>;<Sender|Receiver>.
Authentication
SigV4 signatures are accepted but not verified (any credentials work). This matches LocalStack-style local development.
MD5 checksums
The real @aws-sdk/client-sqs validates MD5OfMessageBody, MD5OfBody, and
MD5OfMessageAttributes locally after each call. This fake computes each of those
exactly the way AWS does (including the canonical length-prefixed message-attribute
digest), so the SDK's built-in validation passes.
Implemented Operations
All 23 operations exposed by @aws-sdk/client-sqs are implemented and tested.
Queue lifecycle
CreateQueue— standard & FIFO queues, attribute defaults, idempotent re-create, tags.DeleteQueueGetQueueUrlListQueues— prefix filter andMaxResults/NextTokenpagination.
Queue attributes
GetQueueAttributes— including computedQueueArn,ApproximateNumberOfMessages,ApproximateNumberOfMessagesNotVisible,ApproximateNumberOfMessagesDelayed,CreatedTimestamp,LastModifiedTimestamp.SetQueueAttributesPurgeQueue
Messaging
SendMessage— delay, message attributes, system attributes, FIFO group/dedup.SendMessageBatch— up to 10 entries, partial success/failure.ReceiveMessage—MaxNumberOfMessages,VisibilityTimeout, system & message attribute selection, FIFO ordering, receive-count tracking.DeleteMessageDeleteMessageBatchChangeMessageVisibilityChangeMessageVisibilityBatch
Tags
TagQueueUntagQueueListQueueTags
Permissions
AddPermissionRemovePermission
Dead-letter queues & message move tasks
ListDeadLetterSourceQueuesStartMessageMoveTaskCancelMessageMoveTaskListMessageMoveTasks
Behavioral notes
- Visibility timeout: received messages are hidden for
VisibilityTimeoutseconds (default 30, or the queue's configured value). They become visible again automatically when the timeout elapses, or immediately when set to0via receive orChangeMessageVisibility. Deleting a message before the timeout removes it permanently. - FIFO queues (
.fifosuffix,FifoQueue=true): requireMessageGroupId; require eitherMessageDeduplicationIdorContentBasedDeduplication=true. Deduplication is enforced within a 5-minute window. Messages carry a monotonically increasingSequenceNumberand are received in FIFO order. - Delay: per-message
DelaySecondsor the queue'sDelaySecondsattribute keeps a message invisible until the delay elapses. - Message move tasks:
StartMessageMoveTaskdrains the source (DLQ) into the destination queue (explicitDestinationArn, or the original source queue inferred from a matchingRedrivePolicy). Completes synchronously in this fake.
Surface coverage
This emulator faithfully replicates the API surface most application code and agents exercise. Anything below the supported lines is either an intentional design choice for a fast, zero-cost local emulator (✓ By design) or a candidate for a future release (⟳ Roadmap) — never a silent inaccuracy.
Legend: ✅ fully supported · ◐ accepted (stored, not strictly enforced) · ✓ by design · ⟳ on the roadmap.
| Feature | Status | Notes |
|---|---|---|
| Standard queues | ✅ Supported | |
| FIFO queues + dedup + ordering | ✅ Supported | 5-minute dedup window |
| Message attributes + MD5 | ✅ Supported | AWS-canonical MD5 digest |
| Message system attributes | ✅ Supported | e.g. AWSTraceHeader |
| Visibility timeout / delay | ✅ Supported | real timers, auto re-visibility |
| Batch send/delete/visibility | ✅ Supported | partial-failure semantics |
| Tags | ✅ Supported | |
| Permissions (Add/Remove) | ✅ Supported | stored, not enforced |
| Dead-letter source listing | ✅ Supported | derived from RedrivePolicy |
| Message move tasks (redrive) | ✅ Supported | synchronous completion |
| Pagination | ✅ Supported | ListQueues MaxResults/NextToken |
Long polling (WaitTimeSeconds) | ⚠️ Accepted, returns immediately | no blocking wait |
| Server-side encryption / KMS | ⚠️ Attributes stored only | no real crypto |
| SigV4 signature verification | ✓ By design — Structurally faithful tokens; cryptographic verification is skipped for local use | |
| Actual message retention expiry | ✓ By design — Intentional for a local, zero-cost test emulator | |
Redrive maxReceiveCount auto-DLQ | ✓ By design — Not enforced |
Error codes
Errors are returned as { "__type": "<Code>", "message": "..." } with the
x-amzn-query-error: <Code>;<Fault> header. The SDK surfaces them as typed exceptions
(the modeled name may differ from the wire code, e.g. the wire code
AWS.SimpleQueueService.NonExistentQueue surfaces as QueueDoesNotExist).
| Wire code | HTTP | When |
|---|---|---|
AWS.SimpleQueueService.NonExistentQueue | 400 | Queue URL/name does not exist (→ QueueDoesNotExist) |
QueueAlreadyExists | 400 | Re-create with conflicting attributes (→ QueueNameExists) |
InvalidParameterValue | 400 | Bad queue name, FIFO mismatch, oversized body, bad visibility, etc. |
InvalidAttributeName | 400 | Unknown attribute passed to SetQueueAttributes |
MissingParameter | 400 | Required parameter omitted |
ReceiptHandleIsInvalid | 400 | Malformed receipt handle on delete |
AWS.SimpleQueueService.MessageNotInflight | 400 | ChangeMessageVisibility on a non-inflight message |
AWS.SimpleQueueService.EmptyBatchRequest | 400 | Batch request with no entries |
AWS.SimpleQueueService.TooManyEntriesInBatchRequest | 400 | More than 10 batch entries |
AWS.SimpleQueueService.BatchEntryIdsNotDistinct | 400 | Duplicate Id within a batch |
ResourceNotFoundException | 404 | Move task / move-task source not found |
InvalidAction | 400 | Unknown X-Amz-Target operation |
Resetting state
State is in-memory and ephemeral. Reset it programmatically or over HTTP:
server.reset(); // in-process
await fetch("http://127.0.0.1:4568/_parlel/reset", { method: "POST" }); // over HTTP
Health check
GET http://127.0.0.1:4568/_parlel/health
→ { "status": "ok", "service": "sqs", "queues": <n> }
<!-- parlel:testenv:start -->
Configuration — test.env
Copy these into your test.env (used by the bridge sidecar flow). Tokens are Parlel's seeded test credentials — any non-empty value is accepted by the emulator, so you rarely need to change them. Swap in real credentials only when pointing at the live service in prod.env.
AWS_ACCESS_KEY_ID=parlel
AWS_SECRET_ACCESS_KEY=parlel
AWS_REGION=us-east-1
AWS_ENDPOINT_URL_SQS=http://parlel-bridge:4568
AWS_ENDPOINT_URL=http://parlel-bridge:4568
<!-- parlel:testenv:end -->