How-To: Use output bindings to interface with external resources

Invoke external systems with output bindings

Output bindings enable you to invoke external resources without taking dependencies on special SDK or libraries. For a complete sample showing output bindings, visit this link.

Example:

The below code example loosely describes an application that processes orders. In the example, there is an order processing service which has a Dapr sidecar. The order processing service uses Dapr to invoke external resources, in this case a Kafka, via an output binding.

Diagram showing bindings of example service

1. Create a binding

An output binding represents a resource that Dapr uses to invoke and send messages to.

For the purpose of this guide, you’ll use a Kafka binding. You can find a list of the different binding specs here.

Create a new binding component with the name of checkout.

Inside the metadata section, configure Kafka related properties such as the topic to publish the message to and the broker.

Create the following YAML file, named binding.yaml, and save this to a components sub-folder in your application directory. (Use the --components-path flag with dapr run to point to your custom components dir)

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: checkout
  5. spec:
  6. type: bindings.kafka
  7. version: v1
  8. metadata:
  9. # Kafka broker connection setting
  10. - name: brokers
  11. value: localhost:9092
  12. # consumer configuration: topic and consumer group
  13. - name: topics
  14. value: sample
  15. - name: consumerGroup
  16. value: group1
  17. # publisher configuration: topic
  18. - name: publishTopic
  19. value: sample
  20. - name: authRequired
  21. value: "false"

To deploy this into a Kubernetes cluster, fill in the metadata connection details of your desired binding component in the yaml below (in this case kafka), save as binding.yaml, and run kubectl apply -f binding.yaml.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: checkout
  5. spec:
  6. type: bindings.kafka
  7. version: v1
  8. metadata:
  9. # Kafka broker connection setting
  10. - name: brokers
  11. value: localhost:9092
  12. # consumer configuration: topic and consumer group
  13. - name: topics
  14. value: sample
  15. - name: consumerGroup
  16. value: group1
  17. # publisher configuration: topic
  18. - name: publishTopic
  19. value: sample
  20. - name: authRequired
  21. value: "false"

2. Send an event (Output binding)

Below are code examples that leverage Dapr SDKs to interact with an output binding.

  1. //dependencies
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Net.Http;
  5. using System.Net.Http.Headers;
  6. using System.Threading.Tasks;
  7. using Dapr.Client;
  8. using Microsoft.AspNetCore.Mvc;
  9. using System.Threading;
  10. //code
  11. namespace EventService
  12. {
  13. class Program
  14. {
  15. static async Task Main(string[] args)
  16. {
  17. string BINDING_NAME = "checkout";
  18. string BINDING_OPERATION = "create";
  19. while(true) {
  20. System.Threading.Thread.Sleep(5000);
  21. Random random = new Random();
  22. int orderId = random.Next(1,1000);
  23. using var client = new DaprClientBuilder().Build();
  24. //Using Dapr SDK to invoke output binding
  25. await client.InvokeBindingAsync(BINDING_NAME, BINDING_OPERATION, orderId);
  26. Console.WriteLine("Sending message: " + orderId);
  27. }
  28. }
  29. }
  30. }

Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application:

  1. dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 --app-ssl dotnet run
  1. //dependencies
  2. import io.dapr.client.DaprClient;
  3. import io.dapr.client.DaprClientBuilder;
  4. import io.dapr.client.domain.HttpExtension;
  5. import org.springframework.boot.autoconfigure.SpringBootApplication;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import java.util.Random;
  9. import java.util.concurrent.TimeUnit;
  10. //code
  11. @SpringBootApplication
  12. public class OrderProcessingServiceApplication {
  13. private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class);
  14. public static void main(String[] args) throws InterruptedException{
  15. String BINDING_NAME = "checkout";
  16. String BINDING_OPERATION = "create";
  17. while(true) {
  18. TimeUnit.MILLISECONDS.sleep(5000);
  19. Random random = new Random();
  20. int orderId = random.nextInt(1000-1) + 1;
  21. DaprClient client = new DaprClientBuilder().build();
  22. //Using Dapr SDK to invoke output binding
  23. client.invokeBinding(BINDING_NAME, BINDING_OPERATION, orderId).block();
  24. log.info("Sending message: " + orderId);
  25. }
  26. }
  27. }

Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application:

  1. dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run
  1. #dependencies
  2. import random
  3. from time import sleep
  4. import requests
  5. import logging
  6. import json
  7. from dapr.clients import DaprClient
  8. #code
  9. logging.basicConfig(level = logging.INFO)
  10. BINDING_NAME = 'checkout'
  11. BINDING_OPERATION = 'create'
  12. while True:
  13. sleep(random.randrange(50, 5000) / 1000)
  14. orderId = random.randint(1, 1000)
  15. with DaprClient() as client:
  16. #Using Dapr SDK to invoke output binding
  17. resp = client.invoke_binding(BINDING_NAME, BINDING_OPERATION, json.dumps(orderId))
  18. logging.basicConfig(level = logging.INFO)
  19. logging.info('Sending message: ' + str(orderId))

Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application:

  1. dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --app-protocol grpc python3 OrderProcessingService.py
  1. //dependencies
  2. import (
  3. "context"
  4. "log"
  5. "math/rand"
  6. "time"
  7. "strconv"
  8. dapr "github.com/dapr/go-sdk/client"
  9. )
  10. //code
  11. func main() {
  12. BINDING_NAME := "checkout";
  13. BINDING_OPERATION := "create";
  14. for i := 0; i < 10; i++ {
  15. time.Sleep(5000)
  16. orderId := rand.Intn(1000-1) + 1
  17. client, err := dapr.NewClient()
  18. if err != nil {
  19. panic(err)
  20. }
  21. defer client.Close()
  22. ctx := context.Background()
  23. //Using Dapr SDK to invoke output binding
  24. in := &dapr.InvokeBindingRequest{ Name: BINDING_NAME, Operation: BINDING_OPERATION , Data: []byte(strconv.Itoa(orderId))}
  25. err = client.InvokeOutputBinding(ctx, in)
  26. log.Println("Sending message: " + strconv.Itoa(orderId))
  27. }
  28. }

Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application:

  1. dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 go run OrderProcessingService.go
  1. //dependencies
  2. import { DaprServer, DaprClient, CommunicationProtocolEnum } from 'dapr-client';
  3. //code
  4. const daprHost = "127.0.0.1";
  5. var main = function() {
  6. for(var i=0;i<10;i++) {
  7. sleep(5000);
  8. var orderId = Math.floor(Math.random() * (1000 - 1) + 1);
  9. start(orderId).catch((e) => {
  10. console.error(e);
  11. process.exit(1);
  12. });
  13. }
  14. }
  15. async function start(orderId) {
  16. const BINDING_NAME = "checkout";
  17. const BINDING_OPERATION = "create";
  18. const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP);
  19. //Using Dapr SDK to invoke output binding
  20. const result = await client.binding.send(BINDING_NAME, BINDING_OPERATION, { orderId: orderId });
  21. console.log("Sending message: " + orderId);
  22. }
  23. function sleep(ms) {
  24. return new Promise(resolve => setTimeout(resolve, ms));
  25. }
  26. main();

Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application:

  1. dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start

All that’s left now is to invoke the output bindings endpoint on a running Dapr instance.

You can also invoke the output bindings endpoint using HTTP:

  1. curl -X POST -H 'Content-Type: application/json' http://localhost:3601/v1.0/bindings/checkout -d '{ "data": { "orderId": "100" }, "operation": "create" }'

As seen above, you invoked the /binding endpoint with the name of the binding to invoke, in our case its checkout. The payload goes inside the mandatory data field, and can be any JSON serializable value.

You’ll also notice that there’s an operation field that tells the binding what you need it to do. You can check here which operations are supported for every output binding.

Watch this video on how to use bi-directional output bindings.

References

Last modified February 18, 2022: Update setup-jetstream.md (#2200) (428d8c2)