SNS is a fully implemented service in LocalStack Explorer. It supports topic management, subscriptions with multiple protocols, message publishing (single and batch), topic attributes editing, filter policies, and tag management.
- List, create, and delete topics
- View and edit topic attributes (DisplayName, Policy, DeliveryPolicy)
- Create subscriptions with support for SQS, HTTP/S, Email, Email-JSON, and Lambda protocols
- SQS subscriptions link directly to the corresponding queue detail page
- Delete subscriptions
- View and edit subscription filter policies (JSON editor)
- Publish messages with optional Subject, Message Attributes, and Target ARN
- JSON editor (Monaco, lazy-loaded) for composing message bodies with validation and formatting
- Batch publish with per-entry results (successful/failed)
- Tag management: list, add, and remove tags
- Search/filter topics by name
All endpoints are prefixed with /api/sns.
| Method | Path | Description | Request | Response |
|---|---|---|---|---|
| GET | / |
List topics | -- | { topics: [...] } |
| POST | / |
Create topic | { name: string } |
{ message: string } |
| DELETE | /:topicName |
Delete topic | -- | { success: boolean } |
| GET | /:topicName/attributes |
Topic attributes | -- | { topic: {...} } |
| PUT | /:topicName/attributes |
Set topic attribute | { attributeName, attributeValue } |
{ success: boolean } |
| Method | Path | Description | Request | Response |
|---|---|---|---|---|
| GET | /:topicName/subscriptions |
List subscriptions | -- | { subscriptions: [...] } |
| POST | /:topicName/subscriptions |
Create subscription | { protocol, endpoint } |
{ message, subscriptionArn } |
| DELETE | /subscriptions/:subscriptionArn |
Delete subscription | -- | { success: boolean } |
| GET | /subscriptions/:subscriptionArn/attributes |
Subscription attributes | -- | { subscription: {...} } |
| PUT | /subscriptions/:subscriptionArn/filter-policy |
Set filter policy | { filterPolicy: object } |
{ success: boolean } |
Note: Subscription ARNs in URL parameters must be URL-encoded since they contain colons.
| Method | Path | Description | Request | Response |
|---|---|---|---|---|
| POST | /:topicName/publish |
Publish message | { message, subject?, messageAttributes?, targetArn? } |
{ messageId: string } |
| POST | /:topicName/publish-batch |
Publish batch | { entries: [{ id, message, subject?, messageAttributes? }] } |
{ successful: [...], failed: [...] } |
| Method | Path | Description | Request | Response |
|---|---|---|---|---|
| GET | /:topicName/tags |
List tags | -- | { tags: [...] } |
| POST | /:topicName/tags |
Add tags | { tags: [{ key, value }] } |
{ success: boolean } |
| DELETE | /:topicName/tags |
Remove tags | { tagKeys: [string] } |
{ success: boolean } |
List topics:
curl http://localhost:3001/api/sns{
"topics": [
{
"topicArn": "arn:aws:sns:us-east-1:000000000000:my-topic",
"name": "my-topic"
}
]
}Create topic:
curl -X POST http://localhost:3001/api/sns \
-H "Content-Type: application/json" \
-d '{"name": "my-topic"}'Get topic attributes:
curl http://localhost:3001/api/sns/my-topic/attributes{
"topic": {
"topicArn": "arn:aws:sns:us-east-1:000000000000:my-topic",
"displayName": "",
"owner": "000000000000",
"policy": "{...}",
"deliveryPolicy": "",
"effectiveDeliveryPolicy": "{...}",
"subscriptionsConfirmed": 2,
"subscriptionsPending": 0,
"subscriptionsDeleted": 0
}
}Set topic display name:
curl -X PUT http://localhost:3001/api/sns/my-topic/attributes \
-H "Content-Type: application/json" \
-d '{"attributeName": "DisplayName", "attributeValue": "My Topic"}'Create SQS subscription:
curl -X POST http://localhost:3001/api/sns/my-topic/subscriptions \
-H "Content-Type: application/json" \
-d '{"protocol": "sqs", "endpoint": "arn:aws:sqs:us-east-1:000000000000:my-queue"}'Publish message:
curl -X POST http://localhost:3001/api/sns/my-topic/publish \
-H "Content-Type: application/json" \
-d '{"message": "Hello, World!", "subject": "Test"}'Publish message with attributes:
curl -X POST http://localhost:3001/api/sns/my-topic/publish \
-H "Content-Type: application/json" \
-d '{
"message": "{\"event\": \"order.created\", \"orderId\": 123}",
"subject": "Order Event",
"messageAttributes": {
"eventType": { "dataType": "String", "stringValue": "order.created" }
}
}'{
"messageId": "abc-123-def-456"
}Publish batch:
curl -X POST http://localhost:3001/api/sns/my-topic/publish-batch \
-H "Content-Type: application/json" \
-d '{
"entries": [
{ "id": "msg-1", "message": "First message" },
{ "id": "msg-2", "message": "Second message", "subject": "Batch" }
]
}'{
"successful": [
{ "id": "msg-1", "messageId": "abc-123" },
{ "id": "msg-2", "messageId": "def-456" }
],
"failed": []
}Set subscription filter policy:
curl -X PUT "http://localhost:3001/api/sns/subscriptions/arn%3Aaws%3Asns%3Aus-east-1%3A000000000000%3Amy-topic%3Aabc-123/filter-policy" \
-H "Content-Type: application/json" \
-d '{"filterPolicy": {"eventType": ["order.created", "order.updated"]}}'Add tags:
curl -X POST http://localhost:3001/api/sns/my-topic/tags \
-H "Content-Type: application/json" \
-d '{"tags": [{"key": "environment", "value": "development"}]}'Remove tags:
curl -X DELETE http://localhost:3001/api/sns/my-topic/tags \
-H "Content-Type: application/json" \
-d '{"tagKeys": ["environment"]}'The SNS service maps AWS SDK errors to appropriate HTTP status codes:
| Scenario | Status | Error Code |
|---|---|---|
| Topic not found | 404 | NOT_FOUND |
| Subscription not found | 404 | NOT_FOUND |
| Invalid parameter | 400 | INVALID_PARAMETER |
| Authorization error | 403 | AUTHORIZATION_ERROR |
All errors return a consistent JSON shape:
{
"error": "NOT_FOUND",
"message": "Topic 'arn:aws:sns:...' not found",
"statusCode": 404
}The SNS plugin consists of four files in packages/backend/src/plugins/sns/:
| File | Purpose |
|---|---|
index.ts |
Plugin registration -- creates the SNS client and service, registers routes |
service.ts |
SNSService class -- business logic wrapping AWS SDK calls |
routes.ts |
Fastify route definitions with TypeBox validation schemas |
schemas.ts |
TypeBox schemas for all request inputs and response outputs |
| Method | AWS SDK Command | Description |
|---|---|---|
listTopics() |
ListTopicsCommand |
Returns all topics with name extracted from ARN |
createTopic(name) |
CreateTopicCommand |
Creates a new standard topic |
deleteTopic(topicArn) |
DeleteTopicCommand |
Deletes a topic |
getTopicAttributes(topicArn) |
GetTopicAttributesCommand |
Returns all topic attributes |
setTopicAttributes(arn, name, value) |
SetTopicAttributesCommand |
Modifies a single topic attribute |
| Method | AWS SDK Command | Description |
|---|---|---|
listSubscriptionsByTopic(topicArn) |
ListSubscriptionsByTopicCommand |
Returns subscriptions for a topic |
createSubscription(topicArn, protocol, endpoint) |
SubscribeCommand |
Creates a new subscription |
deleteSubscription(subscriptionArn) |
UnsubscribeCommand |
Removes a subscription |
getSubscriptionAttributes(subscriptionArn) |
GetSubscriptionAttributesCommand |
Returns all subscription attributes |
setSubscriptionFilterPolicy(arn, policy) |
SetSubscriptionAttributesCommand |
Sets the FilterPolicy attribute |
| Method | AWS SDK Command | Description |
|---|---|---|
publishMessage(topicArn, message, options?) |
PublishCommand |
Publishes a single message with optional subject/attrs |
publishBatch(topicArn, entries) |
PublishBatchCommand |
Publishes multiple messages, returns success/failure |
| Method | AWS SDK Command | Description |
|---|---|---|
listTagsForResource(topicArn) |
ListTagsForResourceCommand |
Returns all topic tags |
tagResource(topicArn, tags) |
TagResourceCommand |
Adds tags to a topic |
untagResource(topicArn, tagKeys) |
UntagResourceCommand |
Removes tags by key |
Routes use :topicName as a URL parameter and reconstruct the full ARN using LocalStack defaults:
arn:aws:sns:us-east-1:000000000000:{topicName}
Subscription ARNs in URL parameters are URL-encoded/decoded since they contain colons.
The SNS frontend is in packages/frontend/src/components/sns/ and packages/frontend/src/routes/sns/.
| Component | Description |
|---|---|
TopicList |
Table of topics with search, create dialog, and delete with confirmation |
TopicCreateDialog |
Modal dialog for creating a new topic |
TopicDetail |
Tabbed view: Attributes, Subscriptions, Publish, Tags |
TopicAttributes |
View/edit topic attributes with inline editing for DisplayName and JSON policies |
SubscriptionList |
Subscription table with protocol badges, SQS queue links, and filter policy access |
SubscriptionCreateDialog |
Multi-protocol subscription form with SQS queue selector |
FilterPolicyDialog |
JSON editor for viewing and editing subscription filter policies |
PublishMessageForm |
Single and batch publish with Monaco editor, message attributes, subject, targetArn |
TagManager |
Tag CRUD with inline add form and per-tag deletion |
| Route | Component | Description |
|---|---|---|
/sns |
TopicList |
List and manage topics |
/sns/:topicName |
TopicDetail |
Topic detail with tabbed view |
| Tab | Component | Description |
|---|---|---|
| Attributes | TopicAttributes |
Topic configuration, subscription counts, JSON policies |
| Subscriptions | SubscriptionList |
Manage subscriptions with protocol-specific features |
| Publish | PublishMessageForm |
Compose and publish messages (single or batch) |
| Tags | TagManager |
Add and remove resource tags |
The subscription creation dialog adapts its endpoint input based on the selected protocol:
| Protocol | Endpoint Input |
|---|---|
| SQS | Dropdown of existing queues (from SQS service) or manual ARN input |
| HTTP/HTTPS | URL text input |
| Email address input | |
| Email-JSON | Email address input |
| Lambda | Lambda function ARN input |
SQS subscriptions in the subscription list include a clickable link that navigates to /sqs/:queueName.
The publish form includes a Monaco JSON editor (same as SQS):
- Monaco Editor loaded lazily (
React.lazy+Suspense) to keep the initial bundle small - JSON mode toggle: switch between plain text and JSON editing with validation
- Format button: pretty-prints JSON in the editor
- Single publish: message body, optional subject, target ARN, and dynamic message attributes (name, type, value)
- Batch publish: add multiple entries each with ID, message, and optional subject; results show successful/failed entries
All hooks are in packages/frontend/src/api/sns.ts:
| Hook / Function | Type | Query Key / Notes |
|---|---|---|
useListTopics() |
Query | ["sns", "topics"] |
useTopicAttributes(topicName) |
Query | ["sns", "topic", topicName, "attributes"] |
useTopicSubscriptions(topicName) |
Query | ["sns", "topic", topicName, "subscriptions"] |
useSubscriptionAttributes(arn) |
Query | ["sns", "subscription", arn, "attributes"] |
useTopicTags(topicName) |
Query | ["sns", "topic", topicName, "tags"] |
useCreateTopic() |
Mutation | Invalidates ["sns", "topics"] |
useDeleteTopic() |
Mutation | Invalidates ["sns", "topics"] |
useSetTopicAttribute(topicName) |
Mutation | Invalidates topic attributes |
useCreateSubscription(topicName) |
Mutation | Invalidates topic subscriptions |
useDeleteSubscription(topicName) |
Mutation | Invalidates topic subscriptions |
useSetFilterPolicy(arn) |
Mutation | Invalidates subscription attributes |
usePublishMessage(topicName) |
Mutation | Invalidates topic attributes (subscription counts) |
usePublishBatch(topicName) |
Mutation | Invalidates topic attributes (subscription counts) |
useAddTags(topicName) |
Mutation | Invalidates topic tags |
useRemoveTags(topicName) |
Mutation | Invalidates topic tags |
Mutations automatically invalidate the relevant query cache, so the UI refreshes after every create, delete, publish, or tag operation.