powerful APIs for
building microservices

stop hacking together fragmented libraries and
get back to building

Start Now For Free
ignore the fluff
focus on business logic
avoid lock-in
swap infra in minutes
fully managed
open-source dapr

eliminate repetitive code with APIs that
seamlessly connect to your existing infrastructure

workflow

Orchestrate business transactions

Durable, long-running executions

Fault-tolerant and automatic recovery of state

Scheduled reminders and external events

publish/subscribe

At-least-once guaranteed message delivery

Automatic CloudEvents support

Time-to-live message expiry

Bulk message delivery

service invocation

Cross-cloud, cross  region, cross-compute

Zero trust
communication

Service registry
& discovery

In-flight message
transformation

state management

Multi-tenancy and data isolation

Concurrency control (first write wins, last write wins)

Consistency control
(strong/eventual)

Data encryption at rest

bindings

Triggers from external systems and APIs

Invoke external systems and APIs

Observable 3rd party interactions

Authenticate calls to/from external systems

what are developers saying?

Compared with building the same app using various stacks, Catalyst just gave me an easier way to do it: minimal moving parts, isolation and decoupling which I adore
Rosius Ndimofor Ateh

AWS Serverless Hero

See More

Catalyst APIs saved time setting up component definitions, kept my solution portable, and the visibility of state, call graph & API logs meant I didn't have to write any additional tooling for debug
Whit Waldo

CEO. Innovian

See More

Catalyst APIs stand out among other API-driven solutions; backed by the Dapr CNCF project, software engineers can rely on it without worrying about the heavy lifting core logic
Sardor

Software Engineering Mgr, Epam Systems

See More

Compared with building the same app using various stacks, Catalyst just gave me an easier way to do it: minimal moving parts, isolation and decoupling which I adore
Rosius Ndimofor Ateh

AWS Serverless Hero

See More

Catalyst APIs saved time setting up component definitions, kept my solution portable, and the visibility of state, call graph & API logs meant I didn't have to write any additional tooling for debug
Whit Waldo

CEO. Innovian

See More

Catalyst APIs stand out among other API-driven solutions; backed by the Dapr CNCF project, software engineers can rely on it without worrying about the heavy lifting core logic
Sardor

Software Engineering Mgr, Epam Systems

See More

APIs to build web-scale applications in days

Author workflows in code to automate complex business processes that are stateful, durable, and long-running.

  • Automatic handling of failures and errors

  • Support for task chaining, fan-out/fan-in, monitor and external system interaction

  • Workflow state can be stored in your database

create workflow
public class MoneyTransferWorkflow : Workflow<MoneyTransferParams, bool>
{
    public override async Task<bool> RunAsync(WorkflowContext context, MoneyTransferParams input)
    {
        // withdraw money from account
        await context.CallActivityAsync(nameof(WithdrawActivity), new WithdrawParams(input.fromAccount, input.amount));
        try
        {
            // deposit money in target account
            await context.CallActivityAsync(nameof(DepositActivity), new DepositParams(input.toAccount, input.amount));
        }
        catch (System.Exception)
        {
            // in case of error deposit back the money in the original account
            await context.CallActivityAsync(nameof(DepositActivity), new DepositParams(input.fromAccount, input.amount));
        }
        return true;
    }
}
def money_transfer_workflow(ctx: DaprWorkflowContext, input: MoneyTransferParams):
    # withdraw money from account
    yield ctx.call_activity(withdraw, input=WithdrawParams(input.fromAccount, input.amount))
    try:
        # deposit money in target account
        yield ctx.call_activity(deposit, input=DepositParams(input.toAccount, input.amount))
    except Exception as e:
        # in case of error deposit back the money in the original account
        yield ctx.call_activity(deposit, input=DepositParams(input.fromAccount, input.amount))
const moneyTransferWorkflow = async function*(ctx, input) {
    // withdraw money from account
    yield ctx.callActivity(withdrawActivity, { account: input.fromAccount, amount: input.amount });
    try {
        // deposit money in target account
        yield ctx.callActivity(depositActivity, { account: input.toAccount, amount: input.amount });
    } catch (error) {
        // in case of error deposit back the money in the original account
        yield ctx.callActivity(depositActivity, { account: input.fromAccount, amount: input.amount });
    }
}
 
 	
public class MoneyTransferWorkflow extends Workflow {
  @Override
  public WorkflowStub create() {
    return ctx -> {
      MoneyTransferParams input = ctx.getInput(MoneyTransferParams.class);

      // withdraw money from account
      ctx.callActivity(WithdrawActivity.class.getName(), new WithdrawParams(input.fromAccount, input.amount)).await();
      try {
        // deposit money in target account
        ctx.callActivity(DepositActivity.class.getName(), new DepositParams(input.toAccount, input.amount)).await();
      } catch (Exception e) {
        // in case of error deposit back the money in the original account
        ctx.callActivity(DepositActivity.class.getName(), new DepositParams(input.fromAccount, input.amount)).await();
      }
    };
  }
}
func MoneyTransferWorkflow(ctx *workflow.WorkflowContext) (any, error) {
        var input MoneyTransferParams
        if err := ctx.GetInput(&input); err != nil {
                return nil, err
        }

        // withdraw money from account
        if err := ctx.CallActivity(Withdraw, workflow.ActivityInput(WithdrawParams{
                Account: input.FromAccount,
                Amount:  input.Amount,
        })).Await(nil); err != nil {
                return nil, err
        }

        // deposit money in target account
        if err := ctx.CallActivity(Deposit, workflow.ActivityInput(DepositParams{
                Account: input.ToAccount,
                Amount:  input.Amount,
        })).Await(nil); err != nil {
                // in case of error deposit back the money in the original account
                return nil, ctx.CallActivity(Deposit, workflow.ActivityInput(DepositParams{
                        Account: input.FromAccount,
                        Amount:  input.Amount,
                })).Await(nil)
        }
        return nil, nil
}

Create event-driven apps that scale to millions of events/sec. Integrates with dozens of message brokers.

  • Configurable push/pull eventing

  • At-least-once guaranteed message delivery

  • Automatic CloudEvents support

  • Bulk message delivery

publish message
using Dapr.Client;

var client = new DaprClientBuilder().Build();

// Publish message
await client.PublishEventAsync(“my-pubsub”, "my-topic", “my-message”);
from dapr.clients import DaprClient;

with DaprClient() as d:
  result = d.publish_event(
    pubsub_name = "my-pubsub",
    topic_name = 'orders',
    data = `{ "orderId": 1 }`,
    data_content_type = 'application/json')
import { DaprClient, CommunicationProtocolEnum } from "@dapr/dapr";

(async () => {
  const client = new DaprClient({ communicationProtocol: CommunicationProtocolEnum.HTTP });

  await client.pubsub.publish("my-pubsub", "orders", {"orderId":"1"});
})()
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.client.domain.Metadata;

import static java.util.Collections.singletonMap;

DaprClient client = new DaprClientBuilder().build()
client.publishEvent("pubsub", "orders", "{\"orderId\":\"1\"}", singletonMap(Metadata.TTL_IN_SECONDS, "600")).block();
package main

import (
        "context"
        "fmt"

        dapr "github.com/dapr/go-sdk/client"
)

func main() {
        client, err := dapr.NewClient()
        if err != nil {
                panic(fmt.Errorf("error creating connection to catalyst: %w", err))
        }
        defer client.Close()

        if err := client.PublishEvent(context.TODO(), "pubsub", "{topic-name}",
                []byte(`{"key":"value"}`)); err != nil {
                panic(fmt.Errorf("error publishing event: %w", err))
        }
}

Connect apps running on any cloud platform and across any region.

  • Zero networking configuration

  • Service discovery

  • End-to-end mTLS & authorization

connect app
using Dapr.Client;

var client = new DaprClientBuilder().Build();
var httpClient = DaprClient.CreateInvokeHttpClient(“my-target-app”);
httpClient.DefaultRequestHeaders.Add("dapr-api-token", “my-api-token”);

var response = await httpClient.PostAsync("/target-method", "{\"data\":\"my-data\"}");
from dapr.clients import DaprClient;

headers = { 'dapr-app-id': ‘my-target-app’, 'content-type': 'application/json'}
result = requests.post(
  url = '/orders',
  data =‘{
\"data\":\"my-data\"}’,
        headers = headers)
import axios from "axios";

async function main() {
  // Adding app id as part of the header
  let axiosConfig = {
    headers: {
        "dapr-app-id": "order-processor"
    }
  };
  
  const order = { orderId: 1 };
    const res = await axios.post(`/orders`, order , axiosConfig);
}

main().catch(e => console.error(e))
private static final HttpClient httpClient = HttpClient.newBuilder()
                        .version(HttpClient.Version.HTTP_2)
                        .connectTimeout(Duration.ofSeconds(10))
                        .build();

    private static final String DAPR_HTTP_PORT = System.getenv().getOrDefault("DAPR_HTTP_PORT", "3500");

    public static void main(String[] args) throws InterruptedException, IOException {
                String dapr_url = "http://localhost:" + DAPR_HTTP_PORT + "/orders";
                int orderId = 1;
                JSONObject obj = new JSONObject();
                obj.put("orderId", orderId);

                HttpRequest request = HttpRequest.newBuilder()
                                .POST(HttpRequest.BodyPublishers.ofString(obj.toString()))
                                .uri(URI.create(dapr_url))
                                .header("Content-Type", "application/json")
                                .header("dapr-app-id", "order-processor")
                                .build();

                HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());   
                System.out.println("Order passed: "+ orderId);
                TimeUnit.MILLISECONDS.sleep(1000);
    }
import (
        "context"
        "fmt"
        "net/http"

        dapr "github.com/dapr/go-sdk/client"
)

func main() {
        client, err := dapr.NewClient()
        if err != nil {
                panic(fmt.Errorf("error creating connection to catalyst: %w", err))
        }
        defer client.Close()

        order := `{"orderId":` + strconv.Itoa(i) + "}"
        req, err := http.NewRequest("POST", "/orders", strings.NewReader(order))
        if err != nil {
                panic()
        }
        req.Header.Add("dapr-app-id", "order-processor")

        response, err := client.Do(req)
        if err != nil {
                panic()
        }

Create CRUD applications with key/value state. Integrates with dozens of databases.

  • Data isolation for multi-tenancy

  • Configurable concurrency (first-write-wins, last-write-wins)

  • Configurable consistency (strong,  eventual)

  • Caching support with TTL

connect database
using Dapr.Client;

var client = new DaprClientBuilder().Build();
await client.SaveStateAsync(“my-database”, “1”, “myvalue”);
from dapr.clients import DaprClient

with DaprClient() as d:
    order = {'orderId': "1", "name": "lightsaber (real)"}
    client.save_state("my-database", "1", str(order))
import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr"

const order = { orderId: "1" }
const state = [
    {
        key: order.orderId,
        value: order,
        options: {
            consistency: "strong",
            concurrency: "first-write-wins"
        }
    }
]

const client = new DaprClient({ communicationProtocol: CommunicationProtocolEnum.HTTP });
await client.state.save("my-database", state)
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;

DaprClient client = new DaprClientBuilder().build();
               
Order order = new Order();
order.setOrderId(orderId);

client.saveState("my-database", String.valueOf(orderId), order).block();
package main

import (
        "context"
        "fmt"

        dapr "github.com/dapr/go-sdk/client"
)

func main() {
        client, err := dapr.NewClient()
        if err != nil {
                panic()
        }
        defer client.Close()

        if err := err := client.SaveState(context.Background(), "my-database, "order", []byte("value), nil); err != nil {
                panic()
        }
}

Interact with external systems via triggers and outgoing calls.

  • Integrations with dozens of third party services

  • Authenticate calls to/from external systems

Connect System
using Dapr.Client;

var client = new DaprClientBuilder().Build();
await client.InvokeBindingAsync(“my-binding”, “create”, “my-data”);
from dapr.clients import DaprClient

with DaprClient() as d:
    resp = d.invoke_binding(
      binding_name="my-binding", 
      operation="create", data="100")
import { DaprClient, CommunicationProtocolEnum } from "@dapr/dapr";

(async () => {
  const client = new DaprClient({ communicationProtocol: CommunicationProtocolEnum.HTTP });
  await client.binding.send("my-binding", "create", 100);
})()
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;

DaprClient client = new DaprClientBuilder().build()
client.invokeBinding("my-binding", "create", "100").block();
package main

import (
        "context"
        "fmt"

        dapr "github.com/dapr/go-sdk/client"
)

func main() {
        client, err := dapr.NewClient()
        if err != nil {
                panic(fmt.Errorf("error creating connection to catalyst: %w", err))
        }
        defer client.Close()

        err = client.InvokeOutputBinding(context.TODO(),
                &dapr.InvokeBindingRequest{
                        Name:      "my-binding",
                        Operation: "create",
                        Data:      []byte(`100`),
                })
        if err != nil {
                panic(err)
        }
}
Workflow
Publish / Subscribe
Service Invocation
State Management
Bindings

Author workflows in code to automate complex business processes that are stateful, durable, and long-running.

  • Automatic handling of failures and errors

  • Support for task chaining, fan-out/fan-in, monitor and external system interaction

  • Workflow state can be stored in your database

create workflow
{
    public override async Task<OrderResult> RunAsync(WorkflowContext context, OrderPayload order)
    {
        string orderId = context.InstanceId;

        // Notify the user that an order has come through
        await context.CallActivityAsync(
            nameof(NotifyActivity),
            new Notification($"Received order {orderId} for {order.Quantity} {order.Name} at ${order.TotalCost}"));

        string requestId = context.InstanceId;

        // Determine if there is enough of the item available for purchase by checking the inventory
        InventoryResult result = await context.CallActivityAsync<InventoryResult>(
            nameof(ReserveInventoryActivity),
            new InventoryRequest(RequestId: orderId, order.Name, order.Quantity));
        
        // If there is insufficient inventory, fail and let the user know 
        if (!result.Success)
        {
            // End the workflow here since we don't have sufficient inventory
            await context.CallActivityAsync(
                nameof(NotifyActivity),
                new Notification($"Insufficient inventory for {order.Name}"));
            return new OrderResult(Processed: false);
        }

        // There is enough inventory available so the user can purchase the item(s). Process their payment
        await context.CallActivityAsync(
            nameof(ProcessPaymentActivity),
            new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));

        try
        {
            // There is enough inventory available so the user can purchase the item(s). Process their payment
            await context.CallActivityAsync(
                nameof(UpdateInventoryActivity),
                new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));                
        }
        catch (TaskFailedException)
        {
            // Let them know their payment was processed
            await context.CallActivityAsync(
                nameof(NotifyActivity),
                new Notification($"Order {orderId} Failed! You are now getting a refund"));
            return new OrderResult(Processed: false);
        }

        // Let them know their payment was processed
        await context.CallActivityAsync(
            nameof(NotifyActivity),
            new Notification($"Order {orderId} has completed!"));

        // End the workflow with a success result
        return new OrderResult(Processed: true);
    }
}
def main(self):
    item_name = "lightsaber"
    order_quantity = 100

    total_cost = int(order_quantity) * baseInventory[item_name].per_item_cost
    order = OrderPayload(item_name=item_name, quantity=int(order_quantity), total_cost=total_cost)
    print(f'Starting order workflow, purchasing {order_quantity} of {item_name}', flush=True)
    start_resp = daprClient.start_workflow(workflow_component=workflow_component,
                                            workflow_name=workflow_name,
                                            input=order)
    _id = start_resp.instance_id

    def prompt_for_approval(daprClient: DaprClient):
        daprClient.raise_workflow_event(instance_id=_id, workflow_component=workflow_component, 
                                        event_name="manager_approval", event_data={'approval': True})

    approval_seeked = False
    start_time = datetime.now()
    while True:
        time_delta = datetime.now() - start_time
        state = daprClient.get_workflow(instance_id=_id, workflow_component=workflow_component)
        if not state:
            print("Workflow not found!")  # not expected
        elif state.runtime_status == "Completed" or\
                state.runtime_status == "Failed" or\
                state.runtime_status == "Terminated":
            print(f'Workflow completed! Result: {state.runtime_status}', flush=True)
            break
        if time_delta.total_seconds() >= 10:
            state = daprClient.get_workflow(instance_id=_id, workflow_component=workflow_component)
            if total_cost > 50000 and (
                state.runtime_status != "Completed" or 
                state.runtime_status != "Failed" or
                state.runtime_status != "Terminated"
                ) and not approval_seeked:
                approval_seeked = True
                threading.Thread(target=prompt_for_approval(daprClient), daemon=True).start()
        
    print("Purchase of item is ", state.runtime_status, flush=True)

def restock_inventory(self, daprClient: DaprClient, baseInventory):
    for key, item in baseInventory.items():
        print(f'item: {item}')
        item_str = f'{{"name": "{item.item_name}", "quantity": {item.quantity},\
                        "per_item_cost": {item.per_item_cost}}}'
        daprClient.save_state(store_name, key, item_str)
export const orderProcessingWorkflow: TWorkflow = async function* (ctx: WorkflowContext, orderPayLoad: OrderPayload): any {
  const orderId = ctx.getWorkflowInstanceId();
  console.log(`Processing order ${orderId}...`);

  const orderNotification: OrderNotification = {
    message: `Received order ${orderId} for ${orderPayLoad.quantity} ${orderPayLoad.itemName} at a total cost of ${orderPayLoad.totalCost}`,
  };
  yield ctx.callActivity(notifyActivity, orderNotification);

  const inventoryRequest = new InventoryRequest(orderId, orderPayLoad.itemName, orderPayLoad.quantity);
  const inventoryResult = yield ctx.callActivity(reserveInventoryActivity, inventoryRequest);

  if (!inventoryResult.success) {
    const orderNotification: OrderNotification = {
      message: `Insufficient inventory for order ${orderId}`,
    };
    yield ctx.callActivity(notifyActivity, orderNotification);
    return;
  }

  if (orderPayLoad.totalCost > 5000) {
    const approvalResult = yield ctx.callActivity(requestApprovalActivity, orderPayLoad);
    if (!approvalResult) {
      const orderNotification: OrderNotification = {
        message: `Order ${orderId} approval denied`,
      };
      yield ctx.callActivity(notifyActivity, orderNotification);
      return;
    }
  }

  const orderPaymentRequest = new OrderPaymentRequest(orderId, orderPayLoad.itemName, orderPayLoad.totalCost, orderPayLoad.quantity);
  const paymentResult = yield ctx.callActivity(processPaymentActivity, orderPaymentRequest);

  if (!paymentResult) {
    const orderNotification: OrderNotification = {
      message: `Payment for order ${orderId} failed`,
    };
    yield ctx.callActivity(notifyActivity, orderNotification);
    return;
  }

  const updatedResult = yield ctx.callActivity(updateInventoryActivity, inventoryRequest);
  if (!updatedResult.success) {
    const orderNotification: OrderNotification = {
      message: `Failed to update inventory for order ${orderId}`,
    };
    yield ctx.callActivity(notifyActivity, orderNotification);
    return;
  }

  const orderCompletedNotification: OrderNotification = {
    message: `order ${orderId} processed successfully!`,
  };
  yield ctx.callActivity(notifyActivity, orderCompletedNotification);

  console.log(`Order ${orderId} processed successfully!`);
}
public WorkflowStub create() {
    return ctx -> {
      Logger logger = ctx.getLogger();
      String orderId = ctx.getInstanceId();
      logger.info("Starting Workflow: " + ctx.getName());
      logger.info("Instance ID(order ID): " + orderId);
      logger.info("Current Orchestration Time: " + ctx.getCurrentInstant());

      OrderPayload order = ctx.getInput(OrderPayload.class);
      logger.info("Received Order: " + order.toString());
      OrderResult orderResult = new OrderResult();
      orderResult.setProcessed(false);

      // Notify the user that an order has come through
      Notification notification = new Notification();
      notification.setMessage("Received Order: " + order.toString());
      ctx.callActivity(NotifyActivity.class.getName(), notification).await();

      // Determine if there is enough of the item available for purchase by checking
      // the inventory
      InventoryRequest inventoryRequest = new InventoryRequest();
      inventoryRequest.setRequestId(orderId);
      inventoryRequest.setItemName(order.getItemName());
      inventoryRequest.setQuantity(order.getQuantity());
      InventoryResult inventoryResult = ctx.callActivity(ReserveInventoryActivity.class.getName(),
          inventoryRequest, InventoryResult.class).await();

      // If there is insufficient inventory, fail and let the user know
      if (!inventoryResult.isSuccess()) {
        notification.setMessage("Insufficient inventory for order : " + order.getItemName());
        ctx.callActivity(NotifyActivity.class.getName(), notification).await();
        ctx.complete(orderResult);
        return;
      }

      // Require orders over a certain threshold to be approved
      if (order.getTotalCost() > 5000) {
        ApprovalResult approvalResult = ctx.callActivity(RequestApprovalActivity.class.getName(),
            order, ApprovalResult.class).await();
        if (approvalResult != ApprovalResult.Approved) {
          notification.setMessage("Order " + order.getItemName() + " was not approved.");
          ctx.callActivity(NotifyActivity.class.getName(), notification).await();
          ctx.complete(orderResult);
          return;
        }
      }

      // There is enough inventory available so the user can purchase the item(s).
      // Process their payment
      PaymentRequest paymentRequest = new PaymentRequest();
      paymentRequest.setRequestId(orderId);
      paymentRequest.setItemBeingPurchased(order.getItemName());
      paymentRequest.setQuantity(order.getQuantity());
      paymentRequest.setAmount(order.getTotalCost());
      boolean isOK = ctx.callActivity(ProcessPaymentActivity.class.getName(),
          paymentRequest, boolean.class).await();
      if (!isOK) {
        notification.setMessage("Payment failed for order : " + orderId);
        ctx.callActivity(NotifyActivity.class.getName(), notification).await();
        ctx.complete(orderResult);
        return;
      }

      inventoryResult = ctx.callActivity(UpdateInventoryActivity.class.getName(),
          inventoryRequest, InventoryResult.class).await();
      if (!inventoryResult.isSuccess()) {
        
        notification.setMessage("Order failed to update inventory! : " + orderId);
        ctx.callActivity(NotifyActivity.class.getName(), notification).await();
        ctx.complete(orderResult);
        return;
      }

      // Let user know their order was processed
      notification.setMessage("Order completed! : " + orderId);
      ctx.callActivity(NotifyActivity.class.getName(), notification).await();

      // Complete the workflow with order result is processed
      orderResult.setProcessed(true);
      ctx.complete(orderResult);
    };
}
func OrderProcessingWorkflow(ctx *workflow.WorkflowContext) (any, error) {
    orderID := ctx.InstanceID()
    var orderPayload OrderPayload
    if err := ctx.GetInput(&orderPayload); err != nil {
        return nil, err
    }
    err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{
        Message: fmt.Sprintf("Received order %s for %d %s - $%d", orderID, orderPayload.Quantity, orderPayload.ItemName, orderPayload.TotalCost),
    })).Await(nil)
    if err != nil {
        return OrderResult{Processed: false}, err
    }

    var verifyInventoryResult InventoryResult
    if err := ctx.CallActivity(VerifyInventoryActivity, workflow.ActivityInput(InventoryRequest{
        RequestID: orderID,
        ItemName:  orderPayload.ItemName,
        Quantity:  orderPayload.Quantity,
    })).Await(&verifyInventoryResult); err != nil {
            return OrderResult{Processed: false}, err
    }

    if !verifyInventoryResult.Success {
        notification := Notification{Message: fmt.Sprintf("Insufficient inventory for %s", orderPayload.ItemName)}
        err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(notification)).Await(nil)
        return OrderResult{Processed: false}, err
    }

    if orderPayload.TotalCost > 50000 {
        var approvalRequired ApprovalRequired
        if err := ctx.CallActivity(RequestApprovalActivity, workflow.ActivityInput(orderPayload)).Await(&approvalRequired); err != nil {
                return OrderResult{Processed: false}, err
        }
        if err := ctx.WaitForExternalEvent("manager_approval", time.Second*200).Await(nil); err != nil {
                return OrderResult{Processed: false}, err
        }
        // TODO: Confirm timeout flow - this will be in the form of an error.
        if approvalRequired.Approval {
                if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Payment for order %s has been approved!", orderID)})).Await(nil); err != nil {
                        log.Printf("failed to notify of a successful order: %v\n", err)
                }
        } else {
                if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Payment for order %s has been rejected!", orderID)})).Await(nil); err != nil {
                        log.Printf("failed to notify of an unsuccessful order :%v\n", err)
                }
                return OrderResult{Processed: false}, err
        }
    }
    err = ctx.CallActivity(ProcessPaymentActivity, workflow.ActivityInput(PaymentRequest{
        RequestID:          orderID,
        ItemBeingPurchased: orderPayload.ItemName,
        Amount:             orderPayload.TotalCost,
        Quantity:           orderPayload.Quantity,
    })).Await(nil)
    if err != nil {
        if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Order %s failed!", orderID)})).Await(nil); err != nil {
                log.Printf("failed to notify of a failed order: %v", err)
        }
        return OrderResult{Processed: false}, err
    }

    err = ctx.CallActivity(UpdateInventoryActivity, workflow.ActivityInput(PaymentRequest{
        RequestID:          orderID,
        ItemBeingPurchased: orderPayload.ItemName,
        Amount:             orderPayload.TotalCost,
        Quantity:           orderPayload.Quantity,
    })).Await(nil)
    if err != nil {
        if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Order %s failed!", orderID)})).Await(nil); err != nil {
                log.Printf("failed to notify of a failed order: %v", err)
        }
        return OrderResult{Processed: false}, err
    }

    if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Order %s has completed!", orderID)})).Await(nil); err != nil {
        log.Printf("failed to notify of a successful order: %v", err)
    }
    return OrderResult{Processed: true}, err
}

Create event-driven apps that scale to millions of events/sec. Integrates with dozens of message brokers.

  • Configurable push/pull eventing

  • At-least-once guaranteed message delivery

  • Automatic CloudEvents support

  • Bulk message delivery

publish message
using Dapr.Client;

var client = new DaprClientBuilder().Build();

// Publish message
await client.PublishEventAsync(“my-pubsub”, "my-topic", 
                               “my-message”);
from dapr.clients import DaprClient;

with DaprClient() as d:
  result = d.publish_event(
    pubsub_name = "my-pubsub",
    topic_name = 'orders',
    data = `{ "orderId": 1 }`,
    data_content_type = 'application/json')
import { DaprClient } from "@dapr/dapr";

(async () => {
  const client = new DaprClient();
  await client.pubsub.publish("my-pubsub", "orders", {"orderId":"1"});
})()
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.client.domain.Metadata;

import static java.util.Collections.singletonMap;

DaprClient client = new DaprClientBuilder().build()
client.publishEvent("pubsub", "orders", "{\"orderId\":\"1\"}",
                    singletonMap(Metadata.TTL_IN_SECONDS, "600")).block();
package main

import (
        "context"
        "fmt"

        dapr "github.com/dapr/go-sdk/client"
)

func main() {
        client, err := dapr.NewClient()
        if err != nil {
                panic(fmt.Errorf("error creating connection to catalyst: %w", err))
        }
        defer client.Close()

        if err := client.PublishEvent(context.TODO(), "pubsub", "{topic-name}",
                []byte(`{"key":"value"}`)); err != nil {
                panic(fmt.Errorf("error publishing event: %w", err))
        }
}

Connect apps running on any cloud platform and across any region.

  • Zero networking configuration

  • Service discovery

  • End-to-end mTLS & authorization

connect app
using Dapr.Client;

var client = new DaprClientBuilder().Build();
var httpClient = DaprClient.CreateInvokeHttpClient(“order-processor”);
httpClient.DefaultRequestHeaders.Add("dapr-api-token", “my-api-token”);

var response = await httpClient.PostAsync("/orders", "{\"data\":\"my-data\"}");
headers = { 'dapr-app-id': ‘orderprocessor’, 'content-type': 'application/json'}

result = requests.post(
  url = '/orders',
  data =‘{\"data\":\"my-data\"}’, headers = headers)
import axios from "axios";

(async () => {
  let axiosConfig = {
    headers: {
        "dapr-app-id": "order-processor"
    }
  };

  const order = { orderId: 1 };
  const res = await axios.post(`/orders`, order , axiosConfig);
})()
private static final HttpClient httpClient = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_2)
        .connectTimeout(Duration.ofSeconds(10))
        .build();

public static void main(String[] args) throws InterruptedException, IOException {
        int orderId = 1;
        JSONObject obj = new JSONObject();
        obj.put("orderId", orderId);

        HttpRequest request = HttpRequest.newBuilder()
                .POST(HttpRequest.BodyPublishers.ofString(obj.toString()))
                .uri(URI.create(dapr_url))
                .header("Content-Type", "application/json")
                .header("dapr-app-id", "order-processor")
                .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("Order passed: "+ orderId);
}
package main

import (
	"context"
	"fmt"
	"net/http"

	dapr "github.com/dapr/go-sdk/client"
)

func main() {
	client, err := dapr.NewClient()
	if err != nil {
		panic(err)
	}
	defer client.Close()

	order := `{"orderId":` + strconv.Itoa(i) + "}"
	req, err := http.NewRequest("POST", "/orders", strings.NewReader(order))
	if err != nil {
		panic()
	}
	req.Header.Add("dapr-app-id", "order-processor")

	response, err := client.Do(req)
	if err != nil {
		panic()
    }
}

Create CRUD applications with key/value state. Integrates with dozens of databases.

  • Data isolation for multi-tenancy

  • Configurable concurrency (first-write-wins, last-write-wins)

  • Configurable consistency (strong,  eventual)

  • Caching support with TTL

connect database
using Dapr.Client;

var client = new DaprClientBuilder().Build();
await client.SaveStateAsync(“my-database”, “1”, “myvalue”);
from dapr.clients import DaprClient

with DaprClient() as d:
    order = {'orderId': "1", "name": "lightsaber (real)"}
    client.save_state("my-database", "1", str(order))
import { DaprClient } from "@dapr/dapr"

const order = { orderId: "1" }
const state = [
    {
        key: order.orderId,
        value: order,
        options: {
            consistency: "strong",
            concurrency: "first-write-wins"
        }
    }
]

const client = new DaprClient();
await client.state.save("my-database", state)
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;

DaprClient client = new DaprClientBuilder().build();
               
Order order = new Order();
order.setOrderId(orderId);

client.saveState("my-database", String.valueOf(orderId), order).block();
package main

import (
        "context"
        "fmt"

        dapr "github.com/dapr/go-sdk/client"
)

func main() {
        client, err := dapr.NewClient()
        if err != nil {
                panic()
        }
        defer client.Close()

        if err := err := client.SaveState(context.Background(), 
                         "my-database, "order", []byte("value), nil); err != nil {
                panic()
        }
}

Interact with external systems via triggers and outgoing calls.

  • Integrations with dozens of third party services

  • Authenticate calls to/from external systems

Connect System
using Dapr.Client;

var client = new DaprClientBuilder().Build();
await client.InvokeBindingAsync(“my-binding”, “create”, “my-data”);
from dapr.clients import DaprClient

with DaprClient() as d:
    resp = d.invoke_binding(
      binding_name="my-binding", 
      operation="create", data="100")
import { DaprClient } from "@dapr/dapr";

(async () => {
  const client = new DaprClient();
  await client.binding.send("my-binding", "create", 100);
})()
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;

DaprClient client = new DaprClientBuilder().build()
client.invokeBinding("my-binding", "create", "100").block();
package main

import (
        "context"
        "fmt"

        dapr "github.com/dapr/go-sdk/client"
)

func main() {
        client, err := dapr.NewClient()
        if err != nil {
                panic(fmt.Errorf("error creating connection to catalyst: %w", err))
        }
        defer client.Close()

        err = client.InvokeOutputBinding(context.TODO(),
                &dapr.InvokeBindingRequest{
                        Name:      "my-binding",
                        Operation: "create",
                        Data:      []byte(`100`),
                })
        if err != nil {
                panic(err)
        }
}
your code
running anywhere
Catalyst
APIs
your existing infrastructure

proven multi-cloud portability with zero code changes

start now for free

for AWS developers:

50% less code. swap AWS services in minutes.
increased security & fault-tolerance

AWS event-driven applications

without Catalyst

infrastructure specific code

fault tolerance with retries & circuit breakers

application authentication

message level security

boilerplate code for messaging patterns

end-to-end tracing and observability

industry message standard CloudEvents

with Catalyst

infrastructure independent code

fault tolerance with retries & circuit breakers

application authentication

message level security

common messaging patterns

end-to-end tracing & observability

CloudEvents message standard

AWS synchronous applications

without Catalyst

end-to-end encryption with MTLS

fault tolerance with retries & circuit breakers

application identity & access control

infrastructure dependent service discoverability

end-to-end tracing & observability

with Catalyst

end-to-end encryption with MTLS

fault tolerance with retries & circuit breakers

application identity & access control

optional infrastructure service discovery

end-to-end tracing & observability

AWS workflow applications

without Catalyst

good luck!

with Catalyst

durable execution for fault tolerance

task chaining, fan-out/fan-in, monitor, and external event interaction

code based, supports multiple languages

combine workflow orchestration & message choreography

workflow visualizer with end-to-end tracing

bring your own database, or use ours

AWS stateful applications

without Catalyst

infrastructure specific code

fault tolerance with retries & circuit breakers

data isolation

outbox with messaging

end-to-end tracing & observability

with Catalyst

infrastructure independent code

fault tolerance with retries & circuit breakers

data isolation

transactional outbox

end-to-end tracing & observability

for Azure developers:

use the latest dapr APIs from any Azure service

without Catalyst

with Catalyst

Diagrid serverless infrastructure for pub/sub & KV store

Connect Catalyst to your existing infra, or use Diagrid’s serverless pub/sub broker & key-value store that scale without limits

Start Now For Free

100% serverless, no ops required

ready to go in under 60 seconds

access from your local developer machine

built-in rate limiting, circuit breakers and retries

accessible from within your VPC / VNET

see it in action

Start Now For Free

latest blog posts

Blog