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 65 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 configured through arcadedb.grpc. system properties - see gRPC settings for the full list. These plugin-level options are read as JVM system properties (-Darcadedb.grpc.) and, unlike arcadedb.server.plugins, are not resolved from environment variables.
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 |
|---|---|---|---|
|
|
stream |
Execute a query and stream results back row by row |
|
|
|
Execute a DDL/DML command (CREATE, INSERT, UPDATE, DELETE) |
|
|
|
Execute a query and return all results at once |
|
|
|
Create a new record (vertex, edge, or document) |
|
|
|
Update an existing record by RID |
|
|
|
Look up a record by its Record ID |
|
|
|
Delete a record by RID |
|
|
|
Insert multiple records in a single request |
|
stream |
|
Client-streaming insert for large datasets |
|
stream |
stream |
Bidirectional streaming for insert with per-record feedback |
|
|
|
Begin an explicit transaction |
|
|
|
Commit the current transaction |
|
|
|
Roll back the current transaction |
|
stream |
|
Client-streaming bulk load of vertices and edges (see below) |
|
|
|
Since 26.10.1. k-nearest-neighbor search over a dense or sparse vector index |
|
|
|
Since 26.10.1. Fused vector, full-text, and graph-expansion search |
|
|
|
Since 26.10.1. Lucene-syntax search over a |
|
|
|
Since 26.10.1. Write a batch of time-series points |
|
stream |
|
Since 26.10.1. Client-streaming time-series ingest |
|
|
stream |
Since 26.10.1. Range query, raw or aggregated into buckets, streamed back |
|
|
|
Since 26.10.1. Latest sample of a time-series type, optionally filtered by tags |
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.
Vector, hybrid, and full-text search
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 |
|---|---|
|
|
|
The vector fields above ( |
|
|
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: eachTimeSeriesPointcarriestype(the measurement, defaulting to the request or chunktype),timestamp, andtags/fieldsmaps ofGrpcValue.precisiondefaults toTS_PRECISION_MILLISECONDS(unlike the HTTP line-protocol endpoint, which defaults to nanoseconds). The streaming variant needsdatabaseon 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.TimeSeriesWriteSummaryreportsreceived,written, anddropped(written + dropped == received), and lists dropped points' types underunknown_types,non_time_series_types, orunavailable_types. -
TimeSeriesQuery:type, inclusivefrom_timestamp/to_timestamp(unset means unbounded),fieldsprojection (an unknown name is refused withINVALID_ARGUMENT),tags(TimeSeriesTagFilter, a conjunction of equality predicates on TAG columns; a non-TAG name is refused withINVALID_ARGUMENT),limit,batch_size(rows per streamed message, default 1000), and optionalaggregation(bucket_interval_ms> 0, at least oneTimeSeriesAggregationRequestofTS_AGG_SUM,AVG,MIN,MAXorCOUNT, and an optionalbucket_origin_ms). The answer streams asTimeSeriesQueryResultmessages carryingrows(raw) orbuckets(aggregated); the last message haslastset, andtruncatedwhen the limit cut the answer short. The server-side ceilingarcadedb.server.grpcTimeSeriesMaxResultRows(default 1000000) cannot be raised by the client: alimitabove it is refused withRESOURCE_EXHAUSTEDbefore the first message. -
TimeSeriesLatest:typeand optionaltags.TimeSeriesLatestResponsereturnscolumns,found, and thelatestrow (unset whenfoundisfalse).
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.
Server info and databases
| RPC | Request | Response | Description |
|---|---|---|---|
|
|
|
Health check |
|
|
|
Server version, uptime, and configuration |
|
|
|
List all databases |
|
|
|
Check if a database exists |
|
|
|
Create a new database (see below) |
|
|
|
Drop an existing database (see below) |
|
|
|
Since 26.10.1. Open the named database |
|
|
|
Since 26.10.1. Close the named database’s open instance; the files stay, and a later request reopens it |
|
|
|
Since 26.10.1. Run |
|
|
|
Schema, types, and database statistics |
|
|
|
Since 26.10.1. Long-running operations (CHECK DATABASE, REBUILD INDEX, backup, import, …) running on the database, as |
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 |
|---|---|---|---|
|
|
|
Create a new user |
|
|
|
Since 26.10.1. Update a user’s |
|
|
|
Delete a user |
|
|
|
Since 26.10.1. List users with their per-database groups. Password hashes are never included |
|
|
|
Since 26.10.1. The whole group document, as JSON in |
|
|
|
Since 26.10.1. Create or replace one group of a database ( |
|
|
|
Since 26.10.1. Delete one group of a database |
|
|
|
Since 26.10.1. List API tokens as |
|
|
|
Since 26.10.1. Mint an API token. The plaintext |
|
|
|
Since 26.10.1. Revoke a token by its |
Settings
| RPC | Request | Response | Description |
|---|---|---|---|
|
|
|
Since 26.10.1. Set a server setting. |
|
|
|
Since 26.10.1. Set a setting of one database |
Backup, restore, and import
| RPC | Request | Response | Description |
|---|---|---|---|
|
|
|
Since 26.10.1. Auto-backup configuration as JSON, and whether the auto-backup plugin is running |
|
|
|
Since 26.10.1. Save the auto-backup configuration from |
|
|
|
Since 26.10.1. Backup archives of a database, with retention totals |
|
|
|
Since 26.10.1. Back up a database now; returns the archive path |
|
|
|
Since 26.10.1. Delete one archive by plain file name (path separators and traversal are refused) |
|
|
stream |
Since 26.10.1. Restore a database’s backup archive into |
|
|
stream |
Since 26.10.1. Create a new database from an archive at a |
|
|
stream |
Since 26.10.1. Create a new database and import the source at a |
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 |
|---|---|---|---|
|
|
|
Since 26.10.1. Start recording. |
|
|
|
Since 26.10.1. Stop recording and return the run as JSON |
|
|
|
Since 26.10.1. Reset the profiler |
|
|
|
Since 26.10.1. Current profiler results as JSON, without stopping |
|
|
|
Since 26.10.1. Saved profiler runs on disk, newest first |
|
|
|
Since 26.10.1. Load a saved run by file name |
Server lifecycle and cluster
| RPC | Request | Response | Description |
|---|---|---|---|
|
|
|
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 |
|
|
|
Since 26.10.1. Shut down this server (empty |
|
|
|
Since 26.10.1. Disconnect this server from the cluster |
|
|
|
Since 26.10.1. Join the server at |
|
|
|
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 |
|---|---|---|---|
|
|
|
Since 26.10.1. Unauthenticated liveness probe; |
|
|
|
Since 26.10.1. Unauthenticated readiness probe; a not-ready node answers |
Message Types
Connection & Authentication
|
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 |
|---|---|
|
Boolean |
|
Integer |
|
Long |
|
Float |
|
Double |
|
String |
|
Binary / byte array |
|
Datetime (google.protobuf.Timestamp) |
|
List / array |
|
Map / embedded document |
|
RID reference (bucket:position) |
|
Decimal (string-encoded) |
|
Null |
GrpcRecord wraps a record with its RID, type name, and a map of property name to GrpcValue.
Enums
| Enum | Values |
|---|---|
|
|
|
|
|
|
|
|
|
|
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
|
Batch of records with type name, properties, and insert options |
|
A chunk of records for client-streaming inserts |
|
Per-record request/response for bidirectional streaming |
|
Result summary: total inserted, errors, duration |
|
Transaction mode, conflict handling, and batch size configuration |
Command Results & Write Statistics
ExecuteCommand returns an ExecuteCommandResponse:
| Field | Meaning |
|---|---|
|
Whether the command succeeded, and an optional message |
|
Server-computed count of affected elements and numeric scalars |
|
Server-side execution time |
|
Optional row payload, returned when |
|
A |
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 |
|---|---|
|
Vertices created / deleted |
|
Edges created / deleted |
|
Properties assigned a value |
|
Type/label memberships added / removed |
|
Indexes created / dropped |
|
Constraints added / removed |
|
|
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
-
HTTP/JSON API — Alternative REST-based access
-
PostgreSQL Protocol — Alternative wire protocol