Java 21+ client for SBX Cloud APIs. Spring Boot 3.x compatible.
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.socobox</groupId>
<artifactId>sbxcloud-lib-java</artifactId>
<version>v0.0.16</version>
</dependency>repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.socobox:sbxcloud-lib-java:v0.0.16'
}import com.sbxcloud.sbx.annotation.SbxModel;
import com.sbxcloud.sbx.annotation.SbxEntity;
import com.sbxcloud.sbx.model.SBXMeta;
@SbxModel("contact") // Maps to SBX model name - handles all Jackson config
public record Contact(
String key, // Auto-mapped to _KEY
SBXMeta meta, // Auto-mapped to _META
String name,
String email,
String status
) implements SbxEntity {}// From environment variables (SBX_APP_KEY, SBX_TOKEN, SBX_DOMAIN, SBX_BASE_URL)
var sbx = SBXServiceFactory.withEnv();
// Or with explicit config
var sbx = SBXServiceFactory.builder()
.appKey("your-app-key")
.token("your-token")
.domain(96)
.baseUrl("https://sbxcloud.com")
.build();// Get typed repository
SbxRepository<Contact> contacts = sbx.repository(Contact.class);
// Create using Sbx.create() - automatically sets key and meta to null
var contact = Sbx.create(Contact.class, "John Doe", "john@example.com", "ACTIVE");
String key = contacts.save(contact);
// Read
Optional<Contact> found = contacts.findById(key);
List<Contact> all = contacts.findAll();
// Update - only _KEY and changed fields needed (partial update)
var updated = new Contact(key, null, "John Updated", null, null);
contacts.save(updated); // Only updates name, other fields unchanged
// Delete
contacts.deleteById(key);Important:
metais read-only and always stripped before create/updatenullvalues are stripped, enabling partial updates- For updates, only
key+ changed fields are required
SbxRepository<Contact> repo = sbx.repository(Contact.class);
// Find
repo.findById("key") // Optional<Contact>
repo.findByIds("k1", "k2") // List<Contact>
repo.findAll() // List<Contact>
repo.findAll(page, pageSize) // SBXFindResponse<Contact>
repo.existsById("key") // boolean
repo.count() // long
// Save (auto insert/update based on key presence)
repo.save(entity) // String (returns key)
repo.saveAll(e1, e2, e3) // List<String>
// Delete
repo.delete(entity) // void
repo.deleteById("key") // void
repo.deleteByIds("k1", "k2") // void
repo.deleteAll(e1, e2) // void// Fluent queries
repo.query()
.where(q -> q
.andWhereIsEqualTo("status", "ACTIVE")
.andWhereIsGreaterThan("age", 18)
.andWhereContains("name", "John"))
.fetch("account", "department")
.page(1, 50)
.list();
// Query results
repo.query().where(...).list() // List<T> - all pages
repo.query().where(...).listPage() // List<T> - current page only
repo.query().where(...).first() // Optional<T>
repo.query().where(...).firstOrThrow() // T or exception
repo.query().where(...).count() // long
repo.query().where(...).exists() // boolean
repo.query().where(...).execute() // SBXFindResponse<T>
// Fetching related entities (type-safe!)
var response = repo.query()
.fetch("account", "department")
.execute();
List<Account> accounts = response.getFetched(Account.class);
Account acc = response.getFetchedByKey(Account.class, contact.account());
// Shortcuts
repo.findWhere(q -> q.andWhereIsEqualTo("status", "ACTIVE"))
repo.query().whereEquals("status", "ACTIVE").first()
repo.query().whereKeys("k1", "k2").list()// AND conditions
.andWhereIsEqualTo(field, value)
.andWhereIsNotEqualTo(field, value)
.andWhereIsGreaterThan(field, value)
.andWhereIsGreaterOrEqualTo(field, value)
.andWhereIsLessThan(field, value)
.andWhereIsLessOrEqualTo(field, value)
.andWhereContains(field, "text") // LIKE %text%
.andWhereStartsWith(field, "text") // LIKE text%
.andWhereEndsWith(field, "text") // LIKE %text
.andWhereIsIn(field, "a", "b", "c")
.andWhereIsNotIn(field, "a", "b")
.andWhereIsNull(field)
.andWhereIsNotNull(field)
// OR conditions (same methods with "or" prefix)
.orWhereIsEqualTo(field, value)
// ...
// Groups
.newGroupWithAnd() // Start AND group
.newGroupWithOr() // Start OR group
// By keys
.whereWithKeys("key1", "key2")@SbxModel("contact")
public record Contact(
String key, // Primary key (_KEY) - null for new records
SBXMeta meta, // Read-only metadata (_META) - always null for create/update
String name,
String email
) implements SbxEntity {}The @SbxModel annotation automatically:
- Sets the SBX model name for queries
- Maps
key→_KEYin JSON - Maps
meta→_METAin JSON (read-only, stripped on create/update) - Ignores unknown JSON properties
- Strips null values (enables partial updates)
Instead of manual convenience constructors, use Sbx.create():
// No need to add a convenience constructor to your record!
// Sbx.create() automatically sets key and meta to null
var contact = Sbx.create(Contact.class, "John", "john@example.com");
// Equivalent to: new Contact(null, null, "John", "john@example.com")
// Pass field values in order (excluding key and meta)
String key = contacts.save(contact);public record Contact(
@JsonProperty("_KEY") String key,
@JsonProperty("_META") SBXMeta meta,
String name,
String email
) {}For advanced use cases, use SBXService directly:
// Find - type is inferred, no need to pass class twice!
var query = FindQuery.from(Contact.class)
.andWhereIsEqualTo("status", "ACTIVE")
.setPageSize(50);
var response = sbx.find(query); // Returns SBXFindResponse<Contact>
// Create/Update (with Map)
sbx.create("contact", Map.of("name", "John", "email", "john@example.com"));
sbx.update("contact", Map.of("_KEY", "existing-key", "name", "Updated"));
// Delete
sbx.delete("contact", "key-to-delete");
sbx.delete("contact", List.of("k1", "k2", "k3"));import static com.sbxcloud.sbx.util.Sbx.*;
// Create entities (automatically sets key and meta to null)
var contact = create(Contact.class, "John", "john@example.com", "ACTIVE");
// JSON conversion
String json = toJson(entity);
String pretty = toPrettyJson(entity);
Contact contact = fromJson(json, Contact.class);
// Map conversion
Map<String, Object> map = toMap(entity);
Contact fromMap = fromMap(map, Contact.class);# application.properties
sbx.app-key=your-app-key-uuid
sbx.token=your-bearer-token
sbx.domain=96
sbx.base-url=https://sbxcloud.com
sbx.debug=false@Service
public class ContactService {
private final SbxRepository<Contact> contacts;
public ContactService(SBXService sbx) {
this.contacts = sbx.repository(Contact.class);
}
public List<Contact> findActive() {
return contacts.findWhere(q -> q.andWhereIsEqualTo("status", "ACTIVE"));
}
public Contact create(String name, String email) {
var contact = new Contact(name, email, "ACTIVE");
String key = contacts.save(contact);
return contacts.findById(key).orElseThrow();
}
}| Variable | Description |
|---|---|
SBX_APP_KEY |
Application key (UUID) |
SBX_TOKEN |
Bearer token |
SBX_DOMAIN |
Domain ID (integer) |
SBX_BASE_URL |
Base URL (e.g., https://sbxcloud.com) |
sbx.login("user@example.com", "password");
sbx.validateSession();
sbx.changePassword("current", "new", userId);sbx.uploadFile("doc.pdf", base64Content, folderKey);
byte[] content = sbx.downloadFile("file-key");
sbx.deleteFile("file-key");sbx.createFolder("New Folder", parentKey);
sbx.listFolder(folderKey);
sbx.renameFolder(key, "New Name");
sbx.deleteFolder(key);var email = EmailParams.builder()
.to("recipient@example.com")
.subject("Hello")
.templateKey("template-key")
.data(Map.of("name", "John"))
.build();
sbx.sendEmail(email);var result = sbx.runCloudScript("script-key", Map.of("param", "value"), ResultType.class);ISC