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
There was a problem hiding this comment.
I had a lot of similar thoughts about naming confusion. I'm not at all sure!
|
|
||
| ::: | ||
|
|
||
| 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".
There was a problem hiding this comment.
I get your point, but this is the doc about starting a standalone activity - so in this document, it seems to make sense to differentiate.
Either way, I removed it from the description, and modified the text in the doc a few lines lower.
| 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.
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.
| **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
| 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...
|
|
||
| **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)
| - **A boundary between teams.** Callers get scoped access to named Operations, not write access to your Namespace, and they never see your Task Queues, your Workers, or your implementation. | ||
| - **Room to change your mind.** As long as the contract holds you can rewrite what runs behind an Operation, including replacing the Activity with a Workflow later, and no caller changes. | ||
|
|
||
| Callers are free either way: a caller Workflow can invoke the Operation as one step of a larger process, or a Client can start it directly as a [Standalone Nexus Operation](/standalone-nexus-operation) with no Workflow on either side. |
There was a problem hiding this comment.
Callers are free either way... that word is scary. I think you mean free to implement either way right? We've got to be careful not confuse that free as in operations don't carry a cost factor.
|
|
||
| Callers are free either way: a caller Workflow can invoke the Operation as one step of a larger process, or a Client can start it directly as a [Standalone Nexus Operation](/standalone-nexus-operation) with no Workflow on either side. | ||
|
|
||
| Here are some example use cases. |
There was a problem hiding this comment.
I really like this section, but would prefer use cases this primitive is optimized for. Or more ideal, these are the scenarios that customers use this functionality for...
There was a problem hiding this comment.
Took me a few drafts but how is "A sampling of customer use cases this was built to address follows."
| @@ -0,0 +1,84 @@ | |||
| --- | |||
| id: index | |||
| title: Nexus Development Walkthrough - Java SDK | |||
There was a problem hiding this comment.
I'm wondering if we want to name this Microservice Development Walkthrough?
While it is not strictly limited to be used for microservices, it more clearly cues the customer use case that we think they would use this for.
|
|
||
| A [Nexus Service](/evaluate/nexus) is a contract that one team publishes and other teams call, across [Namespace](/namespaces) boundaries, without sharing code or a deployment. | ||
|
|
||
| ## A sample problem |
There was a problem hiding this comment.
A Reference Example
|
|
||
| ## A sample problem | ||
|
|
||
| A purchase request needs approval before it can proceed. |
There was a problem hiding this comment.
To aid with the use of this Developer Guide, it is best to walk through it grounded in how it would be used to solve a real world problem, so we are going to solve the problem: A purchase request needs approval before it can proceed.
There was a problem hiding this comment.
Also, the approver problem type (or Human-In-The-Loop, HITL) is a very good reference problem because it is a key scenario that customers use Temporal and Nexus for.
|
|
||
| Writing the contract first is what makes the Service polyglot. | ||
|
|
||
| **Every sample in this walkthrough, in every language, is generated from this one contract.** A Go caller can call the Java handler built here. The Java caller built here can call a Python handler. Neither side hand-writes the request and response types, so neither side can drift from the other. |
There was a problem hiding this comment.
I like this sentiment, "A Go caller can call the Java handler built here. The Java caller built here can call a Python handler. Neither side hand-writes the request and response types, so neither side can drift from the other." BUT it leaves me confused WHAT language are you going to use for caller in the tutorial and what language are you going to use to the handler in the tutorial? I think we add sentence to clarify that.
| @@ -0,0 +1,506 @@ | |||
| --- | |||
| id: nexus-sdk-v2 | |||
There was a problem hiding this comment.
TODO We need a better name for V2 Nexus SDK. Maybe we just call it the Temporal Operations Handler and frame it as a significantly improved Development Primitive optimized for Nexus.
|
|
||
| Because the stub is the generated interface, the call is type-checked against the contract at compile time. A field the contract does not have will not compile, and a payload the contract forbids is rejected by the generated validator before it reaches the wire. | ||
|
|
||
| `{sample code will be here}` |
There was a problem hiding this comment.
Implement in Java and call from Java doesn't really make the scenario pop. If the guide shows how to implement the service in Java, I propose that we show how to call from another language such as Python to really show off the power. We can of course keep the how to call the service from Java there too (to make it easy to find), but I think we should lead showing off it being called in another language.
|
|
||
| ## Before you start | ||
|
|
||
| You need two Namespaces, one for the handler and one for the caller, so the walkthrough crosses a real Namespace boundary. A [local development server](/develop/run-a-development-server) with two Namespaces is enough for steps 1 through 4; step 5 covers both the development server and Temporal Cloud. |
There was a problem hiding this comment.
Put in the commands to create the sample namespaces for them if they don't already have namespaces they want to work from.
|
|
||
| `TemporalOperationHandler.create(...)` gives your start handler a context, a Nexus-aware Client, and the Operation input. Call `startWorkflow` on that Client and return its result. The Operation then completes when the Workflow returns, delivering the Workflow's return value to the caller. | ||
|
|
||
| The Client is not an ordinary Temporal Client. It propagates bidirectional links and request Ids automatically, so the caller's Execution and the approval Workflow are connected in the UI. Fetching your own Client inside a handler works but gives up that linking. |
There was a problem hiding this comment.
I REALLY like this line to explain the Temporal Operation Handler. We should clearly use this or similar to describe the reason for why we introduced Nexus SDK Ergonomics improvements
|
|
||
| Endpoint names are unique within the Registry. In Temporal Cloud the Registry is global across your whole Account and spans every Namespace; in a self-hosted deployment it is scoped to the Cluster. | ||
|
|
||
| ## Allow caller Namespaces |
There was a problem hiding this comment.
There is an important distinction here, this step only applies if you are doing this guide against a Temporal Cloud backing instance. If this is being done against a self hosted Temporal instance on your box this step doesn't apply.
|
|
||
| Call `requestApproval` from a Workflow in the caller Namespace. The caller knows the Endpoint name and the contract, and nothing else about the handler. | ||
|
|
||
| ## Use the generated interface as a stub |
There was a problem hiding this comment.
I'm struggling to follow this section. Needs more examples, grounding to reference code.
| | Operation | Message type | Why this type | | ||
| | --- | --- | --- | | ||
| | `remindApprover` | Signal | Fire-and-forget. The caller does not need a response, only for the nudge to happen. | | ||
| | `getApprovalStatus` | Query | Reads state without changing it. Never blocks, never writes. | |
There was a problem hiding this comment.
I see what we are trying to do with putting query in here, but our docs shouldn't be referencing unreleased stuff.
|
|
||
| ::: | ||
|
|
||
| ## Keep the responsibilities separate |
There was a problem hiding this comment.
The entire wording of this section is hard to follow.
- I don't like just saying mixing these up is a common mistake.
- It is better to just explain the negative behavior. Query is better for when. you are trying to get state without updating it, and it can't be relied on to get the state after retention period expires,'
- But this gives no explanation about how to misuse signals or update...
I think this section it really just to discuss DON'T Use Query for getting the final workflow status...
|
|
||
| `{sample code will be here}` | ||
|
|
||
| :::caution Update-with-Start is not yet available |
There was a problem hiding this comment.
Remove this. It's not included in the Temporal Operation Handler yet, so we should not be discussing it.
|
|
||
| A caller that did not start an approval cannot ask Nexus for its final decision. | ||
|
|
||
| `requestApproval` delivers the decision to whoever awaited it. There is no Operation that attaches to an already-running approval and waits for its result, because Get Workflow Result as an async backing is [not yet available](/nexus/sdk-v2) in any SDK. |
There was a problem hiding this comment.
Remove " because Get Workflow Result as an async backing is not yet available in any SDK." (From discussions with product we have not decided to add this. We shouldn't theorize about potential future features in the docs, we should explain what is there.)
Just change this focus to be only on no way to attach to already running approval, so instead use this pattern...
|
|
||
| Use `TemporalOperationHandler` as with every other Operation, but call `startActivity` on the Nexus-aware Client instead of `startWorkflow`. The Operation starts an Activity Execution with no parent Workflow and completes when the Activity returns. | ||
|
|
||
| Before Activity-backed Operations, this Operation would have needed a Workflow whose only job was to call this one Activity — a wrapper with its own Event History and Workflow Id, providing nothing. |
There was a problem hiding this comment.
Remove this. We should not be explaining this feature in terms of how it used to be done. Assume the caller has not idea about how it used to be done, we should just explain why it is a good pattern and maybe other things it could be used for.
| - **An Activity Id**, unique within the Namespace. | ||
| - **A Task Queue.** It does not have to be the Endpoint's target Task Queue, so notifications can run on their own Worker fleet. | ||
|
|
||
| Derive the Activity Id from the Nexus request Id to make the start idempotent. The server retries a Nexus start request with the same request Id, so each retry targets the same Activity Id instead of sending a second notification. |
There was a problem hiding this comment.
This explanation could be improved. Lead with the outcome. In order to ensure that server retries are idempotent and don't spam notifications to the user, it is important to do this pattern.....
(Also consider mentioning a few other cases where this pattern might be helpful)
|
|
||
| `{sample code will be here}` | ||
|
|
||
| ## Cancellation needs heartbeating |
There was a problem hiding this comment.
This is good information, but not useful to the guide. The guide should stay a bit tighter to How-to-Guide, move this content to Reference.
|
|
||
| Open the caller Workflow in the UI and follow the links. Because the handlers used the Nexus-aware Client, each Operation is connected to the Execution it started or messaged in the handler Namespace, and you can move between the two Namespaces in one view. | ||
|
|
||
| The exception is `getApprovalStatus`, which is [not implementable in the pre-release](/nexus/sdk-v2) because Query is not yet available as sync messaging. |
There was a problem hiding this comment.
Just remove this until we are ready to add query
|
Looks great! When it's ready for review, feel free to drop a comment and post it in the slack channel then someone from the team will review it! |
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!!