agentsclimarketplace

Kora kafka producer

Skill kora-projects/kora-skills/plugins/kora-v1/skills/kora-kafka-producer

Kafka message production in Kora via the @KafkaPublisher annotation on an interface. Covers @KafkaPublisher.Topic typed contracts, send signatures (void, RecordMetadata, Future/CompletionStage, ProducerRecord, Callback), @Json and @Tag serializer selection, KafkaPublishException handling, and transactional sends with TransactionalPublisher. Use when declaring a Kafka producer, publishing domain events, configuring kafka.producer.* driverProperties, or wiring a transactional Kafka publisher. Requires the ru.tinkoff.kora:kafka module, KafkaModule on @KoraApp, and the annotation-processors (Java) or symbol-processors (Kotlin).From its SKILL.md

Install
npx -y skills add kora-projects/kora-skills --skill kora-kafka-producer

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

8.7 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it

Kora Kafka Producer

Kora generates a Kafka Producer implementation at compile time from an interface annotated with @KafkaPublisher. You declare the contract; the annotation processor wires the KafkaProducer, serializers, and telemetry.

Read this first when:

  • declaring a typed publisher with @KafkaPublisher,
  • choosing a send signature (sync, RecordMetadata, async, ProducerRecord),
  • selecting serializers with @Json or @Tag,
  • sending atomically with TransactionalPublisher.

For Kafka consumers (@KafkaListener), use the kora-kafka-consumer skill.


Quick Start

1. Dependencies

dependencies {
    koraBom platform("ru.tinkoff.kora:kora-parent:1.2.17")
    annotationProcessor "ru.tinkoff.kora:annotation-processors" // KSP for Kotlin: ksp "ru.tinkoff.kora:symbol-processors"

    implementation "ru.tinkoff.kora:kafka"
    implementation "ru.tinkoff.kora:json-module" // only if you publish @Json payloads
}

All ru.tinkoff.kora:* artifacts inherit their version from the kora-parent BOM — never pin them individually.

2. Enable the module

@KoraApp
public interface Application extends
        HoconConfigModule,
        LogbackModule,
        JsonModule,
        KafkaModule {

    static void main(String[] args) {
        KoraApplication.run(ApplicationGraph::graph);
    }
}

3. Declare a publisher

import ru.tinkoff.kora.kafka.common.annotation.KafkaPublisher;
import ru.tinkoff.kora.kafka.common.annotation.KafkaPublisher.Topic;

@KafkaPublisher("kafka.producer.userEvents")
public interface UserEventPublisher {

    @Topic("kafka.producer.userEventsTopic")
    void send(@Json UserEvent event);
}

@KafkaPublisher points to the producer config section; @Topic (the nested @KafkaPublisher.Topic) points to a topic config section.

4. Define the event

@Json
public record UserEvent(String userId, String eventType, LocalDateTime timestamp) {}

5. Inject and publish

@Component
public final class UserService {

    private final UserEventPublisher publisher;

    public UserService(UserEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void createUser(String userId) {
        publisher.send(new UserEvent(userId, "CREATED", LocalDateTime.now()));
    }
}

Use constructor injection — Kora has no field injection.

6. Configure

kafka {
  producer {
    userEvents {
      driverProperties {
        "bootstrap.servers": ${KAFKA_BOOTSTRAP}  # required
        "acks": "all"
      }
      telemetry.logging.enabled = true
    }
    userEventsTopic {
      topic = "user-events"  # required
      # partition = 0        # optional
    }
  }
}

The topic config section is a sibling of the producer section under kafka.producer, named to match the @Topic path. Serializers for @Json parameters are injected by Kora — do not set key.serializer / value.serializer in driverProperties for those parameters.


Send signatures

K is the key type, V the value type. With @Topic, value is required; key and Headers are optional. Available return types:

SignatureBehavior
void send(V value)Fire-and-forget; throws KafkaPublishException on failure
void send(K key, V value)With key
void send(K key, V value, Headers headers)With headers
RecordMetadata send(V value)Blocking; returns broker metadata
Future<RecordMetadata> send(V value)Async via Future
CompletionStage<RecordMetadata> send(V value)Async via CompletionStage
void send(ProducerRecord<K, V> record)Full control over the record
void send(ProducerRecord<K, V> record, Callback callback)Record + Kafka Callback

In Kotlin a suspend fun send(value: V): RecordMetadata and Deferred<RecordMetadata> are also supported.

A method WITHOUT @Topic must take a ProducerRecord (the topic comes from the record). A method WITH @Topic takes unpacked value/key/headers.

See Producer Reference for full detail.


Serialization

Kora chooses a Serializer<T> for each key/value from the application graph:

  • @Json on a parameter (and on its DTO) serializes that value as JSON via the json-module.
  • @Tag(SomeTag.class) on a parameter selects a custom Serializer<T> component bound under that tag.
  • Otherwise the matching Serializer<T> component is used (Kora provides the standard String/byte/numeric serializers).
@KafkaPublisher("kafka.producer.myPublisher")
public interface MyPublisher {

    @Topic("kafka.producer.myTopic")
    void send(String key, @Json MyEvent value);

    // custom serializer selected by tag
    void send(ProducerRecord<String, @Tag(MyEvent.class) MyEvent> record);
}

See Serialization Reference.


Transactional publishing

To send several records atomically (all or nothing), declare a regular publisher, then a second @KafkaPublisher interface that extends TransactionalPublisher<P>:

import ru.tinkoff.kora.kafka.common.producer.TransactionalPublisher;

@KafkaPublisher("kafka.producer.myPublisher")
public interface MyPublisher {
    @Topic("kafka.producer.myTopic")
    void send(String key, String value);
}

@KafkaPublisher("kafka.producer.myTransactional")
public interface MyTransactionalPublisher extends TransactionalPublisher<MyPublisher> {
}

Send inside inTx — on success the transaction commits, on any thrown exception it aborts:

transactionalPublisher.inTx(producer -> {
    producer.send("key1", "value1");
    producer.send("key2", "value2");
});

Transaction config lives under the transactional publisher section:

kafka {
  producer {
    myTransactional {
      idPrefix = "order-service-"  # transactional.id prefix
      maxPoolSize = 10             # transaction connection pool size
      maxWaitTime = "10s"          # max transaction wait time
    }
  }
}

Kafka transactions cover Kafka only. They do NOT make a database write and a Kafka send atomic together — for that use the transactional outbox pattern. See Transactions Reference.


References

DocumentDescription
Producer ReferenceFull @KafkaPublisher API, signatures, config keys
Serialization Reference@Json, @Tag, custom Serializer<T> components
Transactions ReferenceTransactionalPublisher, inTx, manual begin()
Error Handling ReferenceKafkaPublishException, SerializationException, async errors

Common pitfalls

SymptomFix
Required dependency not found for the publisherAdd KafkaModule to @KoraApp, and annotation-processors (KSP for Kotlin)
No Serializer found for a parameterAdd @Json (and json-module) or bind a @Tag Serializer<T> component
Method without @Topic fails to generateA non-@Topic method must take a ProducerRecord
KafkaPublishException on every sendCheck bootstrap.servers and broker reachability; inspect getCause()
Topic config not picked upThe @Topic path must match a config section under kafka.producer
Transaction never abortsLet the exception propagate out of the inTx lambda; do not swallow it

Assets

Templates and config live in assets/. See assets/README.md for the full list and usage.

TemplateDescription
MessagePublisher.java.templatePublisher with every send signature
JsonMessagePublisher.java.template@Json publisher with DTO
TransactionalPublisher.java.templateBase + transactional publisher
application.conf.templateHOCON producer config
build.gradle.template / build.gradle.kts.templateGradle dependencies

What ships with it: 26 files

75.4 KB alongside SKILL.md, 3 of them executable

evals/

scripts/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.