EventBridge

Lightweight, dependency-free fake of AWS EventBridge that speaks the real EventBridge AWS JSON 1.1 wire protocol (X-Amz-Target: AWSEvents.<Operation>, Content-Type: application/x-amz-json-1.1), so application code using @aws-sdk/client-eventbridge can run against it with zero cost and zero side effects.

KeyValue
Port4573
ProtocolAWS JSON 1.1 (X-Amz-Target: AWSEvents.<Op>) over HTTP
Target prefixAWSEvents
Compatible client@aws-sdk/client-eventbridge (v3)
Size~90 KB
Startup< 100ms
StateIn-memory, ephemeral, resettable

Quick Start

Start the server:

import { EventbridgeServer } from "./services/eventbridge/src/server.js";

const server = new EventbridgeServer(4573);
await server.start();
// ... use it ...
await server.stop();

Connect with the real AWS SDK client:

import {
  EventBridgeClient,
  CreateEventBusCommand,
  PutRuleCommand,
  PutTargetsCommand,
  PutEventsCommand,
} from "@aws-sdk/client-eventbridge";

const eb = new EventBridgeClient({
  region: "us-east-1",
  endpoint: "http://127.0.0.1:4573",
  credentials: { accessKeyId: "parlel", secretAccessKey: "parlel" },
});

// Create a custom event bus (the "default" bus is seeded automatically)
const { EventBusArn } = await eb.send(new CreateEventBusCommand({ Name: "orders" }));

// Create a rule with an event pattern
const { RuleArn } = await eb.send(
  new PutRuleCommand({
    Name: "order-placed",
    EventBusName: "orders",
    EventPattern: JSON.stringify({ source: ["my.app"], "detail-type": ["OrderPlaced"] }),
  }),
);

// Attach a target
await eb.send(
  new PutTargetsCommand({
    Rule: "order-placed",
    EventBusName: "orders",
    Targets: [{ Id: "1", Arn: "arn:aws:lambda:us-east-1:000000000000:function:handler" }],
  }),
);

// Publish an event (matching events are routed to rules in-memory)
const res = await eb.send(
  new PutEventsCommand({
    Entries: [
      {
        Source: "my.app",
        DetailType: "OrderPlaced",
        Detail: JSON.stringify({ orderId: 123 }),
        EventBusName: "orders",
      },
    ],
  }),
);
// res.Entries[0].EventId is the generated event id

Implemented operations

All 57 operations exposed by @aws-sdk/client-eventbridge are implemented and tested.

Event buses

Permissions (event-bus resource policy)

Rules

Targets

Events

Archives

Replays

Connections

API destinations

Global endpoints

Partner event sources (producer side)

Event sources (consumer side)

Tagging (rules, event buses, archives)

Event pattern matching

PutRule, TestEventPattern and PutEvents routing all run the same content-based filtering engine. Supported matchers:

MatcherExample
Exact value{ "source": ["my.app"] }
Nested fields{ "detail": { "state": ["ok"] } }
prefix{ "source": [{ "prefix": "my." }] }
prefix + equals-ignore-case{ "source": [{ "prefix": { "equals-ignore-case": "MY." } }] }
suffix{ "source": [{ "suffix": ".app" }] }
equals-ignore-case{ "detail": { "name": [{ "equals-ignore-case": "widgetco" }] } }
wildcard{ "detail": { "name": [{ "wildcard": "Widget*" }] } }
cidr{ "detail": { "ip": [{ "cidr": "10.0.0.0/24" }] } }
exists{ "detail": { "state": [{ "exists": true }] } }
anything-but{ "source": [{ "anything-but": ["x"] }] }
numeric{ "detail": { "amount": [{ "numeric": [">", 10, "<", 100] }] } }

Internal / non-AWS endpoints

These are convenience endpoints for tests and tooling (not part of EventBridge):

MethodPathDescription
GET/_parlel/healthReturns { status, service, eventBuses, rules }
POST/_parlel/resetClears all in-memory state and re-seeds the default bus

server.reset() does the same thing in-process. Captured state useful for assertions: server.putEvents (every accepted PutEvents entry) and server.routedEvents (events that matched an enabled rule's pattern).

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.

FeatureStatusNotes
All 57 @aws-sdk/client-eventbridge operations✅ SupportedFull happy-path + key edge cases
Event-bus management + resource policies✅ SupportedStatement and full-policy forms
Rules (pattern + schedule)✅ SupportedPattern validated on write
Targets✅ Supported5-per-request limit, failed-entry reporting
PutEvents + in-memory rule routing✅ SupportedMatching events captured on server.routedEvents
TestEventPattern content filtering✅ Supportedprefix/suffix/numeric/exists/cidr/anything-but/wildcard/equals-ignore-case
Archives, replays, connections, API destinations, endpoints✅ SupportedLifecycle + describe/list
Partner event sources + event sources✅ SupportedProducer + consumer side
Tagging✅ SupportedRules, event buses, archives
Pagination (NextToken)✅ SupportedBase64 offset tokens
Actual target delivery (Lambda/SQS/SNS invocation)⚠️ SimulatedTargets are stored; matching events are recorded, not delivered to live targets
Schedule expression firing (rate/cron)⟳ Roadmap
Replay event re-delivery⚠️ SimulatedReplays complete instantly without re-emitting events
IAM / SigV4 auth enforcement✓ By design — Not enforced
KMS encryption⚠️ Stored onlyKmsKeyIdentifier persisted, no real crypto
Connection OAuth token exchange⚠️ SimulatedConnections move to AUTHORIZED without calling an auth endpoint

Error codes & shapes

Errors are returned as non-2xx responses with Content-Type: application/x-amz-json-1.1, an x-amzn-errortype header, and a JSON body of the form:

{ "__type": "ResourceNotFoundException", "message": "Rule foo does not exist on EventBus default." }
CodeHTTPWhen
ValidationException400Invalid/missing parameters, bad names, too many entries/targets
InvalidEventPatternException400Event pattern is not valid JSON / not an object
ResourceNotFoundException400Bus / rule / archive / replay / connection / destination / endpoint / source missing
ResourceAlreadyExistsException400Creating a resource whose name already exists
LimitExceededException400More than 5 targets in one PutTargets
ManagedRuleException400Deleting an AWS-managed rule without Force
IllegalStatusException400Cancelling a replay that already completed
ConcurrentModificationException400Modeled (reserved)
InternalException500Unexpected server-side failure
AccessDeniedException403Non-POST HTTP method
ThrottlingException429Modeled (reserved)

Per-entry operations (PutEvents, PutPartnerEvents, PutTargets, RemoveTargets) return 200 with a FailedEntryCount and a list of failed entries carrying ErrorCode / ErrorMessage rather than failing the whole request.

<!-- 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_EVENTBRIDGE=http://parlel-bridge:4573
AWS_ENDPOINT_URL=http://parlel-bridge:4573
<!-- parlel:testenv:end -->