Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
11ed3fe
new: starting architecture document
lmtr0 Feb 8, 2025
09163bd
new: wrote the hole client architecture based on the api documentation
lmtr0 Feb 8, 2025
6c96db4
Merge pull request #38 from lmtr0/feat/architecture
lmtr0 Feb 8, 2025
7bc5a87
New: added example of Extension Like client api
lmtr0 Feb 8, 2025
26e1dc4
Merge pull request #41 from lmtr0/feat/arch
lmtr0 Feb 10, 2025
bd69259
new: base client & sdk and api features
lmtr0 Feb 10, 2025
a465f00
Merge pull request #42 from lmtr0/feat/base_client
lmtr0 Feb 11, 2025
b09138c
new: implementation of the Capture endpoint
lmtr0 Feb 12, 2025
218ea40
Merge pull request #43 from lmtr0/feat/capture
lmtr0 Feb 12, 2025
7b3c44d
feat(capture): Add capture method to PostHogClient
lmtr0 Feb 12, 2025
2a25702
feat: Add support for PostHogError and improve DecideRequestBuilder
lmtr0 Feb 12, 2025
8b96a79
Merge pull request #44 from lmtr0/fea/decide
lmtr0 Feb 12, 2025
a223fca
feat: better separation between sdk and api
lmtr0 Feb 13, 2025
fa943ab
Merge pull request #45 from lmtr0/feat/sdk-client
lmtr0 Feb 13, 2025
affbe6f
feat: Implement PostHog API client with rate limit handling
lmtr0 Feb 13, 2025
b9d58b5
feat(capture): Add batch capture test and improve event actor archite…
lmtr0 Feb 13, 2025
b11f877
feat(api): Add OpenAPI-based API generation
lmtr0 Feb 21, 2025
6630d97
feat: Add PostHog service module
lmtr0 Feb 21, 2025
0b8cebf
feat(schema): update property filters and query responses
lmtr0 Feb 22, 2025
f79da55
feat(schema): remove nullable types and simplify schema
lmtr0 Feb 22, 2025
5622f0e
feat: Refactor error handling in PostHog SDK
lmtr0 Feb 22, 2025
780ad35
feat(error): add PostHogServerError and PostHogApiError types
lmtr0 Feb 22, 2025
491466c
feat(query): Improve QueryRequest with default and with_query method
lmtr0 Feb 22, 2025
5fe4a30
feat(api): use project_id from environment variable
lmtr0 Feb 22, 2025
a19bf22
feat(service): Implement batch sending and historical migration support
lmtr0 Feb 22, 2025
024c73a
feat(service): Add error_count field to PostHogServiceActor
lmtr0 Feb 23, 2025
9ccf631
feat: Remove unused PostHogAPIClient implementation
lmtr0 Feb 24, 2025
334eea8
feat(axum-server): Add Axum server example with PostHog SDK
lmtr0 Feb 24, 2025
e9e9d73
feat: Implement actor-based microservice with PostHog integration
lmtr0 Feb 24, 2025
b01c836
feat: add initial query example
lmtr0 Feb 24, 2025
a15d1d3
feat(query): Add example for executing a PostHog query
lmtr0 Feb 24, 2025
b63adc5
feat: documentation
lmtr0 Feb 24, 2025
40a615e
Merge pull request #46 from lmtr0/feat/api-n-sdk
lmtr0 Feb 24, 2025
e934fbe
Update README.md
lmtr0 Mar 10, 2025
39fab04
Merge pull request #47 from lmtr0/lmtr0-patch-1
lmtr0 Mar 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
POSTHOG_RS_E2E_TEST_API_KEY=phc_XXXX # used in tests
POSTHOG_PUBLIC_KEY="..."
POSTHOG_API_KEY="..."
POSTHOG_API_URL="https://us.posthog.com"
POSTHOG_BASE_URL="https://us.i.posthog.com/"
POSTHOG_PROJECT_ID="..."
27 changes: 3 additions & 24 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
[package]
name = "posthog-rs"
license = "MIT"
version = "0.2.6"
authors = ["christos <[email protected]>"]
description = "An unofficial Rust client for Posthog (https://posthog.com/)."
repository = "https://github.com/openquery-io/posthog-rs"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
reqwest = { version = "0.11.3", default-features = false, features = ["blocking", "rustls-tls"] }
serde = { version = "1.0.125", features = ["derive"] }
chrono = {version = "0.4.19", features = ["serde"] }
serde_json = "1.0.64"
semver = "1.0.24"

[dev-dependencies]
dotenv = "0.15.0"
ctor = "0.1.26"

[features]
e2e-test = []
[workspace]
members = ["posthog-rs", "examples/*"]
resolver = "2"
109 changes: 98 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,113 @@
# PostHog Rust
# PostHog Rust SDK

Please see the main [PostHog docs](https://posthog.com/docs).
A Rust SDK for [PostHog](https://posthog.com), featuring a flexible event builder, an actor-based service for efficient event batching, and API client capabilities.

**This crate is under development**
## Features

# Quickstart
- **Event Builder Pattern**: Intuitive builder pattern for constructing PostHog events
- **Flexible Event Properties**: Use `serde_json::Value` for maximum flexibility in event property definitions
- **Actor-based Service**: Built-in actor system for efficient event batching and delivery
- **Query API Support**: Access PostHog's Query API for data analysis
- **Async/Await**: Built on Tokio for asynchronous operation
- **Error Handling**: Comprehensive error handling with proper context

Add `posthog-rs` to your `Cargo.toml`.
## Installation

Add `posthog-rs` to your `Cargo.toml`:

```toml
[dependencies]
posthog_rs = "0.2.0"
posthog-rs = "0.2.0"
```

## Quick Start

### Event Capture

```rust
let client = crate::client(env!("POSTHOG_API_KEY"));
use posthog_rs::sdk::{PostHogSDKClient, PostHogServiceActor, models::event::EventBuilder};
use serde_json::json;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize PostHog client
let client = PostHogSDKClient::new("your_api_key", "https://app.posthog.com")?;
let actor = PostHogServiceActor::new(client);

// Start the service actor
let sender = actor.start().await;

// Create and send an event
let event = EventBuilder::new("event_name")
.distinct_id("user_123")
.timestamp_now()
.properties(json!({
"property1": "value1",
"property2": 42,
"nested": {
"key": "value"
}
}))
.build();

sender.send(PostHogServiceMessage::Capture(event)).await?

Ok(())
}
```

let mut event = Event::new("test", "1234");
event.insert_prop("key1", "value1").unwrap();
event.insert_prop("key2", vec!["a", "b"]).unwrap();
### Query API

client.capture(event).unwrap();
```rust
use posthog_rs::api::{client::PostHogAPIClient, query::QueryRequest};
use serde_json::json;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = PostHogAPIClient::new("your_api_key", "https://app.posthog.com")?;

let request = QueryRequest::default().with_query(json!({
"kind": "HogQLQuery",
"query": "select * from events limit 10"
}));

let response = client.query("your_project_id", request).await?;
println!("{:#?}", response);

Ok(())
}
```

## Examples

Check out our [examples directory](./examples) for complete working examples:

- [Actor Microservice](./examples/actor-microservice): Using the PostHog service actor in a microservice
- [Query API](./examples/query): Executing queries against the PostHog Query API
- [Axum Server](./examples/axum-server): Integrating PostHog with an Axum web server

## API Coverage

Currently, the SDK supports:
- Event capture with the PostHog Service Actor
- Public API client (decide and capture endpoints)
- Feature Flags
- Annotations
- Persons and Groups
- Query API for data analysis

We plan to expand coverage to include:
- all the other posthog apis
- I write the client for the apis that I'm using in my projects, so If you need something, open a feature request in the issues tab or comment on one of the existing ones :)

## Contributing

Contributions are welcome! Feel free to:
- Open issues for bugs or feature requests
- Submit pull requests
- Improve documentation

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

93 changes: 93 additions & 0 deletions arc/Actor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Here's an architectural document describing the PostHog event actor system:

# PostHog Event Actor Architecture Document

## Overview
The system implements an actor-based approach for handling PostHog event submissions with timeout capabilities using Tokio and Rust channels.

## Components

### 1. Actor Message Enum
- `PostHogMessage`: An enumeration containing two variants
- `Capture`: Holds the event data to be sent to PostHog
- `Exit`: Signal to gracefully shutdown the actor

### 2. Actor State
- Contains PostHog client instance
- Internal channel receivers
- Processing state management

### 3. Core Components

#### Actor Creation Function
- Purpose: Creates and initializes the actor system
- Input: PostHog client instance
- Output: PostHog actor system
- Operations:
- Initializes the actor system

### Actor Run function
- Purpose: Runs the actor system
- Input: PostHog actor system
- Output: Sender of actor messages
- Operations:
- Spawns the actor task (using Runtime Provided when creating the system)
- Creates channel pair (sender/receiver)
- Spawns Tokio task for actor loop
- Returns sender handle to caller
- Actor Loop
- Infinite loop processing incoming messages
- Handles timeout for event processing
- Manages graceful shutdown
- Uses select pattern for message handling

#### Event Processing
- Timeout wrapper for PostHog API capture batch calls
- Error handling and retry logic
- Async processing of capture events

## Flow

1. System Initialization:
- Client creates actor with PostHog client specifying the Runtime
- Receives sender handle for communication

2. Normal Operation:
- System sends events via sender
- Actor processes events with timeout
- Events forwarded to PostHog

3. Shutdown:
- System sends Exit signal
- Actor completes pending events
- Closes channels and exits

## Error Handling

- Timeout handling for API calls
- Channel communication errors
- PostHog API errors
- Graceful degradation

## Performance Considerations

- Async processing for non-blocking operation
- Channel buffer sizes
- Timeout durations
- Resource cleanup

## Usage Pattern

1. Initialize actor with PostHog client
2. Obtain sender handle
3. Send events through sender
4. Send exit signal when done
5. Wait for cleanup

This architecture ensures:
- Asynchronous event processing
- Timeout handling
- Graceful shutdown
- Resource management
- Error handling
- Clean API surface
67 changes: 67 additions & 0 deletions arc/Query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Below, I have outlined the PostHog Query API endpoint architecture based on the information provided:

### Overview

The Query API endpoint is the main interface for querying data in PostHog, supporting various types of queries, including trends, funnels, actors, events, and HogQL queries. It is versatile for querying both event data and non-event data, such as persons or session replay metadata. The endpoint imposes certain rate limits and result size restrictions.

### Endpoints Summary

1. **POST /api/projects/:project_id/query**
- **Description**: Main endpoint for executing queries on PostHog data.
- **Method**: POST
- **Path**: `/api/projects/:project_id/query`
- **Request Body**:
- Must be a JSON object with a `query` field.
- Example structure:
```json
{
"query": {
"kind": "HogQLQuery",
"query": "select properties.email from persons where properties.email is not null"
}
}
```
- Optional parameters:
- `async`: A client-provided ID for later reference.
- `filters_override`: Specific filters to apply.
- `refresh`: Defines query execution behavior concerning caching (e.g., 'blocking', 'async').
- **Response**: Typically returns the query result, limited to 10,000 rows. JSON format.

2. **GET /api/projects/:project_id/query/:id**
- **Description**: Retrieve the status or result of a previously executed query.
- **Method**: GET
- **Path**: `/api/projects/:project_id/query/:id`
- **Response**:
- JSON object containing fields such as `query_status`, `task_id`, and `results`.

3. **DELETE /api/projects/:project_id/query/:id**
- **Description**: Cancel an ongoing query.
- **Method**: DELETE
- **Path**: `/api/projects/:project_id/query/:id`
- **Response**: Status 204 No Content upon successful cancellation.

4. **POST /api/projects/:project_id/query/check_auth_for_async**
- **Description**: Check authorization for executing asynchronous queries.
- **Method**: POST
- **Path**: `/api/projects/:project_id/query/check_auth_for_async`
- **Response**: Status 200 with no body.

5. **GET /api/projects/:project_id/query/draft_sql**
- **Description**: Retrieve draft SQL query for a project.
- **Method**: GET
- **Path**: `/api/projects/:project_id/query/draft_sql`
- **Response**: Status 200 with no body.

### Important Considerations

- **Rate Limits**: The API allows up to 120 requests per hour per team, each capable of returning up to 10,000 rows.
- **Authentication**: Requires a personal API key with at least `query:read` scope.
- **Request Headers**: Should include 'Content-Type: application/json' and 'Authorization: Bearer [API_KEY]'.
- **Caching & Execution Modes**: The `refresh` parameter controls how queries interact with the cache and whether they execute asynchronously or synchronously.

### Known Issues

- Date filters may be overridden by default filters in HogQL unless explicitly set with `after` and `before`.
- If the query requires more data or requests than allowed by rate limits, consider using batch exports for larger data needs.

Before implementing the API client, ensure to handle possible API rate limits and request Payload errors gracefully, and maintain up-to-date API keys and scopes.
6 changes: 6 additions & 0 deletions examples/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# PostHog Rust Examples

Here you will find some examples of how to use the PostHog SDK in Rust.
- [actor microservice example](./actor-microservice/Readme.md): An example of how to use the PostHog SDK in an actor-based microservice architecture.
- [axum server](./axum-server/Readme.md): An example of how to use the PostHog SDK in an Axum WebServer server.
- [Query API](./query/Readme.md): An example of how to use the PostHog SDK to query data in a PostHog project.
15 changes: 15 additions & 0 deletions examples/actor-microservice/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "actor-microservice"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0.96"
axum = "0.8.1"
dotenvy = "0.15.7"
posthog-rs = { path = "../../posthog-rs" }
serde = { version = "1.0.218", features = ["derive"] }
serde_json = "1.0.139"
tokio = { version = "1.43.0", features = ["full"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
34 changes: 34 additions & 0 deletions examples/actor-microservice/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# PostHog Actor Microservice Example

This example demonstrates how to use the PostHog SDK in an actor-based microservice architecture. It showcases:
- Creating a PostHog service actor for handling event capture
- Using channels for message passing between actors
- Implementing a sample actor that processes messages and sends events to PostHog
- Proper error handling and logging

## Setup

1. Create a `.env` file in this directory with the following variables:
```env
POSTHOG_PUBLIC_KEY=your_project_api_key
POSTHOG_BASE_URL=your_posthog_instance_url
```

2. Build and run the example:
```bash
cargo run
```

## How it Works

The example creates a `SampleActor` that:
1. Receives JSON messages through a channel
2. Processes each message
3. Sends the processed data to PostHog using the `PostHogServiceActor`

The `PostHogServiceActor` handles:
- Batching events for efficient delivery
- Automatic retries on failure
- Proper shutdown and cleanup

The example will send 100 test messages, each containing a simple counter value, demonstrating how to integrate PostHog event capture into an actor-based system.
Loading