Skip to main content

Building Your App

The following information is aimed at users building or configuring applications directly. It explains how applications connect to SyncHive, access data, and manage authentication.

If you are using an AI coding tool to generate your application, you can usually skip this section. The generated prompt snippet already references the relevant SyncHive documentation, allowing the AI tool to access the technical implementation details it needs while generating the application.

Installing the SyncHive SDK

SyncHive Apps are built using the official SyncHive JavaScript SDK, which handles sign-in and secure access automatically.

View the SDK on GitHub: SyncHive JavaScript SDK.

Most apps install it via npm:

npm install @synchive/synchive-js

Once installed, your app can connect to SyncHive. In the snippet below, replace the placeholder value for publishableKey with the publishable key from the app's connection details.

import { SyncHiveClient } from "@synchive/synchive-js";

const synchive = new SyncHiveClient({
publishableKey: "sh_publishable_...",
});

// Initialize the client first.
try {
await synchive.init();
} catch (error) {
// Surface sign-in callback errors to the user.
console.error("Auth failed:", error);
}

// Listen for auth lifecycle events.
synchive.onAuthStateChange(({ user }, event) => {
// Fires immediately on mount, then whenever auth state changes.
if (event === "authenticated") {
// User is signed in. Show logged-in UI.
// setUser(user);
}

if (event === "unauthenticated") {
// User is signed out. Show logged-out UI.
// setUser(null);
}
});

// Call these from your UI event handlers:
// await synchive.signInRedirect();
// await synchive.signOutRedirect();

Quick flow:

  1. Create SyncHiveClient with your app's publishableKey.
  2. Call await synchive.init() once on app startup.
  3. React to onAuthStateChange events to render signed-in vs signed-out UI.
  4. Use signInRedirect() and signOutRedirect() from your UI event handlers.

Reading & Writing Data with SyncHive

Once you're signed in, you can read and write your app's data using the SyncHive SDK.

List Records

Use list() to fetch records from a shape:

const products = await synchive.list("Product", {
top: 20,
});

Get a Specific Record

Use get() to retrieve a single record by its hiveId:

const product = await synchive.get("Product", "D6BFA0AB71A1");

Create a Record

Use create() to create a new record in a shape:

const createdProduct = await synchive.create("Product", {
name: "Two-Slice Toaster",
sku: "TOASTER-2S-BLK",
});

create() returns the full created shape record.

Update a Record

Use update() to update an existing record by hiveId. For update requests, the root hiveId is required in the payload. You can pass either a partial shape or a full shape:

const updatedProduct = await synchive.update("Product", "D6BFA0AB71A1", {
hiveId: "D6BFA0AB71A1",
status: "discontinued",
});

You can soft delete a record using update() by setting isDeleted to true.

note

isDeleted must be enabled on the shape before you can use soft deletes.

update() returns the full updated shape record unless it has been soft deleted, in which case it returns nothing.

You can view soft-deleted records in SyncHive Explore by clicking Filter, then Show Deleted, and then Apply Filters.

Filtering and Paging

You can pass query options such as:

  • top controls how many records are returned, with a maximum of 500 per request.
  • skip controls how many records are skipped before returning results.
  • filter lets you request only records that match specific conditions.
  • orderby sorts records by one or more fields. Use asc or desc.

Additional details:

  • If top is greater than 500, SyncHive uses 500 instead.
  • For orderby, use a comma-separated list, such as createdOn desc,name asc.

Example:

const salesOrders = await synchive.list("SalesOrder", {
filter:
"transactionNumber eq 'TOASTER-1001' and salesOrderItem/any(i: i/name eq '4-Slice Toaster')",
orderby: "createdOn desc,transactionNumber asc",
skip: 0,
top: 25,
});

Supported filters

SyncHive supports only this subset of the OData filter syntax:

  • Comparison operators: eq, ne, gt, lt, ge, le
  • Logical operators: and, or, not
  • Collection operators: any, all
  • One text function: contains(field,'value')

Examples of supported filters:

status eq 'Active'
status ne 'Inactive'
hiveId eq 'D6BFA0AB71A1'
amount gt 100 and amount lt 500
total ge 1000 or discount le 25
not (status eq 'Archived')
salesOrderItem/any(i: i/name eq '4-Slice Toaster')
salesOrderItem/all(i: i/quantity ge 1)
contains(customerName,'Acme')

hiveId filters support only the eq operator.

Unsupported filters

SyncHive does not currently support:

  • Other OData functions such as startswith(), endswith(), length(), or tolower()
  • Lambda expressions beyond any and all
  • Full OData query features such as expand or advanced nested function combinations

Examples of unsupported filters:

startswith(customerName,'A')
endswith(customerName,'Inc')
hiveId ne 'D6BFA0AB71A1'
length(customerName) gt 10
tolower(customerName) eq 'acme'
salesOrderItem/filter(i: i/qty gt 1)
contains(tolower(customerName),'acme')
expand=salesOrderItem

If a filter uses syntax not listed in Supported filters, treat it as unsupported.

Storing Files in SyncHive

You can also store and manage files using the SyncHive SDK.

When you upload a file, SyncHive automatically creates a control record for it in a system table named FileStore, which you can view directly in your schema. This record stores the file's metadata, such as its name, size, and type. To attach a file to your own shape records, add a property that references the FileStore system table, and store the file's fileHiveId in it.

Uploading a File

Use uploadFile() to upload a file:

const uploaded = await synchive.uploadFile(file);

uploadFile() returns the file's fileHiveId, along with the details SyncHive used to transfer the file.

note

There is an upload limit of 5GB per file.

If your app attaches multiple files to a single record, upload each file separately and store the resulting fileHiveId values against that record.

Replacing a File

To replace an existing file's content, pass its fileHiveId to uploadFile():

const replaced = await synchive.uploadFile(file, {
fileHiveId: uploaded.fileHiveId,
});

The file's fileHiveId does not change. Its metadata, such as file name and file size, is updated automatically to match the new content.

Downloading a File

Use downloadFile() to download a file by its fileHiveId:

const downloaded = await synchive.downloadFile(uploaded.fileHiveId);

downloadFile() returns the file's metadata along with a blob you can use to save, preview, or otherwise process the file in your app.

Deleting a File

Use deleteFile() to permanently delete a file by its fileHiveId:

await synchive.deleteFile(uploaded.fileHiveId);

Deleting a file also deletes its associated metadata. This cannot be undone.

Uploading & Downloading with More Control

uploadFile() and downloadFile() are convenience wrappers around createUploadUrl() and createDownloadUrl(). Use these directly if you need more control over the file transfer, such as tracking upload progress with XMLHttpRequest's upload.onprogress.

const { fileHiveId, uploadUrl, headers } = await synchive.createUploadUrl({
fileName: file.name,
contentType: file.type,
});

// Upload the file bytes yourself, e.g. via XMLHttpRequest, using uploadUrl and headers.
const { downloadUrl, fileName, contentType } = await synchive.createDownloadUrl(
uploaded.fileHiveId,
);

// Fetch the file bytes yourself using downloadUrl.

Pass an existing fileHiveId to createUploadUrl() to replace that file's content, the same way you would with uploadFile().