gRPC API

ArcadeDB exposes a gRPC interface for high-performance, strongly-typed access from any language with gRPC support (Java, Python, Go, C++, Node.js, Rust, C#, and more). The gRPC API provides two services with 67 RPCs covering queries, record operations, transactions, graph bulk loading, vector and full-text search, time series, and server administration.

Proto definition: arcadedb-server.proto

Enabling the gRPC server

The gRPC module is bundled in the full distribution, but the server is implemented as a plugin (com.arcadedb.server.grpc.GrpcServerPlugin) that is not started by default. If the server is not listening on any gRPC port, it is because the plugin has not been registered yet.

Enable it by adding the plugin to the arcadedb.server.plugins setting, then start the server:

-Darcadedb.server.plugins=gRPC:com.arcadedb.server.grpc.GrpcServerPlugin

The format is <pluginName>:<pluginFullClass> (comma-separated when registering several plugins). Once enabled, the server listens on port 50051 by default. Confirm it started by looking for a gRPC server started line in the server log, or with grpcurl -plaintext localhost:50051 list.

Host, port, TLS, message size and the other gRPC options are the arcadedb.grpc. settings - see gRPC settings for the full list. Since v26.10.2 every one of them is a registered server setting, read like arcadedb.server.plugins from the server configuration file, from environment variables and from JVM system properties (-Darcadedb.grpc.). In v26.10.1 and earlier only arcadedb.grpc.port was; the others were read as JVM system properties only, so a grpc.tls.enabled written in the configuration file was ignored. (Since v26.10.2.) arcadedb.grpc.host binds the standard gRPC server to every local address the name resolves to (in v26.10.1 and earlier it was read but never applied, so the server listened on every interface); the xDS server (grpc.mode xds or both) has no host setting and still listens on every interface.

Services

ArcadeDbService

The main service for database operations: querying, record CRUD, bulk inserts, transactions, graph bulk loading, search, and time series.

RPC Request Response Description

StreamQuery

StreamQueryRequest

stream QueryResult

Execute a query and stream results back row by row

ExecuteCommand

ExecuteCommandRequest

ExecuteCommandResponse

Execute a DDL/DML command (CREATE, INSERT, UPDATE, DELETE)

ExecuteQuery

ExecuteQueryRequest

ExecuteQueryResponse

Execute a query and return all results at once

CreateRecord

CreateRecordRequest

CreateRecordResponse

Create a new record (vertex, edge, or document)

UpdateRecord

UpdateRecordRequest

UpdateRecordResponse

Update an existing record by RID

LookupByRid

LookupByRidRequest

LookupByRidResponse

Look up a record by its Record ID

DeleteRecord

DeleteRecordRequest

DeleteRecordResponse

Delete a record by RID

BulkInsert

BulkInsertRequest

InsertSummary

Insert multiple records in a single request

InsertStream

stream InsertChunk

InsertSummary

Client-streaming insert for large datasets

InsertBidirectional

stream InsertRequest

stream InsertResponse

Bidirectional streaming for insert with per-record feedback

BeginTransaction

BeginTransactionRequest

BeginTransactionResponse

Begin an explicit transaction

CommitTransaction

CommitTransactionRequest

CommitTransactionResponse

Commit the current transaction

RollbackTransaction

RollbackTransactionRequest

RollbackTransactionResponse

Roll back the current transaction

GraphBatchLoad

stream GraphBatchChunk

GraphBatchResult

Client-streaming bulk load of vertices and edges (see below)

VectorSearch

VectorSearchRequest

VectorSearchResponse

Since 26.10.1. k-nearest-neighbor search over a dense or sparse vector index

HybridSearch

HybridSearchRequest

HybridSearchResponse

Since 26.10.1. Fused vector, full-text, and graph-expansion search

FullTextSearch

FullTextSearchRequest

FullTextSearchResponse

Since 26.10.1. Lucene-syntax search over a FULL_TEXT index

TimeSeriesWrite

TimeSeriesWriteRequest

TimeSeriesWriteSummary

Since 26.10.1. Write a batch of time-series points

TimeSeriesWriteStream

stream TimeSeriesWriteChunk

TimeSeriesWriteSummary

Since 26.10.1. Client-streaming time-series ingest

TimeSeriesQuery

TimeSeriesQueryRequest

stream TimeSeriesQueryResult

Since 26.10.1. Range query, raw or aggregated into buckets, streamed back

TimeSeriesLatest

TimeSeriesLatestRequest

TimeSeriesLatestResponse

Since 26.10.1. Latest sample of a time-series type, optionally filtered by tags

A record that a BeforeRecordCreateListener or a BeforeRecordUpdateListener vetoed was refused on purpose, and the replies say so instead of reporting it as written. The bulk inserts (BulkInsert, InsertStream, InsertBidirectional) count it as ignored, and a vetoed update under CONFLICT_UPDATE does not insert a second record for the key. UpdateRecord answers success = false and updated = false, which is what the drivers return to the caller as "the record was updated". CreateRecord has to answer a RID, so it fails with INVALID_ARGUMENT naming the refusal.

Before v26.11.1 UpdateRecord answered success = true and updated = true for an update a listener vetoed, and CreateRecord of a vetoed edge failed with INTERNAL carrying the text of a NullPointerException.

Graph batch load

GraphBatchLoad streams GraphBatchChunk messages, each carrying GraphBatchRecord entries of kind VERTEX or EDGE. The first chunk must name the database and carry the GraphBatchOptions (batch size, light edges, WAL, commit frequency, vertex_batch_size, commit retries, and return_id_mapping); options on later chunks are ignored. Vertices declare a temp_id, and edges reference their endpoints by temp_id or by an existing RID (#bucket:pos) in from_ref / to_ref. All VERTEX records must precede every EDGE record across the whole stream: interleaving is an error. On a replicated cluster the load must run on the leader; a follower refuses it.

GraphBatchResult reports vertices_created, edges_created, elapsed_ms and, depending on return_id_mapping, the temp_id to RID map in id_mapping (when the map is not returned, id_mapping_omitted is set and id_mapping_size still reports its size). The load commits incrementally, so a failure is not a rollback: the counts already durable at the point of failure travel in the arcadedb-graph-batch-result-bin trailer of the failed call, with partial_commit set.

Since 26.10.1. These three unary RPCs run the same server-side implementation as the HTTP /api/v1/vector/* routes and the MCP search tools, so a request that is legal on one protocol is legal on all of them and is rejected with the same message. Every hit is a SearchHit with rid, record, and either distance (dense index, lower is better) or score (sparse, full-text, or fused, higher is better); the response’s scoring field says which.

RPC Key request fields

VectorSearch

index_name (an LSM_VECTOR index, or an LSM_SPARSE_VECTOR index when sparse is set), query_vector, query_indices (sparse dimension ids), k (1 to 1000, default 10), ef_search (dense only), filter (read-only SQL WHERE predicate applied to a bounded candidate window). ArcadeDB does not generate embeddings.

HybridSearch

The vector fields above (vector_index_name instead of index_name), plus an optional full-text leg (fulltext_query and fulltext_index_name, which must be given together), fusion_strategy (RRF by default, DBSF, or LINEAR), per-leg weights (keys vector, fulltext, expand), and an optional expand graph leg (edge_types, direction out/in/both, max_depth 1 to 3). The expand leg requires RRF.

FullTextSearch

query_text (Lucene syntax, not blank), the index addressed by index_name or by type_name plus optional properties (index_name wins when both are set), and limit (1 to 1000, default 10).

All three accept an optional transaction: when it names a live transaction the search reads exactly what that transaction reads, including its uncommitted changes. A transaction id the server no longer knows is refused with FAILED_PRECONDITION. See also Full-text index.

Time series

Since 26.10.1. The time-series RPCs read and write TIMESERIES types (see Time series).

  • TimeSeriesWrite / TimeSeriesWriteStream: each TimeSeriesPoint carries type (the measurement, defaulting to the request or chunk type), timestamp, and tags / fields maps of GrpcValue. precision defaults to TS_PRECISION_MILLISECONDS (unlike the HTTP line-protocol endpoint, which defaults to nanoseconds). The streaming variant needs database on the first chunk only and applies back-pressure, pulling one chunk at a time. A write is not atomic: each measurement’s batch commits as it is appended. TimeSeriesWriteSummary reports received, written, and dropped (written + dropped == received), and lists dropped points' types under unknown_types, non_time_series_types, or unavailable_types. (Since v26.10.2.) undeclared_keys (field 8) lists, as <measurement>.<key> (tag|field), the tag or field keys a measurement’s TIMESERIES type does not declare in that role: the points carrying them are dropped unless the database sets arcadedb.timeSeriesUndeclaredKeys=ignore. unreadable_values (field 9) lists, as <measurement>.<column> (tag|field): <reason>, values a column cannot store. Both lists are capped at 100 entries, while dropped still counts every point. The HTTP line-protocol endpoint reports the same lists as undeclaredKeys and unreadableValues in its 400 partial-write body, described by the TimeSeriesWriteError schema of the OpenAPI definition.

  • TimeSeriesQuery: type, inclusive from_timestamp / to_timestamp (unset means unbounded), fields projection (an unknown name is refused with INVALID_ARGUMENT), tags (TimeSeriesTagFilter, a conjunction of equality predicates on TAG columns; a non-TAG name is refused with INVALID_ARGUMENT), limit, batch_size (rows per streamed message, default 1000), and optional aggregation (bucket_interval_ms > 0, at least one TimeSeriesAggregationRequest of TS_AGG_SUM, AVG, MIN, MAX or COUNT, and an optional bucket_origin_ms). The answer streams as TimeSeriesQueryResult messages carrying rows (raw) or buckets (aggregated); the last message has last set, and truncated when the limit cut the answer short. The server-side ceiling arcadedb.server.grpcTimeSeriesMaxResultRows (default 1000000) cannot be raised by the client: a limit above it is refused with RESOURCE_EXHAUSTED before the first message.

  • TimeSeriesLatest: type and optional tags. TimeSeriesLatestResponse returns columns, found, and the latest row (unset when found is false).

TimeSeriesQuery and TimeSeriesLatest accept an optional transaction; when set, the read sees that transaction’s uncommitted points. An unresolvable transaction id is refused with FAILED_PRECONDITION (the HTTP time-series read routes instead fall back to reading outside the transaction).

ArcadeDbAdminService

Administration service for server and database management. Every admin RPC runs the same implementation as the HTTP control plane, so the two protocols behave the same way.

Authentication and authorization. Every request is authenticated from its credentials field, except Health and Ready, which are unauthenticated like GET /api/v1/health and GET /api/v1/ready. Ping, GetServerInfo, ListDatabases, ExistsDatabase, and GetDatabaseInfo need any valid account; GetProgress needs an account granted the named database; every other RPC requires the root user and otherwise fails with PERMISSION_DENIED.

Leader routing. On a replicated cluster, CreateDatabase, DropDatabase, CreateUser, UpdateUser, DeleteUser, SaveGroup, DeleteGroup, CreateApiToken, DeleteApiToken, RestoreBackup, RestoreDatabase, and ImportDatabase are refused on a follower with FAILED_PRECONDITION and the leader’s address in the arcadedb-leader- trailers. gRPC has no request proxy, so the client redirects itself. (Since v26.10.2.) The streaming inserts of ArcadeDbService (InsertStream and InsertBidirectional, and the rows of a BulkInsert) report a write a follower refused differently: the refused row or commit comes back as an InsertError with code NOT_LEADER and the leader’s address in leader_address, rather than through the arcadedb-leader- trailers (see Insert operations).

Server info and databases

RPC Request Response Description

Ping

PingRequest

PingResponse

Health check

GetServerInfo

GetServerInfoRequest

GetServerInfoResponse

Server version, uptime, and configuration

ListDatabases

ListDatabasesRequest

ListDatabasesResponse

List all databases

ExistsDatabase

ExistsDatabaseRequest

ExistsDatabaseResponse

Check if a database exists

CreateDatabase

CreateDatabaseRequest

CreateDatabaseResponse

Create a new database (see below)

DropDatabase

DropDatabaseRequest

DropDatabaseResponse

Drop an existing database (see below)

OpenDatabase

OpenDatabaseRequest

OpenDatabaseResponse

Since 26.10.1. Open the named database

CloseDatabase

CloseDatabaseRequest

CloseDatabaseResponse

Since 26.10.1. Close the named database’s open instance; the files stay, and a later request reopens it

AlignDatabase

AlignDatabaseRequest

AlignDatabaseResponse

Since 26.10.1. Run ALIGN DATABASE on the named database

GetDatabaseInfo

GetDatabaseInfoRequest

GetDatabaseInfoResponse

Schema, types, and database statistics

GetProgress

GetProgressRequest

GetProgressResponse

Since 26.10.1. Long-running operations (CHECK DATABASE, REBUILD INDEX, backup, import, …​) running on the database, as GET /api/v1/progress/{database}. Empty when nothing runs

GetDatabaseInfoResponse.records is the sum of the record counts of the database’s types, read from the bucket counters rather than by a scan. (Since v26.11.1) a type whose records cannot be counted - a time series type whose storage engine did not open, for one - is named in uncounted_types and left out of the sum, so a non-empty list says the total is short by an unknown amount rather than that the database holds fewer records; and a schema that cannot be read fails the call instead of answering zero classes (issue #10877).

CreateDatabase and DropDatabase answer the no-op case the way the HTTP create database and drop database commands do: a name already taken is refused with ALREADY_EXISTS, a name that does not exist with NOT_FOUND. Set if_not_exists on CreateDatabaseRequest, or if_exists on DropDatabaseRequest, to make the call idempotent instead. Either way the response says what happened: CreateDatabaseResponse.created is true only when this call created the database, and DropDatabaseResponse.dropped only when it dropped one, so a provisioning tool can tell "created for me" from "already there" rather than reading an empty OK as ownership. Database names are exact and never case-folded. The Java client exposes the two forms as createDatabase / createDatabaseIfMissing and dropDatabase / dropDatabaseIfExists; the IfMissing / IfExists variants return the boolean.

Users, groups, and API tokens

RPC Request Response Description

CreateUser

CreateUserRequest

CreateUserResponse

Create a new user. A name already taken is ALREADY_EXISTS (since 26.10.2; in 26.10.1 and earlier PERMISSION_DENIED, the status of an authorization refusal)

UpdateUser

UpdateUserRequest

UpdateUserResponse

Since 26.10.1. Update a user’s password and/or databases grants. Each field is optional and an absent one is left untouched; a present but empty databases clears the grants

DeleteUser

DeleteUserRequest

DeleteUserResponse

Delete a user

ListUsers

ListUsersRequest

ListUsersResponse

Since 26.10.1. List users with their per-database groups. Password hashes are never included

ListGroups

ListGroupsRequest

ListGroupsResponse

Since 26.10.1. The whole group document, as JSON in groups_json

SaveGroup

SaveGroupRequest

SaveGroupResponse

Since 26.10.1. Create or replace one group of a database (* for all) from group_json

DeleteGroup

DeleteGroupRequest

DeleteGroupResponse

Since 26.10.1. Delete one group of a database

ListApiTokens

ListApiTokensRequest

ListApiTokensResponse

Since 26.10.1. List API tokens as ApiTokenInfo (hash, last four characters, expiry, expired flag), never the token itself

CreateApiToken

CreateApiTokenRequest

CreateApiTokenResponse

Since 26.10.1. Mint an API token. The plaintext token is returned once only. Refused with FAILED_PRECONDITION unless the channel uses TLS or the client is on loopback; a duplicate name is ALREADY_EXISTS

DeleteApiToken

DeleteApiTokenRequest

DeleteApiTokenResponse

Since 26.10.1. Revoke a token by its token_hash (the plaintext token is not accepted)

Settings

RPC Request Response Description

SetServerSetting

SetServerSettingRequest

SetServerSettingResponse

Since 26.10.1. Set a server setting. key and value are separate fields, so a value with spaces needs no quoting

SetDatabaseSetting

SetDatabaseSettingRequest

SetDatabaseSettingResponse

Since 26.10.1. Set a setting of one database

Backup, restore, and import

RPC Request Response Description

GetBackupConfig

GetBackupConfigRequest

GetBackupConfigResponse

Since 26.10.1. Auto-backup configuration as JSON, and whether the auto-backup plugin is running

SetBackupConfig

SetBackupConfigRequest

SetBackupConfigResponse

Since 26.10.1. Save the auto-backup configuration from config_json

ListBackups

ListBackupsRequest

ListBackupsResponse

Since 26.10.1. Backup archives of a database, with retention totals

TriggerBackup

TriggerBackupRequest

TriggerBackupResponse

Since 26.10.1. Back up a database now; returns the archive path

DeleteBackup

DeleteBackupRequest

DeleteBackupResponse

Since 26.10.1. Delete one archive by plain file name (path separators and traversal are refused)

RestoreBackup

RestoreBackupRequest

stream RestoreProgress

Since 26.10.1. Restore a database’s backup archive into target_database; overwrite replaces an existing target only after the restore succeeded

RestoreDatabase

RestoreDatabaseRequest

stream RestoreProgress

Since 26.10.1. Create a new database from an archive at a url

ImportDatabase

ImportDatabaseRequest

stream ImportProgress

Since 26.10.1. Create a new database and import the source at a url into it; progress carries importer lines and parsed / vertices / edges counters. A name already taken is ALREADY_EXISTS (since 26.10.2; in 26.10.1 and earlier INVALID_ARGUMENT)

The three restore and import RPCs are server-streaming: they send progress messages while the operation runs, then one final message with completed = true (ImportProgress.result_json carries the importer’s final report). A failure ends the stream with an error status instead. Cancelling the call stops the progress messages, not the operation. Unless arcadedb.server.restoreImportAllowLocalUrls is enabled, only http/https URLs to non-private hosts are accepted. See also Backup.

Query profiler

RPC Request Response Description

ProfilerStart

ProfilerStartRequest

ProfilerStateResponse

Since 26.10.1. Start recording. timeout_seconds of 0 or less applies the server default; the effective timeout is returned

ProfilerStop

ProfilerStopRequest

ProfilerDocumentResponse

Since 26.10.1. Stop recording and return the run as JSON

ProfilerReset

ProfilerResetRequest

ProfilerStateResponse

Since 26.10.1. Reset the profiler

ProfilerResults

ProfilerResultsRequest

ProfilerDocumentResponse

Since 26.10.1. Current profiler results as JSON, without stopping

ProfilerList

ProfilerListRequest

ProfilerListResponse

Since 26.10.1. Saved profiler runs on disk, newest first

ProfilerLoad

ProfilerLoadRequest

ProfilerDocumentResponse

Since 26.10.1. Load a saved run by file name

Server lifecycle and cluster

RPC Request Response Description

GetServerEvents

GetServerEventsRequest

GetServerEventsResponse

Since 26.10.1. Server events as a JSON array, from the current event file or a named one, plus the list of event files

Shutdown

ShutdownRequest

ShutdownResponse

Since 26.10.1. Shut down this server (empty server_name) or the named HA peer. The local shutdown is scheduled asynchronously, so the response arrives first

DisconnectCluster

DisconnectClusterRequest

DisconnectClusterResponse

Since 26.10.1. Disconnect this server from the cluster. FAILED_PRECONDITION when the server does not run HA

AcceptDivergedDatabase

AcceptDivergedDatabaseRequest

AcceptDivergedDatabaseResponse

Since 26.10.2. Root only. Lifts the quarantine, and its read floor, standing on database of a node that is the only voter of its cluster (or whose every voter holds the same database quarantined), accepting the local copy as it is without a resync. The response carries local_server, applied_index, divergence_cause and read_floor. NOT_FOUND when nothing stands on the database, FAILED_PRECONDITION when the node has peers it can resync from or runs without HA, INVALID_ARGUMENT on a malformed name. The RPC twin of POST /api/v1/cluster/accept-diverged/{database}, see Lifting a quarantine without a resync

AcceptStaleSnapshot

AcceptStaleSnapshotRequest

AcceptStaleSnapshotResponse

Since 26.10.2. Root only. Lifts the node-wide stale-snapshot read floor of a node that is the only voter of its cluster, accepting its databases as they are without a resync. The response carries local_server, read_floor, snapshot_index and applied_index. NOT_FOUND when no floor stands, FAILED_PRECONDITION when the node has peers it can resync from, while a snapshot download runs, or without HA. The RPC twin of POST /api/v1/cluster/accept-stale-snapshot, see Lifting the stale-snapshot read floor

ConnectCluster

ConnectClusterRequest

ConnectClusterResponse

Since 26.10.1. Join the server at server_address (an arcadedb.ha.serverList entry) to the cluster. FAILED_PRECONDITION when HA is not running or cannot change membership at runtime; empty address is INVALID_ARGUMENT

ListSessions

ListSessionsRequest

ListSessionsResponse

Since 26.10.1. Open HTTP authentication sessions, including their bearer tokens. Enable TLS before calling it across an untrusted network

Health probes

RPC Request Response Description

Health

HealthRequest

HealthResponse

Since 26.10.1. Unauthenticated liveness probe; ok is false only when the HA layer escalated a crash loop

Ready

ReadyRequest

ReadyResponse

Since 26.10.1. Unauthenticated readiness probe; a not-ready node answers ready = false with a reason rather than an error status

Error statuses

A refused admin call answers the status of the refusal whatever the server wrapped it in on its way out: a name already taken is ALREADY_EXISTS, a user, group or token that does not exist NOT_FOUND, a security policy refusing an argument PERMISSION_DENIED, failed authentication UNAUTHENTICATED, a backup, restore or import already running on the database ABORTED, a malformed argument INVALID_ARGUMENT, an operation this server’s configuration does not support FAILED_PRECONDITION, and a transient failure such as a Raft quorum not reached UNAVAILABLE, which a client retries. The description is the refusal’s own sentence, also in production mode, except for a generic argument, authentication or retry failure raised underneath another error: that one keeps its status but its text is concealed in production mode, because it may come from a library and carry internal details. A statement stopped on request is CANCELLED, as on the data-plane RPCs (see Cancellation). Only an unexpected server fault is INTERNAL, and in production mode its description is concealed.

On the data-plane RPCs, the arcadedb-exception-class trailer names the exception that decided the status rather than the one wrapped around it, so the Java client rebuilds the engine’s own type: a SchemaException for a missing type (not the RecordNotFoundException that NOT_FOUND alone reads as), or a DuplicatedKeyException carrying its index name.

(Since v26.11.1) A command that creates, or renames onto, a schema object name already taken (a type, property, index, bucket, view, …​) answers ALREADY_EXISTS, with the arcadedb-already-exists-kind trailer naming the kind. The Java client rebuilds it as AlreadyExistsException, a SchemaException. Before, it was INTERNAL, or NOT_FOUND for some of them. The admin RPCs answer a taken user, token or database name as before, and the Java client raises RemoteAlreadyExistsException for it on every admin call, ImportDatabase included.

In v26.10.2 and earlier, only the leader-redirect refusal was recognised under a wrapper. Any other refusal the server raised under one (for example inside a commit or a backup) answered INTERNAL, and in production mode its sentence was replaced by the wrapper’s. A call that failed on a momentary loss of quorum was therefore not retried by the client. A search refused by the per-type ACL answered INTERNAL the same way. On the data plane, a wrapped duplicated key lost its index-name trailer, and a wrapped maintenance-slot conflict answered INTERNAL instead of ABORTED.

Cancellation and running statements

Since 26.10.2. The ArcadeDbService RPCs (such as ExecuteQuery, ExecuteCommand, StreamQuery, BulkInsert, the search RPCs and TimeSeriesQuery, but not BeginTransaction and RollbackTransaction) are listed, while they run, by the HTTP list queries server command and by Cypher SHOW TRANSACTIONS, with protocol grpc, and can be stopped by terminate query, TERMINATE TRANSACTIONS or Studio. A BulkInsert stops between two rows.

The client-streaming loads (InsertStream, InsertBidirectional, GraphBatchLoad and TimeSeriesWriteStream) are listed from the first chunk, which names the database, to the end of the stream, including while the stream is idle between two chunks, and terminate query stops them there too. A terminated load ends with status CANCELLED, and what the load already wrote depends on its transaction mode:

  • PER_STREAM and PER_REQUEST: the open transaction is rolled back, so nothing the stream sent is committed.

  • PER_BATCH: the open batch is rolled back; batches committed before the termination stay committed.

  • PER_ROW: the rows committed before the termination stay, the rest are not applied.

  • A load that writes into a client transaction (BeginTransaction) rolls that transaction back and ends it.

  • GraphBatchLoad commits incrementally: what is already durable is reported in the arcadedb-graph-batch-result-bin trailer with partial_commit set. TimeSeriesWriteStream stops appending; batches already appended stay.

In v26.10.1 and earlier the client-streaming loads were not listed and could not be terminated.

A client that cancels a call, or whose deadline expires, or whose connection drops, terminates the server-side work too: the server stops it at its next check instead of running it to the end for an answer nobody reads.

A terminated call ends with status CANCELLED and the trailer arcadedb-exception-class set to com.arcadedb.exception.QueryTerminatedException; the gRPC Java client raises that exception, which it does not retry. A statement terminated inside a client-managed transaction (BeginTransaction) ends that transaction: it is rolled back, and a later CommitTransaction answers committed = false.

Message Types

Connection & Authentication

DatabaseCredentials

Database name, username, and password for per-request authentication

Value Types

The GrpcValue message uses a oneof to represent any ArcadeDB value:

Field ArcadeDB Type

bool_value

Boolean

int32_value

Integer

int64_value

Long

float_value

Float

double_value

Double

string_value

String

bytes_value

Binary / byte array

timestamp_value

Datetime (google.protobuf.Timestamp)

list_value

List / array

map_value

Map / embedded document

link_value

RID reference (bucket:position)

decimal_value

Decimal (string-encoded)

null_value

Null

A GrpcValue can also carry a logical_type tag that says which ArcadeDB type it stands for. A client that ignores the tag still reads the plain string or timestamp.

logical_type Carried as

date, datetime

timestamp_value, anchored at UTC

zoned_datetime

string_value with the zone kept, for example 2026-01-01T00:30:00+01:00[Europe/Rome]

offset_time, local_time

string_value, ISO 8601 time of day

duration

string_value, ISO 8601 duration, for example P1Y2M3DT4H5M6.789S

Text tagged with one of these types that cannot be parsed is returned as a plain string.

GrpcRecord wraps a record with its RID, type name, and a map of property name to GrpcValue.

Enums

Enum Values

RetrievalMode

CURSOR (streaming), MATERIALIZE_ALL (all at once), PAGED (paginated)

ProjectionEncoding

PROJECTION_AS_LINK, PROJECTION_AS_MAP, PROJECTION_AS_JSON

TransactionIsolation

READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE

ConflictMode

CONFLICT_ERROR, CONFLICT_UPDATE, CONFLICT_IGNORE, CONFLICT_ABORT

TransactionMode

PER_REQUEST, PER_BATCH, PER_STREAM, PER_ROW, NONE

BeginTransaction accepts READ_COMMITTED and REPEATABLE_READ, the two levels the engine provides, and READ_UNCOMMITTED, which is the proto default and runs at the stronger READ_COMMITTED. The level the transaction really runs at is returned in BeginTransactionResponse.effective_isolation. SERIALIZABLE is not supported: it is refused with INVALID_ARGUMENT, as is a value the server does not know, instead of starting the transaction at a weaker level than the one asked for (since v26.11.1). HTTP, SQL, openCypher and PostgreSQL refuse it the same way.
PAGED retrieval keeps the ORDER BY your query specifies. When the query specifies none, rows are ordered by @rid, because cutting pages needs a stable order or a row could be skipped or repeated across pages. PAGED also reserves the parameter names _skip and _limit, and applies to SQL only: a query in another language falls back to CURSOR. If you order by a column with duplicate values, rows that tie can still move between pages, since every page re-runs the query; add a unique tie-break such as ORDER BY createdAt DESC, @rid when pages must be reproducible.

Insert Operations

BulkInsertRequest

Batch of records with type name, properties, and insert options

InsertChunk

A chunk of records for client-streaming inserts

InsertRequest / InsertResponse

Per-record request/response for bidirectional streaming

InsertSummary

Result summary: received, inserted, updated, ignored, failed, the errors list of InsertError, execution_time_ms, started_at and finished_at

InsertError

One failed row or commit, see below

InsertOptions

Transaction mode, conflict handling, and batch size configuration

A row that cannot be applied does not fail the call: it is counted in failed and reported as an InsertError, and the rest of the batch still goes in.

Field Number Meaning

message

1

The engine’s error text. In production server mode it is replaced by a generic message, see Error details in production mode

row_index

2

Index of the row within the chunk, or -1 when the error belongs to a transaction or a commit rather than to one row

code

3

Machine-readable code, see below

field

4

The offending field, when known

leader_address

5

Since 26.10.2. Set only when code is NOT_LEADER: the address of the cluster leader to send the load to

Code Meaning

CONFLICT

The row’s key is already taken (ConflictMode CONFLICT_ERROR or CONFLICT_ABORT), or a commit found a duplicated key

DB_ERROR

Any other failure of one row or chunk

NOT_LEADER

Since 26.10.2. The node is a follower and refused the write; leader_address names the leader. Before, a row refusal was DB_ERROR and a commit refusal COMMIT_FAILED

COMMIT_FAILED

A transaction commit failed for a reason other than a duplicated key or a leader refusal

CONTRACT_VIOLATION

The stream broke the protocol, for example its options changed mid-stream

UNKNOWN_TRANSACTION

The chunk named a transaction id the server does not know or has expired

INVALID_KEY_COLUMN

key_columns contains an empty name

MISSING_ENDPOINTS

An edge row has no out or no in

Command Results & Write Statistics

ExecuteCommand returns an ExecuteCommandResponse:

Field Meaning

success / message

Whether the command succeeded, and an optional message

affected_records

Server-computed count of affected elements and numeric scalars

execution_time_ms

Server-side execution time

records

Optional row payload, returned when return_rows is set on the request

stats

A QueryUpdateStats message, present only for write commands that mutated data

QueryUpdateStats carries the same write counters the HTTP stats object and the Bolt result summary expose, including counts aggregated across OpenCypher UNION branches and CALL { …​ } subqueries:

Field Meaning

nodes_created / nodes_deleted

Vertices created / deleted

relationships_created / relationships_deleted

Edges created / deleted

properties_set

Properties assigned a value

labels_added / labels_removed

Type/label memberships added / removed

indexes_added / indexes_removed

Indexes created / dropped

constraints_added / constraints_removed

Constraints added / removed

contains_updates

true when any counter above is non-zero

The equivalent HTTP response object is documented at Write statistics.

Connecting from Python

import grpc
import arcadedb_server_pb2 as pb
import arcadedb_server_pb2_grpc as rpc

channel = grpc.insecure_channel('localhost:50051')
stub = rpc.ArcadeDbServiceStub(channel)

# Execute a query
request = pb.ExecuteQueryRequest(
    credentials=pb.DatabaseCredentials(
        database='mydb', username='root', password='arcadedb'
    ),
    language='sql',
    command='SELECT FROM V LIMIT 10',
)
response = stub.ExecuteQuery(request)
for record in response.records:
    print(record)

Connecting from Node.js

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const packageDef = protoLoader.loadSync('arcadedb-server.proto');
const proto = grpc.loadPackageDefinition(packageDef).arcadedb;

const client = new proto.ArcadeDbService(
  'localhost:50051', grpc.credentials.createInsecure()
);

client.ExecuteQuery({
  credentials: { database: 'mydb', username: 'root', password: 'arcadedb' },
  language: 'sql',
  command: 'SELECT FROM V LIMIT 10',
}, (err, response) => {
  console.log(response.records);
});

Further Reading