API description

The API (Application Programming Interface) allows internal and external clients to access and use the functionality of the service. A well-designed API makes it easy for users to understand how to use the service but also influences scalability and flexibility, as it should be easily updated and extended without affecting the applications that use it. As a best practice, you should separate the structures that define the external APIs from those that define the domain state model, so each can evolve independently.

When defining the API for your Kalix services, you can follow one of two approaches: code-first or protocol-first development. You can refer to the Feature Matrix to know which SDK supports which approach.

Code-first Approach

With a code-first approach you can start coding your API right from the start but you can also start from modeling other parts of the application such as your state models - it’s up to you to decide where you start.

Java SDK example

Using the Kalix Java SDK, your services are exposed using Spring REST annotations and serialization is backed by the ubiquitous Jackson library. See example below:

@RequestMapping("/order/{orderId}") (1)
public class OrderAction extends Action {

  record CreateOrder(String orderId, List<OrderItem> items){} (2)
  record OrderItem(String productId, int quantity, float price){}

  @PostMapping (3)
  public Effect<String> createOrder(@RequestBody CreateOrder order) {
    return effects().reply("ok"); (4)
  }
}
1 Setting the base path to this Action as "/order" with an orderId as a path parameter.
2 Using records to model the structure of a CreateOrder request.
3 Exposing a handler method createOrder for POST requests on the base path.
4 Dummy implementation that does nothing other than replying with ok.

Protocol-first Approach

In the Protocol-first SDKs, Kalix requires you to use Protocol buffers to describe the contract for an API. Protocol Buffers is a language and platform agnostic, extensible description model. To create and deploy a service, you define APIs and Entity data structures in a .proto file, each API being a gRPC service.

Protocol Buffer example

Depending on the component (Views, Actions, or Entities) you choose, the options you can define can differ. For a detailed overview of all options, check out the respective guide for the component you want to implement. Next, we have an example of the definition of a Value Entity:

syntax = "proto3"; (1)

package shopping.product.api; (2)

import "kalix/annotations.proto"; (3)
import "google/api/annotations.proto";

message Order { (4)
    string orderID = 2; [(kalix.field).entity_key = true]; (5)
    repeated OrderItem items = 3;
}

message OrderItem {
    string productID = 1;
    int32 quantity = 2;
    float price = 3;
}

service OrderBackendService { (6)
    option (kalix.service) = { (7)
        type: SERVICE_TYPE_ENTITY
        component: "shopping.product.domain.ProductPopularityValueEntity"
    };

    rpc AddOrder (Order) returns (Order) { (8)
        option (google.api.http) = {
            post: "/order/{orderID}",
            body: "*"
        };
    }
}
1 Specify the version of the Protobuf syntax, Kalix uses proto3.
2 Packages prevent name clashes between protocol messages, similar to how packages work in programming languages.
3 Imports allow you to use definitions from other protobuf files. You’ll always need kalix/annotations.proto, other imports are optional. In this case google/api/annotations.proto adds the ability to expose services with HTTP endpoints.
4 Messages represent the request and response of API calls and are used to define the data structure of the data you want to persist (also known as state). These structures can be as simple or as complex as you need for your use case.
5 This annotation defines which field in your message uniquely identifies the instance of your entity.
6 A gRPC service defines how external clients can interact with your business logic.
7 This annotation is used by the Kalix codegen tools to specify what type of code needs to be generated (in this case an entity) and the fully qualified name (package name and entity name) of the message that represents the state.
8 Every service has one or more RPC methods that specify the functionality of your service. Each method will be handled by a function in your code. The google.api.http option tells Kalix that this RPC method should get a gRPC and an HTTP endpoint.

The implementation, your code, ties these .proto files together as shown in the image below:

Concepts API Design