@togatherlabs/event-sdk
v1.0.70
Published
Shared Protobuf event schemas and generated code for toGather microservices
Keywords
Readme
togather-shared-events
This repository contains event schemas for the toGather event-driven backend system. We use Protocol Buffers (Protobuf) as the messaging format for events. It provides type-safe code generation for TypeScript and Python, enabling services in different languages to share a consistent event schema.
Table of Contents
Directory Structure
Protobuf files should follow a domain/version-based structure:
proto/
└── <domain>/
└── <version>/
└── <event>.proto- Domain: The feature or module (e.g.,
user) - Version: The event schema version (e.g.,
v1)
Example:
proto/user/v1/UserCreated.protoThis structure ensures forward-compatibility and makes schema evolution manageable.
SDK Development
Adding New Proto Files
- Place your
.protofile in the appropriateproto/<domain>/<version>folder. - Define the
packagein the.protofile to match the folder structure.
Example for user.create.v1:
syntax = "proto3";
package user.create.v1;
message UserCreated {
string id = 1;
string name = 2;
string email = 3;
int64 created_at = 4;
}Linting
Lint your proto files to ensure consistency and adherence to standards:
pnpm buf:lintThis checks for things like missing packages, incorrect directory structure, and other style issues.
Code Generation
Generate TypeScript and Python code from your proto files:
pnpm buf:generateThe generated code is placed in the gen/ directory:
gen/ts/...
gen/python/...Tip: Keep
buf.gen.yamlupdated if you add new languages or plugins.
Publishing Package
To publish the package, run the following command:
pnpm run releaseThis command performs the following steps:
- Deletes the old build artifacts.
- Lints the proto files.
- Generates new code for TypeScript and Python.
- Patches the npm version.
- Publishes the package to the registry.
Note: Ensure all changes are committed and tested before running this command.
Using Generated Code
TypeScript
Dependencies:
pnpm add @bufbuild/protobuf@^2.9.0Example usage:
import {UserCreated,UserCreatedSchema} from '@togatherlabs/event-sdk/user/v1'
import { create, toBinary, fromBinary, toJson } from '@bufbuild/protobuf';
// Create a new message
const newUser: UserCreated = create(UserCreatedSchema, {
id: "1",
name: "Abhiram",
email: "[email protected]",
createdAt: BigInt(Date.now()),
});
// Serialize to binary
const buffer = toBinary(UserCreatedSchema, newUser);
// Deserialize from binary
const decoded = fromBinary(UserCreatedSchema, buffer);
// Convert to JSON
const json = toJson(UserCreatedSchema, decoded);Tip: Use
toBinary/fromBinaryfor Kafka or network transmission. UsetoJson/fromJsonfor logging or debugging.
Python
Install dependencies (for usage):
pip install protobuf togather-event-sdk Dependencies (for maintainers / publishing):
pip install build twineExample usage:
from togather_event_sdk.user.v1 import UserCreated
# Create a new message
msg = UserCreated(
id="1",
name="Abhiram",
email="[email protected]",
created_at=int(1728512345123),
)
# Serialize to bytes
binary_data = msg.SerializeToString()
# Deserialize from bytes
decoded = UserCreated()
decoded.ParseFromString(binary_data)
print(decoded)Build & Publish Python SDK (for maintainers):
pnpm python:buildThis runs:
- buf generate
- Adds init.py files
- Builds the Python wheel + source tarball
Tip: Python type hints can be added with
mypy-protobufor community plugins for full static type checking.
Best Practices
Package and folder alignment
packagein.protomust match folder structure (proto/<domain>/<version>).
Versioning
- Always increment the version folder (
v1,v2, etc.) when changing message formats to avoid breaking consumers.
- Always increment the version folder (
Type safety
- Use the generated schemas rather than manually constructing messages.
- In TypeScript, always use
create(); in Python, use the generatedUserCreatedclass.
Schema evolution
- Avoid renaming or deleting existing fields; mark them deprecated instead.
- Use
int64for timestamps,stringfor IDs/emails, etc.
How to Type a Event
A Event will have many fields like
message DomainEventExample {
/// event name (e.g. "user.account.created")
string event_name = 1;
/// Schema version (e.g."v1"), reciving systems can use this to deserialize the payload accordingly with version
string event_version = 2;
/// The entity or aggregate this event belongs to (for partitioning)
string aggregate_id = 3;
/// Binary-encoded event data (the inner protobuf message),will be typed check events for specific event
bytes payload = 4;
/// Epoch timestamp in milliseconds when the event was created,indicate when this action was recorded by the system
int64 timestamp = 5;
}How should you type a event
syntax = "proto3";
package admin.v1;
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions {
string fixed_value = 50001;
}
message Payload {
string id = 10; // Account ID
string email = 11; // Account email
string name = 12; // Account name
int64 created_at = 13; // Account creation timestamp
}
message AdminAccountCreated {
// Common metadata fields (in every event)
// "admin.account.created"
string event_name = 1 [(fixed_value) = "admin.account.created"];
string event_version = 2; // "v1"
string aggregate_id = 3; // Account ID for partitioning
int64 timestamp = 4; // When event was created
string trace_id = 5; // For distributed tracing
Payload payload = 9; // Event-specific data
}
check the docs to see why this decistion was taken: docs
Troubleshooting
ModuleNotFoundError in Python
- Ensure
gen/folder is in yourPYTHONPATH. - Add
__init__.pyfiles in each subfolder if needed.
- Ensure
TypeScript errors with missing
$typeNameorUserCreated- Make sure you are importing the schema (
UserCreatedSchema) and usingcreate()instead of instantiating the type directly.
- Make sure you are importing the schema (
Buf generation issues
- Run
pnpm buf:lintfirst to catch package/directory mismatches.
- Run
