Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Docs build passed |
📖 Docs PR preview links
|
| @@ -0,0 +1,254 @@ | |||
| --- | |||
| id: nexus-standalone-activity | |||
| title: Nexus Standalone Activity | |||
There was a problem hiding this comment.
Reading this gave me an interesting thought . Should this doc live under SAA rather than under Nexus. The PRD names this feature is Standalone Activity: Start from Nexus, which is actually a clearer name for this. If we leave here the name really should be something like Standalone Activity Started By Nexus
|
|
||
| :::caution | ||
|
|
||
| Activity-backed Nexus Operations are pre-release and build on [Nexus SDK V2](/nexus/sdk-v2). |
|
|
||
| ::: | ||
|
|
||
| A Nexus Operation can be backed by a [Standalone Activity](/standalone-activity) instead of a Workflow. |
There was a problem hiding this comment.
I don't like framing this as "instead of a workflow" . The framing really should be A NexusOperations can Start a Workflow or an Activity.
Note we also need to update the https://docs.temporal.io/nexus/operations to highlight this.
"...Nexus Operation has an operation token that can be used to re-attach to a long-running Operation backed by a Workflow or a Standalone Activity".
| Starting the Operation starts an Activity Execution that has no parent Workflow, and the Operation completes when that Activity returns. | ||
|
|
||
| This is the right shape when the work behind an Operation is a single step with no orchestration: call an external API, run a computation, send a notification. | ||
| Before Activity-backed Operations, exposing an Activity through Nexus meant writing a Workflow whose only job was to call that one Activity — a wrapper with its own Event History, its own Task Queue considerations, and no value of its own. |
There was a problem hiding this comment.
I don't like comparing this to before state. The key reason why is that I want this documentation to target new customers that aren't using Nexus, and want to use this a new pattern, not because it is cleaner than than some old pattern that they weren't using.
| A Nexus Operation can be backed by a [Standalone Activity](/standalone-activity) instead of a Workflow. | ||
| Starting the Operation starts an Activity Execution that has no parent Workflow, and the Operation completes when that Activity returns. | ||
|
|
||
| This is the right shape when the work behind an Operation is a single step with no orchestration: call an external API, run a computation, send a notification. |
There was a problem hiding this comment.
We need to beef up this section A LOT. If I'm a new user why do I want to use the Standalone Activities. We need "real world use cases":
- This is a GREAT pattern for invoking an MCP Tool in a different security context to sandbox it.
- This is a great pattern to durably invoke any other system. (IE any other service)
- It's a great way to execute the ensure durable write to unreliable 3rd party systems ((IE to build a home-grown connector, or integrate with 3rd party systems, or unreliable internal infrastructure)
| This is the right shape when the work behind an Operation is a single step with no orchestration: call an external API, run a computation, send a notification. | ||
| Before Activity-backed Operations, exposing an Activity through Nexus meant writing a Workflow whose only job was to call that one Activity — a wrapper with its own Event History, its own Task Queue considerations, and no value of its own. | ||
|
|
||
| These compose. A Standalone Nexus Operation can be backed by a Standalone Activity, which means neither side has a Workflow. |
There was a problem hiding this comment.
Get rid of these compose. One common pattern is to Use a Standalone Nexus Operation to invoke a
| This is the right shape when the work behind an Operation is a single step with no orchestration: call an external API, run a computation, send a notification. | ||
| Before Activity-backed Operations, exposing an Activity through Nexus meant writing a Workflow whose only job was to call that one Activity — a wrapper with its own Event History, its own Task Queue considerations, and no value of its own. | ||
|
|
||
| These compose. A Standalone Nexus Operation can be backed by a Standalone Activity, which means neither side has a Workflow. |
There was a problem hiding this comment.
Focus here on use cases and patterns it could be used for.
The signature use case: durable webhook processing without owning a queue
The scenario: A product ingests webhooks from third-party SaaS providers — Stripe payment events, GitHub push events, Twilio delivery receipts, Segment events, Salesforce change notifications. Each webhook needs to trigger a durable one-shot task: update a downstream system, kick off a notification, index into search, sync into a data warehouse.
The current shape most customers land on:
- Stateless HTTP receiver behind a load balancer accepts the webhook
- Receiver drops the payload onto SQS / Kafka / Redis
- A worker fleet consumes and processes with hand-rolled retries + dead-letter handling
- Idempotency is enforced with a Redis SETNX on the webhook ID
That's a queue, a worker, retry logic, DLQ tooling, idempotency store, dashboards — all just to run one function reliably.
With Standalone Nexus Op → Standalone Activity, it collapses to:
// Inside the webhook HTTP handler — no workflow, no proxy
sc := temporalClient.NexusServiceClient(client.NexusServiceClientOptions{
Endpoint: "billing-events",
Service: "stripe",
})
handle, _ := sc.StartOperation(ctx, "process-
client.StartNexusOperationOptions{
OperationID: stripeEventID, // idempotency, dedup
ScheduleToCloseTimeout: 24 * time.Hou
})
// return 200 to Stripe immediately — Temporal owns delivery
On the other side, process-payment-event is a Nexus operation whose handler is a Standalone Activity. That's it. No caller workflow, no handler workflow, no queue, no DLQ table, no retry harness.
Why this specific combination wins
┌────────────────────────┬─────────────────────────────────────────────────────────────┐
│ Property │ Why it matters for webhooks │
├────────────────────────┼─────────────────────────────────────────────────────────────┤
│ No caller workflow │ HTTP webhook receivers are inherently stateless. Wrapping every incoming request in a │
│ (Standalone Nexus Op) │ proxy workflow is Cloud Actions before any real work │
│ │ happens. │
├────────────────────────┼─────────────────────────────────────────────────────────────┤
│ No handler workflow │ Most webhook processors do one thing: transform + write. A state machine adds nothing. 1 │
│ (Standalone Activity) │ Cloud Action per event vs. 2 is 50% cost savings at scale — and Stripe/GitHub webhook │
│ │ volume is high. │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤
│ Operation ID = │ Stripe/GitHub/Twilio all send event.id you can use as the dedup key. Nexus conflict │
│ provider event ID │ policy gives you exactly-once semantics for free — no Redis, no dedup table. │
├────────────────────────┼─────────────────────────────────────────────────────────────┤
│ │ The team owning the webhook receiver is usually not the team owning the downstream │
│ Cross-namespace / │ processing. The bients; the growth team consumes Segment │
│ cross-team ownership │ events. Nexus endpoint = clean team boundary with scoped access, not full namespace │
│ │ write. │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤
│ │ Webhook processing hits unreliable downstreams (CRM APIs, analytics tools). Standalone │
│ Circuit breaker safety │ Activity wrapping the Nexus circuit breaker on repeated │
│ │ retryable errors — this is the flagship reason this pattern exists. │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤
│ 200-back-fast │ Stripe/GitHub retry aggressively if you don't 200 within seconds. Fire the Nexus op → │
│ │ return 200 → Tempoe. │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤
│ Long-tail retry │ Downstream CRM is try policy handles it. No DLQ triage │
│ │ ritual. │
├────────────────────────┼─────────────────────────────────────────────────────────────┤
│ Addressable │ Support asks "what23?" → look it up by Operation ID. Get │
│ │ status, retry count, last error, result. Cancelable if the event was later invalidated. │
└────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────┘
Alternatives it beats
- SQS + Lambda worker fleet: You've built a small queueing system with hand-rolled dedup + DLQ. Nexus SIA gives you all
of that plus visibility and dedup for free. - Wrapper workflow per webhook: Works today, but doubles Cloud Actions and adds workflow-history bloat for genuinely
stateless work. - Direct activity from a proxy workflow (Duolingo gateway pattern): Adds a caller workflow you don't need — Standalone
Nexus Ops eliminate it. - Raw HTTP call from receiver to downstream service: No durability. The moment your process crashes after the DB write, the event is lost.
Adjacent use cases with the same shape
The same Standalone-Nexus-Op → Standalone-Activity pattern fits any "external trigger, one durable step, cross-team"
scenario:
- BFF-triggered async user actions — user clite my report," "revoke my sessions." BFF fires a Standalone Nexus Op, returns an operation handle to the client, no proxy workflow.
- Kafka/event-consumer offload — consumer reas, commits its offset. Temporal ownsdurability from there. Event ID = Operation ID for dedup.
- CI/CD infra actions — GitHub Action or Jenknce scan," "kick off canary deploy step,""regenerate credentials." Platform team owns the handler; consumer teams get scoped endpoint access.
- Cron/scheduler-triggered platform tasks — K scheduler fires a Standalone Nexus Op →shared platform team runs the task.
| </SdkTabs.DotNet> | ||
| </SdkTabs> | ||
|
|
||
| The Activities themselves are ordinary Activities. |
There was a problem hiding this comment.
I feel weird about saying the "Activities are ordinary Activities". This assumes knowledge of the Activity and I don't like characterizing an Activity as ordinary, that is one of our most powerful primatives.
I think the thing to highlight is the same Activities can be invoked from either traditional workfllows or by Nexus invoked SAA
| **Sync messaging — as many as you need.** Reach these through `client.getWorkflowClient()`. | ||
| They take effect during the handler call, still get link propagation, and do not require an async backing. | ||
|
|
||
| - Signal, Signal-with-Start, Query, Cancel, and Terminate |
There was a problem hiding this comment.
I don't think we are going to have these 3 at pre-release: Query, Cancel, and Terminate
| Prefer `TemporalOperationHandler` for new work, including simple cases. | ||
| Using one type everywhere means a handler that starts out synchronous can grow an async backing, or pick up a Signal, without changing shape. | ||
|
|
||
| Beyond the handler, [parent-close policy](/nexus/operations) parity with Child Workflows — deciding what happens to the handler Workflow when the caller completes, fails, or is cancelled — is still outstanding in every SDK. |
There was a problem hiding this comment.
This is a very confusing sentence, and we aren't going to have parent-close-policy at pre-release, so I wouldn't even bring it up
| The Service contract, [Nexus Endpoint](/nexus/endpoints) setup, and Worker registration are the same as before. | ||
| What changes is the handler you write. | ||
|
|
||
| ## Why it changed |
There was a problem hiding this comment.
I'm struggling a bit with the whole framing of this doc, I DON'T want it to focus on why it is better than V1. TBH there were so few customers using the V1, we can basically assume that the vast majority of people coming to this doc are new. I really want this doc to just espouse all of the new amazingness that the programing model can do. Only at the very end should we even make reference to the old one to talk about the improvements -> migration path...
|
|
||
| This walkthrough builds one Nexus Service from nothing to a complete API, adding a single Nexus capability at each step. | ||
|
|
||
| ## Nexus Introduction |
There was a problem hiding this comment.
The Development walk through is not a good place to give an introduction to Nexus use cases. These should live on https://docs.temporal.io/evaluate/nexus (But also, keep this out of the scope of these changes)
|
|
||
| **A shared facade for extensibility.** A Nexus Service can front something that is not a Temporal Workflow at all — an existing internal API, a legacy job queue, a third-party endpoint. Write the wrapper once, run it as one Worker fleet, and every team calls the same Operations instead of each writing its own integration. Because callers only depend on the contract, the team behind it can modify or update the service without breaking anyone. | ||
|
|
||
| ## The sample problem |
There was a problem hiding this comment.
The entire development walkthrough should stay tighter to implementing this specific scenario. And each steps should be framed around the scenario, (IE Let's define the purchase object, now we need to have our sales team use it in Golang, but our distribution team wants to use it in .NET, we need to make a call to a 3rd party system to make an Authorization call...let's use a Nexus Invoked Standalone Activity, because it is just one call, we need to make a call to an order auditing system that needs to run a multi-step workflow, let's use a Invoke a Workflow, etc)
Note for documentation reviewers - do not review or merge. I am sending this to some internal folks as we are discussing what we want to do.
┆Attachments: EDU-6917 Nexus V2 Documentation - don't review or merge!!