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
npx -y skills add kora-projects/kora-skills --skill kora-kafka-producerAssembled 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
@Jsonor@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:
| Signature | Behavior |
|---|---|
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:
@Jsonon a parameter (and on its DTO) serializes that value as JSON via thejson-module.@Tag(SomeTag.class)on a parameter selects a customSerializer<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);
}
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
| Document | Description |
|---|---|
| Producer Reference | Full @KafkaPublisher API, signatures, config keys |
| Serialization Reference | @Json, @Tag, custom Serializer<T> components |
| Transactions Reference | TransactionalPublisher, inTx, manual begin() |
| Error Handling Reference | KafkaPublishException, SerializationException, async errors |
Common pitfalls
| Symptom | Fix |
|---|---|
Required dependency not found for the publisher | Add KafkaModule to @KoraApp, and annotation-processors (KSP for Kotlin) |
No Serializer found for a parameter | Add @Json (and json-module) or bind a @Tag Serializer<T> component |
Method without @Topic fails to generate | A non-@Topic method must take a ProducerRecord |
KafkaPublishException on every send | Check bootstrap.servers and broker reachability; inspect getCause() |
| Topic config not picked up | The @Topic path must match a config section under kafka.producer |
| Transaction never aborts | Let 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.
| Template | Description |
|---|---|
MessagePublisher.java.template | Publisher with every send signature |
JsonMessagePublisher.java.template | @Json publisher with DTO |
TransactionalPublisher.java.template | Base + transactional publisher |
application.conf.template | HOCON producer config |
build.gradle.template / build.gradle.kts.template | Gradle dependencies |
What ships with it: 26 files
75.4 KB alongside SKILL.md, 3 of them executable
assets/
- AdvancedMessagePublisher.java.template1.1 KB
- application.conf.template1.2 KB
- Application.java.template567 B
- Application.kt.template592 B
- AsyncMessagePublisher.java.template1.4 KB
- build.gradle.kts.template1.5 KB
- build.gradle.template1.2 KB
- JsonMessagePublisher.java.template907 B
- JsonMessagePublisher.kt.template917 B
- KotlinTransactionalPublisher.kt.template785 B
- MessagePublisher.java.template1.4 KB
- MessagePublisher.kt.template1.4 KB
- MessagePublisherTests.java.template5.7 KB
- MessagePublisherTests.kt.template5.2 KB
- MessageSender.java.template1.4 KB
- OrderEventPublisher.java.template983 B
- TransactionalPublisher.java.template1.9 KB
- TransactionalPublisher.kt.template1.7 KB
evals/
- evals.json5.9 KB
references/
scripts/
- generate_producer.pyruns13.8 KB
- run_evals.pyruns1.5 KB
- validate_config.pyruns5.2 KB