The Kodall Java SDK (kodall-client-java) is a lightweight, high-performance REST client designed specifically for Java 21+. It features zero third-party runtime dependencies, relying entirely on standard JDK 21 libraries (java.net.http.HttpClient, Java Records, and Pattern Matching).
Add the client dependency to your build tool configuration:
Add to your pom.xml:
<dependency>
<groupId>kodall</groupId>
<artifactId>kodall-client-java</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
Add to your build.gradle:
dependencies {
implementation 'kodall:kodall-client-java:1.0.0-SNAPSHOT'
}
Add to your build.gradle.kts:
dependencies {
implementation("kodall:kodall-client-java:1.0.0-SNAPSHOT")
}
Download the standalone kodall-client-java-1.0.0.jar and include it directly in your project classpath or lib/ directory:
javac -cp "lib/kodall-client-java-1.0.0.jar:." Main.java
java -cp "lib/kodall-client-java-1.0.0.jar:." Main
Initialize the client with your backend server URL and API key:
import kodall.client.KodallClient;
import kodall.client.KodallClientConfig;
import kodall.client.exception.KodallApiException;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// Configure client
KodallClientConfig config = KodallClientConfig.newBuilder()
.baseUrl("https://docs-demo.kodall.io")
.apiKey("e3097afe-1528-4fdb-a816-0d3724a8f318")
.build();
KodallClient client = new KodallClient(config);
// Execute a fetch query
try {
String query = """
FETCH todo (key, title, is_completed)
ORDER BY key DESC
LIMIT 5
""";
List<Map<String, Object>> todos = client.fetch(query);
for (Map<String, Object> todo : todos) {
System.out.println("Todo #" + todo.get("key") + ": " + todo.get("title"));
}
} catch (KodallApiException e) {
System.err.println("API Error (" + e.getStatusCode() + "): " + e.getMessage());
}
}
}
The client is configured using the immutable KodallClientConfig builder:
import kodall.client.KodallClient;
import kodall.client.KodallClientConfig;
import java.time.Duration;
// Stateless API Key mode (Microservices, background tasks)
KodallClientConfig apiKeyConfig = KodallClientConfig.newBuilder()
.baseUrl("https://docs-demo.kodall.io")
.apiKey("e3097afe-1528-4fdb-a816-0d3724a8f318")
.connectTimeout(Duration.ofSeconds(10))
.requestTimeout(Duration.ofSeconds(30))
.build();
KodallClient client = new KodallClient(apiKeyConfig);
// Stateful Session Cookie mode (Desktop client, interactive CLI)
KodallClientConfig sessionConfig = KodallClientConfig.newBuilder()
.baseUrl("https://docs-demo.kodall.io")
.build();
KodallClient sessionClient = new KodallClient(sessionConfig);
| Config Option | Type | Description |
|---|---|---|
baseUrl(String) | String | Base endpoint URL (e.g. https://docs-demo.kodall.io). |
apiKey(String) | String | Secret API key used for stateless X-API-Key authentication. |
connectTimeout(Duration) | Duration | HTTP connection establishment timeout (default: 10s). |
requestTimeout(Duration) | Duration | Individual HTTP request execution timeout (default: 30s). |
import kodall.client.KodallClient;
import kodall.client.KodallClientConfig;
import kodall.client.model.UserPassword;
import kodall.client.model.Session;
import kodall.client.exception.KodallApiException;
KodallClientConfig config = KodallClientConfig.newBuilder()
.baseUrl("https://docs-demo.kodall.io")
.build();
KodallClient client = new KodallClient(config);
try {
UserPassword credentials = new UserPassword("root", "password123");
Session session = client.auth(credentials);
System.out.println("Authenticated as: " + session.userName());
} catch (KodallApiException e) {
System.err.println("Authentication failed: " + e.getMessage());
}
import kodall.client.model.OidcTokens;
Session session = client.auth(new OidcTokens("eyJhbGciOi...", "optionalRefreshToken"));
import kodall.client.model.AuthOptions;
import kodall.client.model.UserProfile;
import java.util.List;
// Fetch current session with roles and available user profiles
Session session = client.session(new AuthOptions(true, true));
System.out.println("User roles: " + session.roles());
// Switch active user profile
client.changeUserProfile("sales_admin");
// Refresh active session cookie token
boolean refreshed = client.refreshSession();
// Logout
client.logout();
For more authentication details, visit the Auth API Reference.
The Java SDK uses strongly typed immutable Java records (Entity, ChildEntity, Operation):
import kodall.client.model.Entity;
import kodall.client.model.ChildEntity;
import kodall.client.model.Operation;
import kodall.client.exception.KodallValidationException;
import kodall.client.exception.KodallApiException;
import java.util.Map;
import java.util.List;
try {
// 1. Create Entity with nested children
Entity newTodo = new Entity("todo",
Map.of("title", "Review Java 21 SDK", "is_completed", 0),
Map.of("todo_todo_item", List.of(
new ChildEntity("todo_item", Map.of("description", "Write documentation", "is_completed", 1))
))
);
Operation created = client.create(newTodo);
long key = created.key();
System.out.println("Created Entity Key: " + key);
// 2. Retrieve Entity by Key
Entity fetched = client.get("todo", key);
System.out.println("Fetched properties: " + fetched.properties());
// 3. Update Entity
Entity updated = new Entity("todo", Map.of(
"key", key,
"title", "Review Java 21 SDK (Completed)",
"is_completed", 1
));
client.update(updated);
// 4. Delete Entity
client.delete("todo", key);
} catch (KodallValidationException e) {
// HTTP 424: Business rule validation constraint violated
System.err.println("Validation Error: " + e.getValidation().detail());
} catch (KodallApiException e) {
System.err.println("API Error (" + e.getStatusCode() + "): " + e.getMessage());
}
For more entity operations and response structures, visit the Entities API Reference.
Query records flexibly as dynamic Map<String, Object> or bind directly to your own Java Records:
String query = """
FETCH todo (key, title, is_completed) {
todo_item TO id_todo (key AS item_key, description, is_completed AS item_completed)
}
FILTER AND (is_completed == 0)
""";
List<Map<String, Object>> rows = client.fetch(query);
Define a record matching your query projections:
public record TodoSummary(long key, String title, int isCompleted) {}
// Directly map query results to your Java 21 record
List<TodoSummary> todos = client.fetch(
"FETCH todo (key, title, is_completed AS isCompleted)",
TodoSummary.class
);
for (TodoSummary todo : todos) {
System.out.println(todo.title() + " (Key: " + todo.key() + ")");
}
// Parse AST
Map<String, Object> ast = client.parseQuery("FETCH todo (key, title)");
// Inspect query columns metadata
Map<String, Object> metadata = client.fetchMetadata("FETCH todo (key, title)");
For more query syntax options, visit the Fetch API Reference.
Invoke server-side workflows by name and deserialize responses to custom types:
public record WorkflowResponse(String message, String status) {}
Map<String, Object> payload = Map.of("name", "Java Developer", "type", "default");
WorkflowResponse response = client.workflow(
"DemoWf",
"hello",
payload,
WorkflowResponse.class
);
System.out.println("Workflow response: " + response.message());
For more workflow examples, visit the Workflow API Reference.
Upload, download, and version files with zero external HTTP form dependencies:
import kodall.client.model.StorageItemResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
// 1. Upload a file
byte[] fileBytes = Files.readAllBytes(Path.of("report.pdf"));
List<StorageItemResponse> items = client.uploadFile(fileBytes, "report.pdf", null);
long fileId = items.get(0).id();
// 2. Upload a new version of the file
byte[] updatedBytes = Files.readAllBytes(Path.of("report_v2.pdf"));
client.uploadFileVersion(updatedBytes, "report.pdf", fileId);
// 3. Download file bytes
byte[] downloaded = client.getFile(fileId, null, "download");
Files.write(Path.of("downloaded_report.pdf"), downloaded);
For more storage operations, visit the Storage API Reference.
The Java SDK throws structured typed exceptions for precise error interception:
import kodall.client.exception.KodallValidationException;
import kodall.client.exception.KodallApiException;
try {
client.create(myEntity);
} catch (KodallValidationException e) {
// HTTP 424: Business rule validation failure
System.err.println("Event: " + e.getValidation().eventName());
System.err.println("Entity: " + e.getValidation().entityName());
System.err.println("Property: " + e.getValidation().propertyName());
System.err.println("Constraint violation: " + e.getValidation().detail());
} catch (KodallApiException e) {
// HTTP 400, 401, 403, 404, 500
System.err.println("HTTP Status Code: " + e.getStatusCode());
System.err.println("Error Detail: " + e.getMessage());
if (e.getProblem() != null) {
System.err.println("Problem Type: " + e.getProblem().type());
}
}