Java SDK

Type-safe Java client for InferaDB.

Coming soon. The Java SDK is under active development. The API surface shown here is based on the Rust SDK and may change before release.

Typed, fluent client for InferaDB. Requires Java 17+.

Installation

Maven

<dependency>
    <groupId>com.inferadb</groupId>
    <artifactId>inferadb-sdk</artifactId>
    <version>0.1.0</version>
</dependency>

Gradle

implementation "com.inferadb:inferadb-sdk:0.1.0"

Authentication

Three authentication methods:

Method Use Case Security
Client Credentials (Ed25519 JWT) Service-to-service High
Bearer Token User sessions, OAuth Medium
API Key Testing, simple integrations Basic
import com.inferadb.InferaDB;
import com.inferadb.auth.Ed25519PrivateKey;
import com.inferadb.auth.ClientCredentials;

import java.nio.file.Path;

var key = Ed25519PrivateKey.fromPemFile(Path.of("client.pem"));

var client = InferaDB.builder()
    .url("https://engine.inferadb.com")
    .credentials(new ClientCredentials("my-client", key, "cert-id"))
    .build();

Bearer Token

var client = InferaDB.builder()
    .url("https://engine.inferadb.com")
    .bearerToken(System.getenv("INFERADB_TOKEN"))
    .build();

API Key

var client = InferaDB.builder()
    .url("https://engine.inferadb.com")
    .apiKey(System.getenv("INFERADB_API_KEY"))
    .build();

Permission Checks

var vault = client.organization("my-org").vault("production");

boolean allowed = vault.check("user:alice", "can_edit", "document:readme");

With ABAC Context

import java.util.Map;

boolean allowed = vault.check("user:alice", "can_view", "document:readme",
    CheckOptions.builder()
        .context(Map.of("ip_address", "10.0.0.1"))
        .build());

Require — Throws on Deny

// Throws AccessDeniedException if permission is denied
vault.require("user:alice", "can_edit", "document:readme");

With Consistency Token

boolean allowed = vault.check("user:alice", "can_view", "document:readme",
    CheckOptions.builder()
        .atLeastAsFresh(revisionToken)
        .build());

Batch Check

import java.util.List;

var results = vault.checkBatch(List.of(
    new CheckRequest("user:alice", "can_edit", "document:readme"),
    new CheckRequest("user:bob", "can_view", "document:readme")
));
if (results.allAllowed()) {
    // all checks passed
}

Relationships

Write

// Returns a revision token
var token = vault.relationships().write(
    new Relationship("document:readme", "editor", "user:alice"));

Batch Write

vault.relationships().writeBatch(List.of(
    new Relationship("document:readme", "editor", "user:alice"),
    new Relationship("document:readme", "viewer", "user:bob")
));

List

var rels = vault.relationships()
    .list()
    .resource("document:readme")
    .collect();

Delete

vault.relationships()
    .deleteWhere()
    .resource("document:readme")
    .relation("viewer")
    .subject("user:bob")
    .execute();

Lookups

// What resources can Alice view?
var resources = vault.resources()
    .accessibleBy("user:alice")
    .withPermission("can_view")
    .resourceType("document")
    .collect();

// Who can edit this document?
var subjects = vault.subjects()
    .withPermission("can_edit")
    .onResource("document:readme")
    .collect();

Testing

MockClient (Fastest)

Stub specific check results. With verifyOnClose(true), all expectations are asserted on client.close() – use with try-with-resources.

import com.inferadb.testing.MockClient;

var client = MockClient.builder()
    .onCheck("user:alice", "can_edit", "document:readme").allow()
    .onCheck("user:bob", "can_edit", "document:readme").deny()
    .onCheckAnySubject("can_view", "document:readme").allow()
    .defaultDeny()
    .verifyOnClose(true)
    .build();

InMemoryClient (Full Policy Evaluation)

import com.inferadb.testing.InMemoryClient;

var client = InMemoryClient.withSchemaAndData(
    """
    type document {
        relation viewer
        relation editor
        relation can_view = viewer | editor
    }
    """,
    List.of(
        new Relationship("document:readme", "editor", "user:alice"),
        new Relationship("document:readme", "viewer", "user:bob")
    )
);

TestVault (Real Instance)

Auto-cleans up on close via try-with-resources.

import com.inferadb.testing.TestVault;

try (var vault = TestVault.create(org, schemaIpl)) {
    boolean allowed = vault.check("user:alice", "can_edit", "document:readme");
}

Error Handling

import com.inferadb.InferaDBException;
import com.inferadb.AccessDeniedException;

try {
    vault.require("user:alice", "can_edit", "document:readme");
} catch (AccessDeniedException e) {
    // permission denied
} catch (InferaDBException e) {
    if (e.isRetriable()) {
        // retry after e.getRetryAfter()
    }
    logger.error("Authorization error: kind={}, requestId={}",
        e.getKind(), e.getRequestId());
}

ErrorKind enum: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, RATE_LIMITED, SCHEMA_VIOLATION, UNAVAILABLE, TIMEOUT, INVALID_ARGUMENT.

Framework Integrations

Spring Boot

import com.inferadb.spring.RequirePermission;

@RestController
@RequestMapping("/documents")
public class DocumentController {

    @GetMapping("/{id}")
    @RequirePermission(
        subject = "#{authentication.name}",
        permission = "can_view",
        resource = "'document:' + #id"
    )
    public Document getDocument(@PathVariable String id) {
        return documentService.findById(id);
    }
}

Spring Security Integration

import com.inferadb.spring.InferaDBAuthorizationManager;

@Bean
SecurityFilterChain filterChain(HttpSecurity http,
        InferaDBAuthorizationManager authz) throws Exception {
    return http
        .authorizeHttpRequests(requests -> requests
            .requestMatchers("/api/**").access(authz)
            .anyRequest().permitAll()
        )
        .build();
}