Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 37 additions & 0 deletions posthog-samples/posthog-spring-sample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar

# Gradle
.gradle/
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
gradle-app.setting
.gradletasknamecache

# IDE
.idea/
*.iml
*.iws
*.ipr
.vscode/
.classpath
.project
.settings/

# OS
.DS_Store
Thumbs.db

# Logs
*.log
139 changes: 139 additions & 0 deletions posthog-samples/posthog-spring-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# PostHog Spring Sample

A sample Spring Boot application demonstrating how to integrate the PostHog server-side SDK as a Spring-managed bean.

## Overview

This sample shows how to:

- Configure PostHog as a Spring Bean using dependency injection
- Inject the PostHog client into Spring controllers
- Capture events from REST API endpoints
- Configure PostHog via Spring properties

## Project Structure

```
src/main/java/com/posthog/spring/sample/
├── PostHogSpringSampleApplication.java # Spring Boot application entry point
├── PostHogConfiguration.java # PostHog bean configuration
└── ActionController.java # REST controller that captures events
```

## Configuration

### PostHogConfiguration.java

The `PostHogConfiguration` class creates a Spring-managed PostHog bean:

```java
@Configuration
public class PostHogConfiguration {
@Value("${posthog.api.key:phc_YOUR_API_KEY_HERE}")
private String apiKey;

@Value("${posthog.host:https://us.i.posthog.com}")
private String host;

@Bean(destroyMethod = "close")
public PostHogInterface posthog() {
PostHogConfig config = PostHogConfig
.builder(this.apiKey)
.host(this.host)
.debug(true)
.build();

return PostHog.with(config);
}
}
```

Key points:

- The `@Bean` annotation makes PostHog available for dependency injection
- `destroyMethod = "close"` ensures proper cleanup when the application shuts down
- Configuration values are read from `application.properties`

### application.properties

Configure PostHog settings in `src/main/resources/application.properties`:

```properties
# PostHog Configuration
posthog.api.key=phc_YOUR_API_KEY_HERE
posthog.host=https://us.i.posthog.com

# Spring Boot Configuration
spring.application.name=posthog-spring-sample
logging.level.com.posthog=DEBUG
```

## Setup

1. Update `application.properties` with your PostHog API key:

```properties
posthog.api.key=phc_your_actual_api_key
```

2. (Optional) Change the host if using a self-hosted instance:

```properties
posthog.host=https://your-posthog-instance.com
```

3. Build and run the application:
```bash
./gradlew :posthog-samples:posthog-spring-sample:bootRun
```

The application will start on port 8080 by default.

## Usage

### Capturing Events

The sample provides a REST endpoint that captures a PostHog event:

```bash
curl -X POST http://localhost:8080/api/action
```

This will:

1. Generate a distinct user ID (in production, use a real user identifier)
2. Capture an event named `action_performed` with properties `{ "source": "api" }`
3. Return a 200 OK response

### Controller Implementation

The `ActionController` demonstrates dependency injection of the PostHog client:

```java
@RestController
@RequestMapping("/api")
public class ActionController {

private final PostHogInterface postHog;

public ActionController(PostHogInterface postHog) {
this.postHog = postHog;
}

@PostMapping("/action")
public ResponseEntity performAction() {
String distinctId = "user-" + System.currentTimeMillis();

postHog.capture(
distinctId,
"action_performed",
PostHogCaptureOptions
.builder()
.property("source", "api")
.build()
);

return ResponseEntity.ok().build();
}
}
```
24 changes: 24 additions & 0 deletions posthog-samples/posthog-spring-sample/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
plugins {
java
id("org.springframework.boot") version "3.2.0"
id("io.spring.dependency-management") version "1.1.4"
}

group = "com.posthog"
version = "1.0.0"

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation(project(":posthog-server"))
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<Test> {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "posthog-spring-sample"
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.posthog.spring.sample;

import com.posthog.server.PostHogInterface;
import com.posthog.server.PostHogCaptureOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class ActionController {

private static final Logger logger = LoggerFactory.getLogger(ActionController.class);

private final PostHogInterface postHog;

public ActionController(PostHogInterface postHog) {
this.postHog = postHog;
}

@PostMapping("/action")
public ResponseEntity performAction() {
// In practice, use a real, stable distinct ID
// String distinctId = currentUser.getId();
String distinctId = "user-" + System.currentTimeMillis();

postHog.capture(
distinctId,
"action_performed",
PostHogCaptureOptions
.builder()
.property("source", "api")
.build()
);

return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.posthog.spring.sample;

import com.posthog.server.PostHogConfig;
import com.posthog.server.PostHogInterface;
import com.posthog.server.PostHog;
import jakarta.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PostHogConfiguration {
@Value("${posthog.api.key:phc_YOUR_API_KEY_HERE}")
private String apiKey;

@Value("${posthog.host:https://us.i.posthog.com}")
private String host;

@Bean(destroyMethod = "close")
public PostHogInterface posthog() {
PostHogConfig config = PostHogConfig
.builder(this.apiKey)
.host(this.host)
.build();

return PostHog.with(config);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.posthog.spring.sample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PostHogSpringSampleApplication {

public static void main(String[] args) {
SpringApplication.run(PostHogSpringSampleApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# PostHog Configuration
posthog.api.key=phc_YOUR_API_KEY_HERE
posthog.host=https://us.i.posthog.com

# Spring Boot Configuration
spring.application.name=posthog-spring-sample
logging.level.com.posthog=DEBUG
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ include(":posthog-server")
// samples
include(":posthog-samples:posthog-android-sample")
include(":posthog-samples:posthog-java-sample")
include(":posthog-samples:posthog-spring-sample")
Loading