This is the abridged developer documentation for Actionbase # Actionbase > A database for serving user interactions. Write-Time Materialization Pre-compute the data required for fast, predictable reads at write time, eliminating service-specific indexing and counting logic. Interaction-Oriented Graph Model Represent user interactions as actor→target edges with schema-defined properties for efficient querying. Unified REST API Expose a simple, storage-agnostic API for querying and mutating interaction data. WAL / CDC Integration Emit write-ahead logs and change data capture streams for recovery, replay, asynchronous processing, and downstream data pipelines. # Metadata API Reference > Metadata management API reference (v2) > **Note:** Currently, metadata APIs use **v2**. Mutation and query operations have transitioned to v3, but metadata v3 support is planned for the future. > **Note:** Authentication is not yet implemented. The `Authorization` header is reserved for future use. Metadata APIs allow you to manage the structure and configuration of graph data in Actionbase, including services (v3: databases), labels (v3: tables), aliases, and queries. For conceptual understanding, see [Schema](/design/schema/). ## 1. Service [Section titled “1. Service”](#1-service) Services are logical groups that contain labels and aliases, providing organization and isolation for related graph structures. ### 1.1 Get Service [Section titled “1.1 Get Service”](#11-get-service) Retrieve a single service by name. #### Endpoint [Section titled “Endpoint”](#endpoint) ```plaintext GET /graph/v2/service/{service} ``` #### Parameters [Section titled “Parameters”](#parameters) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | #### Response Type [Section titled “Response Type”](#response-type) [ServiceEntity](#serviceentity) - Service information #### Request Example [Section titled “Request Example”](#request-example) | Parameter | Value | | ------------- | -------------- | | Authorization | YOUR\_API\_KEY | | service | myservice | ```bash # GET /graph/v2/service/myservice curl -X GET \ "http://ab.example.com/graph/v2/service/myservice" \ -H "Authorization: YOUR_API_KEY" ``` #### Response Example [Section titled “Response Example”](#response-example) ```json { "active": true, "name": "myservice", "desc": "My service description" } ``` ### 1.2 Get All Services [Section titled “1.2 Get All Services”](#12-get-all-services) Retrieve all services. #### Endpoint [Section titled “Endpoint”](#endpoint-1) ```plaintext GET /graph/v2/service ``` #### Parameters [Section titled “Parameters”](#parameters-1) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | #### Response Type [Section titled “Response Type”](#response-type-1) [DdlPage](#ddlpage) of [ServiceEntity](#serviceentity) - Paginated list of services #### Request Example [Section titled “Request Example”](#request-example-1) ```bash # GET /graph/v2/service curl -X GET \ "http://ab.example.com/graph/v2/service" \ -H "Authorization: YOUR_API_KEY" ``` #### Response Example [Section titled “Response Example”](#response-example-1) ```json { "count": 2, "content": [ { "active": true, "name": "myservice", "desc": "My service description" }, { "active": true, "name": "anotherservice", "desc": "Another service" } ] } ``` ### 1.3 Create Service [Section titled “1.3 Create Service”](#13-create-service) Create a new service. #### Endpoint [Section titled “Endpoint”](#endpoint-2) ```plaintext POST /graph/v2/service/{service} ``` #### Parameters [Section titled “Parameters”](#parameters-2) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Body | desc | Required | Service description | #### Request Body [Section titled “Request Body”](#request-body) [ServiceCreateRequest](#servicecreaterequest) - Service creation payload #### Response Type [Section titled “Response Type”](#response-type-2) [DdlStatus](#ddlstatus) of [ServiceEntity](#serviceentity) - Service creation status #### Request Example [Section titled “Request Example”](#request-example-2) ```bash # POST /graph/v2/service/myservice curl -X POST \ "http://ab.example.com/graph/v2/service/myservice" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "desc": "My service description" }' ``` #### Response Example [Section titled “Response Example”](#response-example-2) ```json { "status": "CREATED", "result": { "active": true, "name": "myservice", "desc": "My service description" } } ``` ### 1.4 Update Service [Section titled “1.4 Update Service”](#14-update-service) Update an existing service. #### Endpoint [Section titled “Endpoint”](#endpoint-3) ```plaintext PUT /graph/v2/service/{service} ``` #### Parameters [Section titled “Parameters”](#parameters-3) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Body | active | Optional | Whether the service is active | | Body | desc | Optional | Service description | #### Request Body [Section titled “Request Body”](#request-body-1) [ServiceUpdateRequest](#serviceupdaterequest) - Service update payload #### Response Type [Section titled “Response Type”](#response-type-3) [DdlStatus](#ddlstatus) of [ServiceEntity](#serviceentity) - Service update status #### Request Example [Section titled “Request Example”](#request-example-3) ```bash # PUT /graph/v2/service/myservice curl -X PUT \ "http://ab.example.com/graph/v2/service/myservice" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "active": true, "desc": "Updated service description" }' ``` ## 2. Label [Section titled “2. Label”](#2-label) Labels define the schema for graph edges, specifying the structure of relationships, including source and target node types, edge properties, and indexes. ### 2.1 Get Label [Section titled “2.1 Get Label”](#21-get-label) Retrieve a single label by name. #### Endpoint [Section titled “Endpoint”](#endpoint-4) ```plaintext GET /graph/v2/service/{service}/label/{label} ``` #### Parameters [Section titled “Parameters”](#parameters-4) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | label | Required | Label name | #### Response Type [Section titled “Response Type”](#response-type-4) [LabelEntity](#labelentity) - Label information #### Request Example [Section titled “Request Example”](#request-example-4) ```bash # GET /graph/v2/service/myservice/label/follows curl -X GET \ "http://ab.example.com/graph/v2/service/myservice/label/follows" \ -H "Authorization: YOUR_API_KEY" ``` #### Response Example [Section titled “Response Example”](#response-example-3) ```json { "active": true, "name": "myservice.follows", "desc": "User follows relationship", "type": "HASH", "schema": { "src": { "type": "LONG", "desc": "User ID" }, "tgt": { "type": "LONG", "desc": "User ID" }, "fields": [ { "name": "created_at", "type": "LONG", "nullable": false, "desc": "Creation timestamp" } ] }, "dirType": "OUT", "storage": "hbase-storage-1", "indices": [], "groups": [], "event": false, "readOnly": false, "mode": "SYNC" } ``` ### 2.2 Get All Labels [Section titled “2.2 Get All Labels”](#22-get-all-labels) Retrieve all labels in a service. #### Endpoint [Section titled “Endpoint”](#endpoint-5) ```plaintext GET /graph/v2/service/{service}/label ``` #### Parameters [Section titled “Parameters”](#parameters-5) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | #### Response Type [Section titled “Response Type”](#response-type-5) [DdlPage](#ddlpage) of [LabelEntity](#labelentity) - Paginated list of labels #### Request Example [Section titled “Request Example”](#request-example-5) ```bash # GET /graph/v2/service/myservice/label curl -X GET \ "http://ab.example.com/graph/v2/service/myservice/label" \ -H "Authorization: YOUR_API_KEY" ``` ### 2.3 Create Label [Section titled “2.3 Create Label”](#23-create-label) Create a new label. #### Endpoint [Section titled “Endpoint”](#endpoint-6) ```plaintext POST /graph/v2/service/{service}/label/{label} ``` #### Parameters [Section titled “Parameters”](#parameters-6) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | ----------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | label | Required | Label name | | Body | desc | Required | Label description | | Body | type | Required | Label type (HASH, INDEXED, MULTI\_EDGE) | | Body | schema | Required | Edge schema definition | | Body | dirType | Required | Direction type (OUT, IN, UNDIRECTED) | | Body | storage | Required | Storage name | | Body | groups | Optional | List of groups | | Body | indices | Optional | List of indexes | | Body | event | Optional | Whether events are enabled (default: false) | | Body | readOnly | Optional | Whether the label is read-only (default: false) | | Body | mode | Optional | Mutation mode (default: SYNC) | #### Request Body [Section titled “Request Body”](#request-body-2) [LabelCreateRequest](#labelcreaterequest) - Label creation payload #### Response Type [Section titled “Response Type”](#response-type-6) [DdlStatus](#ddlstatus) of [LabelEntity](#labelentity) - Label creation status #### Request Example [Section titled “Request Example”](#request-example-6) ```bash # POST /graph/v2/service/myservice/label/follows curl -X POST \ "http://ab.example.com/graph/v2/service/myservice/label/follows" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "desc": "User follows relationship", "type": "HASH", "schema": { "src": { "type": "LONG", "desc": "User ID" }, "tgt": { "type": "LONG", "desc": "User ID" }, "fields": [ { "name": "created_at", "type": "LONG", "nullable": false, "desc": "Creation timestamp" } ] }, "dirType": "OUT", "storage": "hbase-storage-1", "indices": [], "groups": [], "event": false, "readOnly": false, "mode": "SYNC" }' ``` ### 2.4 Update Label [Section titled “2.4 Update Label”](#24-update-label) Update an existing label. #### Endpoint [Section titled “Endpoint”](#endpoint-7) ```plaintext PUT /graph/v2/service/{service}/label/{label} ``` #### Parameters [Section titled “Parameters”](#parameters-7) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | label | Required | Label name | | Body | active | Optional | Whether the label is active | | Body | desc | Optional | Label description | | Body | type | Optional | Label type | | Body | schema | Optional | Edge schema definition | | Body | groups | Optional | List of groups | | Body | indices | Optional | List of indexes | | Body | readOnly | Optional | Whether the label is read-only | | Body | mode | Optional | Mutation mode | #### Request Body [Section titled “Request Body”](#request-body-3) [LabelUpdateRequest](#labelupdaterequest) - Label update payload #### Response Type [Section titled “Response Type”](#response-type-7) [DdlStatus](#ddlstatus) of [LabelEntity](#labelentity) - Label update status #### Request Example [Section titled “Request Example”](#request-example-7) ```bash # PUT /graph/v2/service/myservice/label/follows curl -X PUT \ "http://ab.example.com/graph/v2/service/myservice/label/follows" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "active": true, "desc": "Updated label description" }' ``` ### 2.5 Copy Label [Section titled “2.5 Copy Label”](#25-copy-label) Copy an existing label to create a new label. #### Endpoint [Section titled “Endpoint”](#endpoint-8) ```plaintext POST /graph/v2/service/{service}/label/{label}/copy ``` #### Parameters [Section titled “Parameters”](#parameters-8) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | label | Required | Source label name | | Body | target | Required | Target label name | | Body | storage | Required | Storage name for the new label | #### Request Body [Section titled “Request Body”](#request-body-4) [LabelCopyRequest](#labelcopyrequest) - Label copy payload #### Response Type [Section titled “Response Type”](#response-type-8) [DdlStatus](#ddlstatus) of [LabelEntity](#labelentity) - Label creation status #### Request Example [Section titled “Request Example”](#request-example-8) ```bash # POST /graph/v2/service/myservice/label/follows/copy curl -X POST \ "http://ab.example.com/graph/v2/service/myservice/label/follows/copy" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "target": "follows_backup", "storage": "hbase-storage-1" }' ``` ### 2.6 Get Label Status [Section titled “2.6 Get Label Status”](#26-get-label-status) Get the status of a label. #### Endpoint [Section titled “Endpoint”](#endpoint-9) ```plaintext GET /graph/v2/service/{service}/label/{label}/status ``` #### Parameters [Section titled “Parameters”](#parameters-9) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | label | Required | Label name | #### Response Type [Section titled “Response Type”](#response-type-9) String - Label status information #### Request Example [Section titled “Request Example”](#request-example-9) ```bash # GET /graph/v2/service/myservice/label/follows/status curl -X GET \ "http://ab.example.com/graph/v2/service/myservice/label/follows/status" \ -H "Authorization: YOUR_API_KEY" ``` ## 3. Alias [Section titled “3. Alias”](#3-alias) Aliases are references that point to labels, enabling alternative names for the same label. ### 3.1 Get Alias [Section titled “3.1 Get Alias”](#31-get-alias) Retrieve a single alias by name. #### Endpoint [Section titled “Endpoint”](#endpoint-10) ```plaintext GET /graph/v2/service/{service}/alias/{alias} ``` #### Parameters [Section titled “Parameters”](#parameters-10) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | alias | Required | Alias name | #### Response Type [Section titled “Response Type”](#response-type-10) [AliasEntity](#aliasentity) - Alias information #### Request Example [Section titled “Request Example”](#request-example-10) ```bash # GET /graph/v2/service/myservice/alias/friends curl -X GET \ "http://ab.example.com/graph/v2/service/myservice/alias/friends" \ -H "Authorization: YOUR_API_KEY" ``` #### Response Example [Section titled “Response Example”](#response-example-4) ```json { "active": true, "name": "myservice.friends", "desc": "Alias for follows relationship", "target": "myservice.follows", "label": { "active": true, "name": "myservice.follows", "desc": "User follows relationship", "type": "HASH", "schema": { ... }, "dirType": "OUT", "storage": "hbase-storage-1" } } ``` ### 3.2 Get All Aliases [Section titled “3.2 Get All Aliases”](#32-get-all-aliases) Retrieve all aliases in a service. #### Endpoint [Section titled “Endpoint”](#endpoint-11) ```plaintext GET /graph/v2/service/{service}/alias ``` #### Parameters [Section titled “Parameters”](#parameters-11) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | #### Response Type [Section titled “Response Type”](#response-type-11) [DdlPage](#ddlpage) of [AliasEntity](#aliasentity) - Paginated list of aliases #### Request Example [Section titled “Request Example”](#request-example-11) ```bash # GET /graph/v2/service/myservice/alias curl -X GET \ "http://ab.example.com/graph/v2/service/myservice/alias" \ -H "Authorization: YOUR_API_KEY" ``` ### 3.3 Create Alias [Section titled “3.3 Create Alias”](#33-create-alias) Create a new alias. #### Endpoint [Section titled “Endpoint”](#endpoint-12) ```plaintext POST /graph/v2/service/{service}/alias/{alias} ``` #### Parameters [Section titled “Parameters”](#parameters-12) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | alias | Required | Alias name | | Body | desc | Required | Alias description | | Body | target | Required | Target label name (format: service.label) | #### Request Body [Section titled “Request Body”](#request-body-5) [AliasCreateRequest](#aliascreaterequest) - Alias creation payload #### Response Type [Section titled “Response Type”](#response-type-12) [DdlStatus](#ddlstatus) of [AliasEntity](#aliasentity) - Alias creation status #### Request Example [Section titled “Request Example”](#request-example-12) ```bash # POST /graph/v2/service/myservice/alias/friends curl -X POST \ "http://ab.example.com/graph/v2/service/myservice/alias/friends" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "desc": "Alias for follows relationship", "target": "myservice.follows" }' ``` ### 3.4 Update Alias [Section titled “3.4 Update Alias”](#34-update-alias) Update an existing alias. #### Endpoint [Section titled “Endpoint”](#endpoint-13) ```plaintext PUT /graph/v2/service/{service}/alias/{alias} ``` #### Parameters [Section titled “Parameters”](#parameters-13) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | alias | Required | Alias name | | Body | active | Optional | Whether the alias is active | | Body | desc | Optional | Alias description | | Body | target | Optional | Target label name | #### Request Body [Section titled “Request Body”](#request-body-6) [AliasUpdateRequest](#aliasupdaterequest) - Alias update payload #### Response Type [Section titled “Response Type”](#response-type-13) [DdlStatus](#ddlstatus) of [AliasEntity](#aliasentity) - Alias update status #### Request Example [Section titled “Request Example”](#request-example-13) ```bash # PUT /graph/v2/service/myservice/alias/friends curl -X PUT \ "http://ab.example.com/graph/v2/service/myservice/alias/friends" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "active": true, "desc": "Updated alias description" }' ``` ### 3.5 Delete Alias [Section titled “3.5 Delete Alias”](#35-delete-alias) Delete an alias. #### Endpoint [Section titled “Endpoint”](#endpoint-14) ```plaintext DELETE /graph/v2/service/{service}/alias/{alias} ``` #### Parameters [Section titled “Parameters”](#parameters-14) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | alias | Required | Alias name | #### Response Type [Section titled “Response Type”](#response-type-14) [DdlStatus](#ddlstatus) of [AliasEntity](#aliasentity) - Alias deletion status #### Request Example [Section titled “Request Example”](#request-example-14) ```bash # DELETE /graph/v2/service/myservice/alias/friends curl -X DELETE \ "http://ab.example.com/graph/v2/service/myservice/alias/friends" \ -H "Authorization: YOUR_API_KEY" ``` ### 3.6 Create Label from Alias [Section titled “3.6 Create Label from Alias”](#36-create-label-from-alias) Create a new label based on the label that an alias points to. #### Endpoint [Section titled “Endpoint”](#endpoint-15) ```plaintext POST /graph/v2/service/{service}/alias/{alias}/new-label ``` #### Parameters [Section titled “Parameters”](#parameters-15) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | alias | Required | Alias name | | Body | target | Required | Target label name for the new label | | Body | storage | Required | Storage name for the new label | #### Request Body [Section titled “Request Body”](#request-body-7) [LabelCopyRequest](#labelcopyrequest) - Label creation payload #### Response Type [Section titled “Response Type”](#response-type-15) [DdlStatus](#ddlstatus) of [LabelEntity](#labelentity) - Label creation status #### Request Example [Section titled “Request Example”](#request-example-15) ```bash # POST /graph/v2/service/myservice/alias/friends/new-label curl -X POST \ "http://ab.example.com/graph/v2/service/myservice/alias/friends/new-label" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "target": "follows_new", "storage": "hbase-storage-1" }' ``` ## 4. Query [Section titled “4. Query”](#4-query) Queries define reusable query patterns for graph operations. ### 4.1 Get Query [Section titled “4.1 Get Query”](#41-get-query) Retrieve a single query by name. #### Endpoint [Section titled “Endpoint”](#endpoint-16) ```plaintext GET /graph/v2/service/{service}/query/{query} ``` #### Parameters [Section titled “Parameters”](#parameters-16) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | query | Required | Query name | #### Response Type [Section titled “Response Type”](#response-type-16) [QueryEntity](#queryentity) - Query information #### Request Example [Section titled “Request Example”](#request-example-16) ```bash # GET /graph/v2/service/myservice/query/user-followers curl -X GET \ "http://ab.example.com/graph/v2/service/myservice/query/user-followers" \ -H "Authorization: YOUR_API_KEY" ``` ### 4.2 Get All Queries [Section titled “4.2 Get All Queries”](#42-get-all-queries) Retrieve all queries in a service. #### Endpoint [Section titled “Endpoint”](#endpoint-17) ```plaintext GET /graph/v2/service/{service}/query ``` #### Parameters [Section titled “Parameters”](#parameters-17) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | #### Response Type [Section titled “Response Type”](#response-type-17) [DdlPage](#ddlpage) of [QueryEntity](#queryentity) - Paginated list of queries #### Request Example [Section titled “Request Example”](#request-example-17) ```bash # GET /graph/v2/service/myservice/query curl -X GET \ "http://ab.example.com/graph/v2/service/myservice/query" \ -H "Authorization: YOUR_API_KEY" ``` ### 4.3 Create Query [Section titled “4.3 Create Query”](#43-create-query) Create a new query. #### Endpoint [Section titled “Endpoint”](#endpoint-18) ```plaintext POST /graph/v2/service/{service}/query/{query} ``` #### Parameters [Section titled “Parameters”](#parameters-18) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | query | Required | Query name | | Body | desc | Required | Query description | | Body | query | Required | Query definition | | Body | stats | Optional | Statistics configuration | #### Request Body [Section titled “Request Body”](#request-body-8) [QueryCreateRequest](#querycreaterequest) - Query creation payload #### Response Type [Section titled “Response Type”](#response-type-18) [DdlStatus](#ddlstatus) of [QueryEntity](#queryentity) - Query creation status #### Request Example [Section titled “Request Example”](#request-example-18) ```bash # POST /graph/v2/service/myservice/query/user-followers curl -X POST \ "http://ab.example.com/graph/v2/service/myservice/query/user-followers" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "desc": "Get user followers", "query": "SELECT * FROM myservice.follows WHERE src = ?", "stats": null }' ``` ### 4.4 Update Query [Section titled “4.4 Update Query”](#44-update-query) Update an existing query. #### Endpoint [Section titled “Endpoint”](#endpoint-19) ```plaintext PUT /graph/v2/service/{service}/query/{query} ``` #### Parameters [Section titled “Parameters”](#parameters-19) | Location | Parameter | Required | Description | | -------- | ------------- | -------- | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | service | Required | Service name | | Path | query | Required | Query name | | Body | active | Optional | Whether the query is active | | Body | desc | Optional | Query description | | Body | query | Optional | Query definition | | Body | stats | Optional | Statistics configuration | #### Request Body [Section titled “Request Body”](#request-body-9) [QueryUpdateRequest](#queryupdaterequest) - Query update payload #### Response Type [Section titled “Response Type”](#response-type-19) [DdlStatus](#ddlstatus) of [QueryEntity](#queryentity) - Query update status #### Request Example [Section titled “Request Example”](#request-example-19) ```bash # PUT /graph/v2/service/myservice/query/user-followers curl -X PUT \ "http://ab.example.com/graph/v2/service/myservice/query/user-followers" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "desc": "Updated query description", "query": "SELECT * FROM myservice.follows WHERE src = ? LIMIT 100" }' ``` ## Data Model [Section titled “Data Model”](#data-model) ### ServiceEntity [Section titled “ServiceEntity”](#serviceentity) Service information payload. ```kotlin data class ServiceEntity( val active: Boolean, // Whether the service is active val name: String, // Service name val desc: String, // Service description ) ``` ### LabelEntity [Section titled “LabelEntity”](#labelentity) Label information payload. ```kotlin data class LabelEntity( val active: Boolean, // Whether the label is active val name: String, // Label name (format: service.label) val desc: String, // Label description val type: LabelType, // Label type (HASH, INDEXED, MULTI_EDGE) val schema: EdgeSchema, // Edge schema definition val dirType: DirectionType, // Direction type (OUT, IN, UNDIRECTED) val storage: String, // Storage name val indices: List, // List of indexes val groups: List, // List of groups val event: Boolean, // Whether events are enabled val readOnly: Boolean, // Whether the label is read-only val mode: MutationMode, // Mutation mode ) ``` ### AliasEntity [Section titled “AliasEntity”](#aliasentity) Alias information payload. ```kotlin data class AliasEntity( val active: Boolean, // Whether the alias is active val name: String, // Alias name (format: service.alias) val desc: String, // Alias description val target: String, // Target label name (format: service.label) val label: LabelEntity?, // Referenced label information (optional) ) ``` ### QueryEntity [Section titled “QueryEntity”](#queryentity) Query information payload. ```kotlin data class QueryEntity( val active: Boolean, // Whether the query is active val name: String, // Query name (format: service.query) val desc: String, // Query description val query: String, // Query definition val stats: String?, // Statistics configuration (optional) ) ``` ### DdlPage [Section titled “DdlPage”](#ddlpage) Paginated response payload. ```kotlin data class DdlPage<T>( val count: Long, // Total count val content: List<T>, // List of items ) ``` ### DdlStatus [Section titled “DdlStatus”](#ddlstatus) DDL operation status payload. ```kotlin data class DdlStatus<T>( val status: String, // Operation status (e.g., CREATED, UPDATED, DELETED) val result: T?, // Result entity (optional) ) ``` ### ServiceCreateRequest [Section titled “ServiceCreateRequest”](#servicecreaterequest) Service creation request payload. ```kotlin data class ServiceCreateRequest( val desc: String, // Service description ) ``` ### ServiceUpdateRequest [Section titled “ServiceUpdateRequest”](#serviceupdaterequest) Service update request payload. ```kotlin data class ServiceUpdateRequest( val active: Boolean?, // Whether the service is active (optional) val desc: String?, // Service description (optional) ) ``` ### LabelCreateRequest [Section titled “LabelCreateRequest”](#labelcreaterequest) Label creation request payload. ```kotlin data class LabelCreateRequest( val desc: String, // Label description val type: LabelType, // Label type val schema: EdgeSchema, // Edge schema definition val dirType: DirectionType, // Direction type val storage: String, // Storage name val groups: List?, // List of groups (optional) val indices: List?, // List of indexes (optional) val event: Boolean?, // Whether events are enabled (optional, default: false) val readOnly: Boolean?, // Whether the label is read-only (optional, default: false) val mode: MutationMode?, // Mutation mode (optional, default: SYNC) ) ``` ### LabelUpdateRequest [Section titled “LabelUpdateRequest”](#labelupdaterequest) Label update request payload. ```kotlin data class LabelUpdateRequest( val active: Boolean?, // Whether the label is active (optional) val desc: String?, // Label description (optional) val type: LabelType?, // Label type (optional) val schema: EdgeSchema?, // Edge schema definition (optional) val groups: List?, // List of groups (optional) val indices: List?, // List of indexes (optional) val readOnly: Boolean?, // Whether the label is read-only (optional) val mode: MutationMode?, // Mutation mode (optional) ) ``` ### LabelCopyRequest [Section titled “LabelCopyRequest”](#labelcopyrequest) Label copy request payload. ```kotlin data class LabelCopyRequest( val target: String, // Target label name val storage: String, // Storage name for the new label ) ``` ### AliasCreateRequest [Section titled “AliasCreateRequest”](#aliascreaterequest) Alias creation request payload. ```kotlin data class AliasCreateRequest( val desc: String, // Alias description val target: String, // Target label name (format: service.label) ) ``` ### AliasUpdateRequest [Section titled “AliasUpdateRequest”](#aliasupdaterequest) Alias update request payload. ```kotlin data class AliasUpdateRequest( val active: Boolean?, // Whether the alias is active (optional) val desc: String?, // Alias description (optional) val target: String?, // Target label name (optional) ) ``` ### QueryCreateRequest [Section titled “QueryCreateRequest”](#querycreaterequest) Query creation request payload. ```kotlin data class QueryCreateRequest( val desc: String, // Query description val query: String, // Query definition val stats: String?, // Statistics configuration (optional) ) ``` ### QueryUpdateRequest [Section titled “QueryUpdateRequest”](#queryupdaterequest) Query update request payload. ```kotlin data class QueryUpdateRequest( val active: Boolean?, // Whether the query is active (optional) val desc: String?, // Query description (optional) val query: String?, // Query definition (optional) val stats: String?, // Statistics configuration (optional) ) ``` # Mutation API Reference > Edge mutation API reference This API provides mutation operations for edges. > **Note:** Authentication is not yet implemented. The `Authorization` header is reserved for future use. For conceptual understanding, see [Mutation](/design/mutation/). ## 1. Edge Mutation [Section titled “1. Edge Mutation”](#1-edge-mutation) Mutate edges between source nodes and target nodes. ### Endpoint [Section titled “Endpoint”](#endpoint) ```plaintext POST /graph/v3/databases/{database}/tables/{table}/edges ``` ### Parameters [Section titled “Parameters”](#parameters) | Location | Parameter | Required | Description | | -------- | ------------- | ------------------------ | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | database | Required | Target database name | | Path | table | Required | Target table name | | Query | lock | Optional (default: true) | Whether to acquire lock during mutation | | Body | mutations | Required | List of mutation items | ### Request Body [Section titled “Request Body”](#request-body) [EdgeBulkMutationRequest](#edgebulkmutationrequest) - Payload containing mutation items ### Response Type [Section titled “Response Type”](#response-type) [EdgeMutationResponse](#edgemutationresponse) - Payload containing mutation results ### Request Example [Section titled “Request Example”](#request-example) | Parameter | Value | | ------------- | -------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | lock | true | ```bash # POST /graph/v3/databases/your_database/tables/your_table/edges curl -X POST \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges?lock=true" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mutations": [ { "type": "INSERT", "edge": { "version": 1, "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" } } } ] }' ``` ### Response Example [Section titled “Response Example”](#response-example) ```json { "results": [ { "source": "source1", "target": "target1", "status": "CREATED", "count": 1 } ] } ``` ## 2. Edge Mutation (Sync) [Section titled “2. Edge Mutation (Sync)”](#2-edge-mutation-sync) Mutate edges synchronously. This endpoint waits for the mutation to complete before returning a response. ### Endpoint [Section titled “Endpoint”](#endpoint-1) ```plaintext POST /graph/v3/databases/{database}/tables/{table}/edges/sync ``` ### Parameters [Section titled “Parameters”](#parameters-1) | Location | Parameter | Required | Description | | -------- | ------------- | ------------------------ | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | database | Required | Target database name | | Path | table | Required | Target table name | | Query | lock | Optional (default: true) | Whether to acquire lock during mutation | | Body | mutations | Required | List of mutation items | ### Request Body [Section titled “Request Body”](#request-body-1) [EdgeBulkMutationRequest](#edgebulkmutationrequest) - Payload containing mutation items ### Response Type [Section titled “Response Type”](#response-type-1) [EdgeMutationResponse](#edgemutationresponse) - Payload containing mutation results ### Request Example [Section titled “Request Example”](#request-example-1) | Parameter | Value | | ------------- | -------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | lock | true | ```bash # POST /graph/v3/databases/your_database/tables/your_table/edges/sync curl -X POST \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/sync?lock=true" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mutations": [ { "type": "UPDATE", "edge": { "version": 2, "source": "source1", "target": "target1", "properties": { "weight": 0.9, "type": "FOLLOWS" } } } ] }' ``` ### Response Example [Section titled “Response Example”](#response-example-1) ```json { "results": [ { "source": "source1", "target": "target1", "status": "UPDATED", "count": 1 } ] } ``` ## 3. Multi-Edge Mutation [Section titled “3. Multi-Edge Mutation”](#3-multi-edge-mutation) Mutate multi-edges identified by edge IDs. ### Endpoint [Section titled “Endpoint”](#endpoint-2) ```plaintext POST /graph/v3/databases/{database}/tables/{table}/multi-edges ``` ### Parameters [Section titled “Parameters”](#parameters-2) | Location | Parameter | Required | Description | | -------- | ------------- | ------------------------ | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | database | Required | Target database name | | Path | table | Required | Target table name | | Query | lock | Optional (default: true) | Whether to acquire lock during mutation | | Body | mutations | Required | List of mutation items | ### Request Body [Section titled “Request Body”](#request-body-2) [MultiEdgeBulkMutationRequest](#multiedgebulkmutationrequest) - Payload containing mutation items ### Response Type [Section titled “Response Type”](#response-type-2) [MultiEdgeMutationResponse](#multiedgemutationresponse) - Payload containing mutation results ### Request Example [Section titled “Request Example”](#request-example-2) | Parameter | Value | | ------------- | -------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | lock | true | ```bash # POST /graph/v3/databases/your_database/tables/your_table/multi-edges curl -X POST \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/multi-edges?lock=true" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mutations": [ { "type": "INSERT", "edge": { "version": 1, "id": "edge1", "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" } } } ] }' ``` ### Response Example [Section titled “Response Example”](#response-example-2) ```json { "results": [ { "id": "edge1", "status": "CREATED", "count": 1 } ] } ``` ## 4. Multi-Edge Mutation (Sync) [Section titled “4. Multi-Edge Mutation (Sync)”](#4-multi-edge-mutation-sync) Mutate multi-edges synchronously. This endpoint waits for the mutation to complete before returning a response. ### Endpoint [Section titled “Endpoint”](#endpoint-3) ```plaintext POST /graph/v3/databases/{database}/tables/{table}/multi-edges/sync ``` ### Parameters [Section titled “Parameters”](#parameters-3) | Location | Parameter | Required | Description | | -------- | ------------- | ------------------------ | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | database | Required | Target database name | | Path | table | Required | Target table name | | Query | lock | Optional (default: true) | Whether to acquire lock during mutation | | Body | mutations | Required | List of mutation items | ### Request Body [Section titled “Request Body”](#request-body-3) [MultiEdgeBulkMutationRequest](#multiedgebulkmutationrequest) - Payload containing mutation items ### Response Type [Section titled “Response Type”](#response-type-3) [MultiEdgeMutationResponse](#multiedgemutationresponse) - Payload containing mutation results ### Request Example [Section titled “Request Example”](#request-example-3) | Parameter | Value | | ------------- | -------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | lock | true | ```bash # POST /graph/v3/databases/your_database/tables/your_table/multi-edges/sync curl -X POST \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/multi-edges/sync?lock=true" \ -H "Authorization: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mutations": [ { "type": "DELETE", "edge": { "version": 3, "id": "edge1", "source": "source1", "target": "target1", "properties": {} } } ] }' ``` ### Response Example [Section titled “Response Example”](#response-example-3) ```json { "results": [ { "id": "edge1", "status": "DELETED", "count": 1 } ] } ``` ## Event Types [Section titled “Event Types”](#event-types) The `type` field in mutation items specifies the operation to perform: | Type | Description | | -------- | --------------------------------------------------------------- | | `INSERT` | Create a new edge or update an existing edge with a new version | | `UPDATE` | Update properties of an existing edge | | `DELETE` | Delete an edge (marks it as deleted) | ## Lock Parameter [Section titled “Lock Parameter”](#lock-parameter) The `lock` parameter controls whether to acquire a lock during mutation: * **true (default)**: Acquires a lock to prevent concurrent modifications. Ensures data consistency but may have higher latency. * **false**: Skips locking. Faster but may lead to race conditions if multiple mutations target the same edge simultaneously. **Note:** It is recommended to use `lock=true` in production environments to ensure data consistency. ## Data Model [Section titled “Data Model”](#data-model) ### EdgeBulkMutationRequest [Section titled “EdgeBulkMutationRequest”](#edgebulkmutationrequest) Edge mutation request payload. ```kotlin data class EdgeBulkMutationRequest( val mutations: List, // List of mutation items ) { data class MutationItem( val type: EventType, // Event type (INSERT, UPDATE, DELETE) val edge: Edge, // Edge data ) } ``` ### MultiEdgeBulkMutationRequest [Section titled “MultiEdgeBulkMutationRequest”](#multiedgebulkmutationrequest) Multi-edge mutation request payload. ```kotlin data class MultiEdgeBulkMutationRequest( val mutations: List, // List of mutation items ) { data class MutationItem( val type: EventType, // Event type (INSERT, UPDATE, DELETE) val edge: MultiEdge, // Multi-edge data ) } ``` ### EdgeMutationResponse [Section titled “EdgeMutationResponse”](#edgemutationresponse) Edge mutation response payload. ```kotlin data class EdgeMutationResponse( val results: List, // List of mutation results ) { data class Item( val source: Any, // Source node ID val target: Any, // Target node ID val status: String, // Mutation status (e.g., CREATED, UPDATED, DELETED) val count: Int, // Number of edges affected ) } ``` ### MultiEdgeMutationResponse [Section titled “MultiEdgeMutationResponse”](#multiedgemutationresponse) Multi-edge mutation response payload. ```kotlin data class MultiEdgeMutationResponse( val results: List, // List of mutation results ) { data class Item( val id: Any, // Edge ID val status: String, // Mutation status (e.g., CREATED, UPDATED, DELETED) val count: Int, // Number of edges affected ) } ``` ### Edge [Section titled “Edge”](#edge) Individual edge information for mutation. ```kotlin data class Edge( val version: Long, // Edge version val source: Any, // Source node ID val target: Any, // Target node ID val properties: Map, // Edge properties ) ``` ### MultiEdge [Section titled “MultiEdge”](#multiedge) Individual multi-edge information for mutation. ```kotlin data class MultiEdge( val version: Long, // Edge version val id: Any, // Edge ID val source: Any? = null, // Source node ID (optional) val target: Any? = null, // Target node ID (optional) val properties: Map, // Edge properties ) ``` # Query API Reference > Edge query API reference This API provides query operations for edges (unique-edge). For the difference between unique-edge and multi-edge, see [FAQ](/faq/#what-is-the-difference-between-unique-edge-and-multi-edge). > **Note:** Authentication is not yet implemented. The `Authorization` header is reserved for future use. For conceptual understanding, see [Query](/design/query/). ## 1. Count [Section titled “1. Count”](#1-count) Retrieve the number of edges starting from a specific node. ### Endpoint [Section titled “Endpoint”](#endpoint) ```plaintext GET /graph/v3/databases/{database}/tables/{table}/edges/count ``` ### Parameters [Section titled “Parameters”](#parameters) | Location | Parameter | Required | Description | | -------- | ------------- | ------------------------ | ----------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | database | Required | Target database name | | Path | table | Required | Target table name | | Query | start | Required | Starting node ID | | Query | direction | Required | [Query direction](#query-direction) (OUT or IN) | | Query | ranges | Optional (default: null) | [Scan range](#index-ranges) | ### Response Type [Section titled “Response Type”](#response-type) [Count](#count-response) - Payload containing edge count information ### Request Example [Section titled “Request Example”](#request-example) | Parameter | Value | | ------------- | -------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | start | source1 | | direction | OUT | ```bash # count?start=source1&direction=OUT curl -X GET \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/count?start=source1&direction=OUT" \ -H "Authorization: YOUR_API_KEY" ``` ### Response Example [Section titled “Response Example”](#response-example) ```json { "start": "source1", "direction": "OUT", "count": 10, "context": {} } ``` ## 2. Get [Section titled “2. Get”](#2-get) Retrieve an edge between a source node and a target node. ### Endpoint [Section titled “Endpoint”](#endpoint-1) ```plaintext GET /graph/v3/databases/{database}/tables/{table}/edges/get ``` ### Parameters [Section titled “Parameters”](#parameters-1) | Location | Parameter | Required | Description | | -------- | ------------- | ------------------------ | -------------------------------------------- | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | database | Required | Target database name | | Path | table | Required | Target table name | | Query | source | Required | Source node ID list (comma-separated) | | Query | target | Required | Target node ID list (comma-separated) | | Query | filters | Optional (default: null) | [Filtering conditions](#filters) | When multiple source or target IDs are specified, the operation behaves as **mget** to retrieve multiple edges. In this case, a maximum of **25 edges** can be retrieved per API request. To retrieve more edges, make multiple API requests. ### Response Type [Section titled “Response Type”](#response-type-1) [EdgeFrame](#edgeframe-response) - Payload containing edge data ### Request Example (Get) - 1 source, 1 target [Section titled “Request Example (Get) - 1 source, 1 target”](#request-example-get---1-source-1-target) Retrieves edge data for (source, target). (Maximum 1 result) | Parameter | Value | | ------------- | -------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | source | source1 | | target | target1 | ```bash # get?source=source1&target=target1 curl -X GET \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/get?source=source1&target=target1" \ -H "Authorization: YOUR_API_KEY" ``` **Response:** ```json { "edges": [ { "version": 1, "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" }, "context": {} } ], "count": 1, "total": 1, "offset": null, "hasNext": false, "context": {} } ``` ### Request Example (MGet) - 1 source, N targets [Section titled “Request Example (MGet) - 1 source, N targets”](#request-example-mget---1-source-n-targets) Retrieves edge data for (source, target1) \~ (source, targetN). (Maximum N results) | Parameter | Value | | ------------- | ----------------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | source | source1 | | target | target1,target2,target3 | ```bash # get?source=source1&target=target1,target2,target3 curl -X GET \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/get?source=source1&target=target1,target2,target3" \ -H "Authorization: YOUR_API_KEY" ``` **Response:** ```json { "edges": [ { "version": 3, "source": "source1", "target": "target3", "properties": { "weight": 0.75, "type": "FOLLOWS" }, "context": {} }, { "version": 1, "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" }, "context": {} } ], "count": 2, "total": 2, "offset": null, "hasNext": false, "context": {} } ``` ### Request Example (MGet) - M sources, 1 target [Section titled “Request Example (MGet) - M sources, 1 target”](#request-example-mget---m-sources-1-target) Retrieves edge data for (source1, target) \~ (sourceM, target). (Maximum M results) | Parameter | Value | | ------------- | ----------------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | source | source1,source2,source3 | | target | target1 | ```bash # get?source=source1,source2,source3&target=target1 curl -X GET \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/get?source=source1,source2,source3&target=target1" \ -H "Authorization: YOUR_API_KEY" ``` **Response:** ```json { "edges": [ { "version": 3, "source": "source3", "target": "target1", "properties": { "weight": 0.75, "type": "FOLLOWS" }, "context": {} }, { "version": 1, "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" }, "context": {} } ], "count": 2, "total": 2, "offset": null, "hasNext": false, "context": {} } ``` ### Request Example (MGet) - M sources, N targets (Not Recommended) [Section titled “Request Example (MGet) - M sources, N targets (Not Recommended)”](#request-example-mget---m-sources-n-targets-not-recommended) Retrieves edge data for (source1, target1) \~ (sourceM, targetN). (Maximum M × N results) **Note:** This format should not be used in production as it can retrieve up to M × N edges. | Parameter | Value | | ------------- | ----------------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | source | source1,source2,source3 | | target | target1,target2,target3 | ```bash # get?source=source1,source2,source3&target=target1,target2,target3 curl -X GET \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/get?source=source1,source2,source3&target=target1,target2,target3" \ -H "Authorization: YOUR_API_KEY" ``` **Response:** ```json { "edges": [ { "version": 3, "source": "source1", "target": "target3", "properties": { "weight": 0.75, "type": "FOLLOWS" }, "context": {} }, { "version": 3, "source": "source3", "target": "target1", "properties": { "weight": 0.75, "type": "FOLLOWS" }, "context": {} }, { "version": 1, "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" }, "context": {} } ], "count": 3, "total": 3, "offset": null, "hasNext": false, "context": {} } ``` ## 3. Scan [Section titled “3. Scan”](#3-scan) Scan edges using an index. ### Endpoint [Section titled “Endpoint”](#endpoint-2) ```plaintext GET /graph/v3/databases/{database}/tables/{table}/edges/scan/{index} ``` ### Parameters [Section titled “Parameters”](#parameters-2) | Location | Parameter | Required | Description | | -------- | ------------- | ------------------------------ | ------------------------------------------------------------------------ | | Header | Authorization | Optional | Authentication key (reserved for future use) | | Path | database | Required | Target database name | | Path | table | Required | Target table name | | Path | index | Required | Index name to use | | Query | start | Required | Starting node ID | | Query | direction | Required | [Query direction](#query-direction) (OUT or IN) | | Query | limit | Optional | Maximum number of results to return; 25 is recommended for production | | Query | offset | Optional (default: null) | [Pagination](#pagination) offset | | Query | ranges | Optional (default: null) | [Scan range](#index-ranges) | | Query | filters | Optional (default: null) | [Filtering conditions](#filters) | | Query | features | Optional (default: empty list) | List of features to include (e.g., `total` to calculate the total value) | ### Response Type [Section titled “Response Type”](#response-type-2) [EdgeFrame](#edgeframe-response) - Payload containing edge data ### Request Example [Section titled “Request Example”](#request-example-1) | Parameter | Value | | ------------- | -------------- | | Authorization | YOUR\_API\_KEY | | database | your\_database | | table | your\_table | | index | your\_index | | start | source1 | | direction | OUT | ```bash # scan/your_index?start=source1&direction=OUT curl -X GET \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/scan/your_index?start=source1&direction=OUT" \ -H "Authorization: YOUR_API_KEY" ``` ### Response Example [Section titled “Response Example”](#response-example-1) ```json { "edges": [ { "version": 1, "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" }, "context": {} } ], "count": 1, "total": -1, "offset": null, "hasNext": false, "context": {} } ``` **Note:** When `total` is -1, the total count is not calculated. If `features=total` is provided, the total is actually calculated. ## Pagination [Section titled “Pagination”](#pagination) The `offset` parameter indicates the starting position of the next page, and `hasNext` indicates whether there is a next page. If there is no offset, the first page is returned. ### 1. First Page Request [Section titled “1. First Page Request”](#1-first-page-request) Request the first page without an offset. **Request Example** | Parameter | Value | Description | | ------------- | -------------- | -------------- | | Authorization | YOUR\_API\_KEY | | | database | your\_database | | | table | your\_table | | | index | your\_index | | | start | source1 | | | direction | OUT | | | limit | | 25 recommended | ```bash # scan/your_index?start=source1&direction=OUT curl -X GET \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/scan/your_index?start=source1&direction=OUT" \ -H "Authorization: YOUR_API_KEY" ``` **Response:** ```json { "edges": [ { "version": 1, "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" }, "context": {} }, ... ], "count": 10, "total": -1, "offset": "string_encoded", "hasNext": true, "context": {} } ``` ### 2. Next Page Request [Section titled “2. Next Page Request”](#2-next-page-request) Use the `offset` value from the previous response to retrieve the next page. **Request Example** | Parameter | Value | Description | | ------------- | --------------- | ------------------------------------- | | Authorization | YOUR\_API\_KEY | | | database | your\_database | | | table | your\_table | | | index | your\_index | | | start | source1 | | | direction | OUT | | | offset | string\_encoded | Value received from previous response | | limit | | 25 recommended | ```bash # scan/your_index?start=source1&direction=OUT&offset=string_encoded curl -X GET \ "http://ab.example.com/graph/v3/databases/your_database/tables/your_table/edges/scan/your_index?start=source1&direction=OUT&offset=string_encoded" \ -H "Authorization: YOUR_API_KEY" ``` **Response:** ```json { "edges": [ { "version": 1, "source": "source1", "target": "target1", "properties": { "weight": 0.8, "type": "FOLLOWS" }, "context": {} }, ... ], "count": 10, "total": -1, "offset": null, "hasNext": false, "context": {} } ``` ## Index Ranges [Section titled “Index Ranges”](#index-ranges) The `ranges` parameter allows you to specify the data scan range in count/scan queries. ### Relationship between Ranges and Indexes [Section titled “Relationship between Ranges and Indexes”](#relationship-between-ranges-and-indexes) > **Important:** Ranges can only use fields that are included in the index. 1. **Index Dependency:** Fields used in `ranges` must have an index. Attempting to specify a range on a field without an index will return unexpected results. 2. **Operator Meaning:** The `eq`, `gt(e)`, `lt(e)`, and `between` operators determine the start and end points of the actual storage scan. They must be used appropriately according to index properties. * When an index is set to `DESC` (descending): `lt` becomes the start point and `gt` becomes the end point. * When an index is set to `ASC` (ascending): `gt` becomes the start point and `lt` becomes the end point. 3. **Composite Ranges:** Ranges can be used in the order of the field combination that the index is defined on. Ranges must be applied in the order of fields defined in the index. ### Range Syntax [Section titled “Range Syntax”](#range-syntax) Ranges are written in the following format: ```plaintext ranges=range1[;range2;range3;...] ``` Each range follows this format: ```plaintext field:operator:value ``` To apply multiple ranges, separate them with semicolons (`;`): ```plaintext field1:operator1:value1;field2:operator2:value2 ``` When using the `in` or `bt` (between) operator, multiple values are separated by commas (`,`): ```plaintext field:in:value1,value2,value3 field:bt:minValue,maxValue ``` ### Operators [Section titled “Operators”](#operators) | Operator | Description | Example | | -------- | --------------------- | ------------------------------ | | `eq` | Equal | `type:eq:0` | | `gt` | Greater than | `createdAt:gt:1000000` | | `gte` | Greater than or equal | `createdAt:gte:1000000` | | `lt` | Less than | `createdAt:lt:2000000` | | `lte` | Less than or equal | `createdAt:lte:2000000` | | `bt` | Between two values | `createdAt:bt:1000000,2000000` | ### Examples [Section titled “Examples”](#examples) **Simple range:** ```plaintext GET .../scan/your_index?start=10&direction=OUT&ranges=type:eq:0 ``` This example specifies a range to retrieve only edges where the `type` property is 0. Note that `type` refers to `properties.type`, and `properties.type` must be included in `your_index`. **Composite range:** ```plaintext GET .../scan/your_index?start=10&direction=OUT&ranges=type:eq:0;createdAt:gt:1000000 ``` This example specifies a range to retrieve only edges where `type` is 0 and `createdAt` is greater than 1000000. **Between operator:** ```plaintext GET .../scan/your_index?start=10&direction=OUT&ranges=createdAt:bt:1000000,2000000 ``` This example specifies a range to retrieve only edges where `createdAt` is between 1000000 and 2000000. ## Filters [Section titled “Filters”](#filters) The `filters` parameter allows you to filter data (edges) in get/scan queries. It uses the same format as [ranges](#index-ranges). **Difference from ranges:** While [ranges](#index-ranges) pre-specifies the query range and can only use fields set as indexes, `filters` filters the query results and can use fields that are not set as indexes. ### Filter Examples [Section titled “Filter Examples”](#filter-examples) ```plaintext GET .../get?source=1,2,3&target=10&filters=type:eq:0 GET .../scan/your_index?start=1&direction=OUT&ranges=createdAt:gte:1000000&filters=type:eq:0 ``` ## Data Model [Section titled “Data Model”](#data-model) ### Query Direction [Section titled “Query Direction”](#query-direction) * **OUT:** Outgoing direction (e.g., retrieving products that a user liked in a user \[likes]→ product relationship) * **IN:** Incoming direction (e.g., retrieving users who liked a product in the above relationship) ### Count Response [Section titled “Count Response”](#count-response) Count query response. Payload containing edge count information. ```kotlin data class Count( val start: Any, // Starting node ID val direction: Direction, // Direction (IN, OUT) val count: Long, // Edge count val context: Map, // Context information ) ``` ### EdgeFrame Response [Section titled “EdgeFrame Response”](#edgeframe-response) Get and scan query response. Payload containing edge data. ```kotlin data class EdgeFrame( val edges: List, // Edge list val count: Int, // Number of edges currently returned val total: Long, // Total edge count (-1 means not calculated) val offset: String?, // Pagination offset val hasNext: Boolean, // Whether next page exists val context: Map, // Context information ) ``` ### Edge [Section titled “Edge”](#edge) Individual edge information payload. ```kotlin data class Edge( val version: Long, // Edge version val source: Any, // Source node ID val target: Any, // Target node ID val properties: Map, // Edge properties val context: Map, // Context information ) ``` # Introducing Actionbase as Open Source > An open-source database designed to reliably serve user interactions—such as likes, follows, and recently viewed items—at massive scale. ## Likes 👍 [Section titled “Likes 👍”](#likes) ***A simple feature that becomes surprisingly complex at scale*** Imagine adding a “Like” feature to a feed or a product page. The basic implementation is straightforward. You just need to record **who** (user ID) liked **what** (target ID). The total number of likes can be calculated by counting (`COUNT`) all records with the same target ID. Most systems therefore start with a single table containing user IDs and target IDs. Indexes are added based on query patterns, and once `COUNT` becomes expensive, a cache is introduced. And this approach works well at most scales. **But once a service grows beyond a certain point, things begin to change.** You start thinking about sharding. You repeatedly add compensating logic to keep caches consistent. What was once a simple model gradually becomes complex. The cost of supporting something as simple as a “Like” keeps increasing. Teams that have experienced large-scale traffic usually end up asking the same question at some point: > **“Is this really the right way to keep doing this?”** Faced with that question, our conclusion was clear: > **“We need a different approach.”** ## Actionbase [Section titled “Actionbase”](#actionbase) ***A different answer to the same problem — a database for user interactions*** Actionbase is a database designed to reliably handle user interactions even under massive traffic. By **interactions**, we mean any kind of action that occurs between a user and a target—such as **likes, reactions, and follows**. Actionbase models these interactions as: **who (the actor) performed what action on which target.** > Example: “User1” (actor) liked (action) “Product A” (target). ### Designing Writes for Reads [Section titled “Designing Writes for Reads”](#designing-writes-for-reads) ***What “a different approach” actually means*** The core idea behind Actionbase is simple: > **At write time, precompute everything you’ll need at read time.** When a user clicks “Like,” the user’s list of liked items is updated, the target’s like count is incremented, and even the ordering information required for sorting and querying is updated—**all within a single write flow**. As a result, reads require no aggregation or additional computation. You simply **read the precomputed results as they are.** > This is the “different approach” Actionbase is built on. Instead of starting with “How do we read fast?” and optimizing later, Actionbase defines fast reads first—and then designs writes around them. This approach has already been proven in production, serving large-scale traffic at Kakao services such as KakaoShopping. ## Open Source [Section titled “Open Source”](#open-source) ***From individual features to a shared structure*** In this post, we used “Likes” as an example, but Actionbase is not limited to a single feature. Likes 👍, recently viewed items 👀, follows 👥, even sent and received gifts 🎁— if something can be expressed as a user interaction, it can be modeled with Actionbase. While these interactions may look different on the surface, from a data modeling perspective, they eventually converge into a shared structure. As this structure accumulates, it grows beyond individual features into **a larger graph of relationships**. **Content and products connect around users. People connect with people. The flow of the entire service becomes something that can be stored, queried, and traversed within a single coherent structure.** Rather than solving this problem repeatedly in different ways, we believe there should be a **shared structure** that can be validated and refined together across many environments. Actionbase is an attempt to treat user interactions not as isolated features, but as **connected relationships**— and open source is our way of building that structure together. > ✨ **When your data converges in Actionbase, you may discover possibilities you couldn’t see before.** ✨ ## Getting Started [Section titled “Getting Started”](#getting-started) * GitHub: * Documentation: * Contribution Guide: **Explore Actionbase—and help shape its future.** # Code of Conduct > Community code of conduct for Actionbase ## Our Pledge [Section titled “Our Pledge”](#our-pledge) We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards [Section titled “Our Standards”](#our-standards) Examples of behavior that contributes to a positive environment for our community include: * **Being respectful** of differing opinions, viewpoints, and experiences * **Giving and gracefully accepting** constructive feedback * **Accepting responsibility** and apologizing to those affected by our mistakes, and learning from the experience * **Focusing on what is best** for the community * **Showing empathy** towards other community members Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others’ private information without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities [Section titled “Enforcement Responsibilities”](#enforcement-responsibilities) Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope [Section titled “Scope”](#scope) This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Reporting [Section titled “Reporting”](#reporting) Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to . All complaints will be reviewed and investigated promptly and fairly. As the project is in its early open-source stage, reports are currently handled by the project maintainer. A dedicated contact channel will be established as the community grows. ## Enforcement Guidelines [Section titled “Enforcement Guidelines”](#enforcement-guidelines) Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction [Section titled “1. Correction”](#1-correction) **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning [Section titled “2. Warning”](#2-warning) **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban [Section titled “3. Temporary Ban”](#3-temporary-ban) **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban [Section titled “4. Permanent Ban”](#4-permanent-ban) **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution [Section titled “Attribution”](#attribution) This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at . For answers to common questions about this code of conduct, see . # Contributing > How to participate in the Actionbase project Thank you for your interest in Actionbase. Any form of participation—using the project, asking questions, reporting issues, improving documentation, or contributing code—is appreciated. ## How we collaborate [Section titled “How we collaborate”](#how-we-collaborate) We collaborate through [GitHub](https://github.com/kakao/actionbase): * **[Discussions](https://github.com/kakao/actionbase/discussions)**: Questions, ideas, and open-ended conversations * **[Issues](https://github.com/kakao/actionbase/issues)**: Bug reports, feature requests, and concrete improvements * **[Pull Requests](https://github.com/kakao/actionbase/pulls)**: Code and documentation changes If you’re unsure where something belongs, start with a Discussion. Pull requests are reviewed collaboratively and merged by Maintainers. When submitting a pull request, please sign the [CLA](https://cla-assistant.io/kakao/actionbase) (Contributor Licensing Agreement) for Individual. If you need a Contributor Licensing Agreement for Corporate, please [contact us](mailto:oss@kakaocorp.com). ## Security [Section titled “Security”](#security) Report security vulnerabilities to instead of opening a public issue. ## Community standards [Section titled “Community standards”](#community-standards) All contributors are expected to follow the **[Code of Conduct](/community/code-of-conduct/)**. For project roles, see [Governance](/community/governance/). # Governance > Overview of Actionbase's governance model and roles Actionbase is in its early open-source stage. Our governance structure is transparent and flexible. For practical guidance on how to contribute, see the [Contributing Guide](/community/contributing/). ## Roles [Section titled “Roles”](#roles) * **Maintainer**: Oversees project direction, ensures quality, and manages releases. Maintainers have final decision-making authority. Under the current governance model, Maintainers are Kakao employees. * **Committer**: Trusted contributors who actively participate in development, reviews, and technical discussions. * **Contributor**: Participates through code, documentation, issue reporting, or discussions. Welcome from anywhere, regardless of affiliation. ## Role Progression and Evolution [Section titled “Role Progression and Evolution”](#role-progression-and-evolution) Contributors may be invited to become Committers. Maintainers are selected from Committers. Maintainer selection may later follow community consensus or foundation policies. ## Project Voice [Section titled “Project Voice”](#project-voice) In project communications, “we” refers to Actionbase maintainers, committers, and contributors, not any company or organization. # Core Concepts > Core concepts behind Actionbase design Actionbase is an OLTP engine specialized for user interactions, optimized for high-throughput, low-latency workloads. It uses graph-based modeling specifically designed for user interactions, not as a general-purpose graph database. ## User Interactions [Section titled “User Interactions”](#user-interactions) Actionbase handles various types of user interactions: * Recent views (products, content) * Likes and reactions (hearts, care, emojis, etc.) * Follows and relationships These activities share common characteristics: they represent relationships between users and entities, require real-time access, and are queried in predictable patterns. ## Property Graph Model [Section titled “Property Graph Model”](#property-graph-model) Actionbase represents user interactions using a Property Graph model: * **Source (actor)**: User ID or any entity that performs an action * **Target**: Product, content, user ID, or any entity that receives an action * **Properties**: Schema-defined attributes like `timestamp`, `type`, `metadata`, etc. This model naturally fits user interactions and enables rich query patterns based on properties. ```plaintext User (source) └─ id: "user123" User --[likes]--> Product (edge) ├─ source: "user123" ├─ target: "product456" └─ properties: { timestamp: 1234567890, type: "heart", device: "mobile", location: "US" } User --[follows]--> User (edge) ├─ source: "user123" ├─ target: "user789" └─ properties: { timestamp: 1234567891, notification_enabled: true } ``` For how to define schemas for your graph, see [Schema](/design/schema/). ## State and Event Model [Section titled “State and Event Model”](#state-and-event-model) Internally, Actionbase uses a **State and Event model** to ensure consistency: * **State**: The current state of the state machine (e.g., “user liked product”) * **Event**: Input that transitions the state (e.g., “user clicked like button”) When a user action occurs, Actionbase: 1. Reads the current state 2. Performs a state transition based on the incoming event 3. Stores the new state The system is designed so that even if events arrive out of order, the final state remains consistent. Clients attach timestamps to events, and Actionbase uses these timestamps to compute the correct final state regardless of arrival order. For detailed information on how mutations work, see [Mutation](/design/mutation/). ## Write-Time Optimization [Section titled “Write-Time Optimization”](#write-time-optimization) Actionbase’s key strength is **pre-computing data structures at write time** for fast, predictable reads. When an edge is written, Actionbase creates three types of pre-computed data: 1. **State**: Direct records representing connections between source and target nodes 2. **Index**: Pre-computed indexes based on schema-defined properties (e.g., `created_at DESC`) 3. **Count**: Pre-computed counters (e.g., “number of products viewed by user”) By pre-computing these structures during writes, reads can use simple GET/SCAN/COUNT operations for ultra-fast responses without expensive query-time computations. For details on how these structures are created, see [Mutation](/design/mutation/). For how queries access them, see [Query](/design/query/). ## Data Flow [Section titled “Data Flow”](#data-flow) ### Write Path [Section titled “Write Path”](#write-path) ```plaintext Client → Server → Engine → WAL → Storage → CDC ``` The write path involves: 1. Writing to WAL (Write-Ahead Log) for durability 2. Acquiring locks for consistency 3. Reading current state 4. Applying state transition 5. Computing indexes and counters 6. Writing to storage 7. Emitting CDC for downstream systems For detailed write flow, see [Mutation](/design/mutation/). ### Read Path [Section titled “Read Path”](#read-path) ```plaintext Client → Server → Engine → Storage → Response ``` The read path retrieves pre-computed data structures: * **Count Query** → EdgeCounter * **Get Query** → EdgeState * **Scan Query** → EdgeIndex For detailed read flow, see [Query](/design/query/). ## Next Steps [Section titled “Next Steps”](#next-steps) * [Schema](/design/schema/): Define the structure of your edges * [Mutation](/design/mutation/): Understand how writes work * [Query](/design/query/): Learn how to query your data * [Datastore](/design/datastore/): Explore datastore backend options # Datastore > Understanding datastore backends in Actionbase Actionbase builds on proven storage technologies, leveraging their strengths while adding value through graph modeling and write-time optimization. The datastore abstraction layer allows integration with different backends. ## Datastore Abstraction [Section titled “Datastore Abstraction”](#datastore-abstraction) Actionbase abstracts storage operations through a minimal interface, enabling integration with various backends. By building on established technologies, Actionbase leverages their reliability, scalability, and performance while focusing on interaction-specific capabilities. ### Required Operations [Section titled “Required Operations”](#required-operations) A datastore backend must support: | Operation | Description | | -------------- | ------------------------------------------ | | get | Retrieve value(s) by key(s) | | delete | Delete a value by key | | scan | Range scan with prefix, start, stop, limit | | checkAndMutate | Atomic check-and-mutate for consistency | | batch | Batch mutations (optional, recommended) | This interface is intentionally minimal. Any backend implementing these operations can be integrated. ## Supported Backends [Section titled “Supported Backends”](#supported-backends) ### HBase [Section titled “HBase”](#hbase) **HBase** is the production datastore backend for Actionbase. It’s a distributed, scalable NoSQL database built on HDFS. | Characteristic | Description | | ------------------------ | ---------------------------------------- | | Horizontal Scalability | Shards data across nodes, linear scaling | | Strong Durability | Data replicated across nodes | | Low-latency Access | Optimized for random reads and writes | | Automatic Load Balancing | Even distribution across cluster | | Proven at Scale | Used for petabyte-scale data | HBase requires expertise when used directly: * Row key design * Column family organization * Region splitting and compaction * Cluster management Actionbase provides: * HBase’s durability, scale, and distribution * Simpler usability through high-level abstraction * Interaction-specific features (State/Index/Count) For HBase configuration, see [HBase Operations](/operations/hbase/). ### Memory [Section titled “Memory”](#memory) **Memory** is an in-memory backend for development and testing. | Characteristic | Description | | -------------- | ------------------------- | | Easy Setup | No configuration required | | No Persistence | Data lost on server stop | | Fast | In-memory operations | Ideal for local development, testing, and prototyping. ### SlateDB (Planned) [Section titled “SlateDB (Planned)”](#slatedb-planned) **[SlateDB](https://slatedb.io/)** is a planned backend for small to medium-scale deployments. | Characteristic | Description | | --------------------- | ------------------------------ | | Lower Complexity | Simpler setup and maintenance | | Resource Efficiency | Less infrastructure overhead | | Transactional Support | Suitable for smaller workloads | ## How Actionbase Uses Datastore [Section titled “How Actionbase Uses Datastore”](#how-actionbase-uses-datastore) Actionbase maps its operations to datastore operations: | Actionbase Operation | Datastore Operation | Data Structure | | -------------------- | ------------------- | --------------------- | | Get Query | get | EdgeState | | Scan Query | scan | EdgeIndex | | Count Query | get | EdgeCounter | | Mutation (lock) | checkAndMutate | Lock | | Mutation (write) | batch / put | State, Index, Counter | | Mutation (cleanup) | delete | Old indexes | By building on proven storage, Actionbase focuses on interaction-specific features while the underlying datastore handles persistence, replication, and scalability. ## Choosing a Backend [Section titled “Choosing a Backend”](#choosing-a-backend) | Backend | Use Case | Scale | | ------- | -------------------- | --------------- | | HBase | Production | Large | | SlateDB | Production (planned) | Small to Medium | | Memory | Development | Local | ## Next Steps [Section titled “Next Steps”](#next-steps) * [Data Encoding](/internals/encoding/): How data is encoded in the datastore * [HBase Operations](/operations/hbase/): HBase configuration guide * [Local Provisioning](/provisioning/local/): Get started locally # Glossary > Key terminology used in Actionbase documentation This page defines key terms used throughout the Actionbase documentation. ## API Version Mapping [Section titled “API Version Mapping”](#api-version-mapping) Actionbase is in the process of migrating from v2 to v3 APIs. The terminology differs between versions: | v2 Term | v3 Term | Description | | ------- | -------- | ----------------------------- | | Service | Database | Namespace for grouping tables | | Label | Table | Edge schema definition | * **Metadata APIs** use v2 (service/label) — will migrate to v3 in a future release * **Query/Mutation APIs** use v3 (database/table) ## Data Model [Section titled “Data Model”](#data-model) | Term | Description | | --------------- | --------------------------------------------------------------------- | | **Edge** | A graph edge representing a user interaction | | **Source** | The entity performing the interaction (e.g., user\_id) | | **Target** | The entity receiving the interaction (e.g., product\_id) | | **Properties** | Attributes stored on an edge (e.g., created\_at) | | **Unique-edge** | An edge identified by (source, target) pair | | **Multi-edge** | An edge identified by ID; multiple edges allowed per (source, target) | | **Index** | Pre-computed structure for efficient querying | | **Alias** | An alternative name for a label (or table) | ## Query and Mutation [Section titled “Query and Mutation”](#query-and-mutation) | Term | Description | | ------------- | ------------------------------------------------------------------------------------------------------- | | **Count** | Query that returns the number of edges | | **Get** | Query that retrieves a specific edge by source and target | | **Scan** | Query that scans edges using a pre-computed index | | **Start** | Starting node for Scan/Count queries; acts as source when direction is OUT, target when IN | | **Direction** | Query direction: `OUT` (source→target) or `IN` (target←source) | | **Range** | Index-based scan boundaries applied at storage level | | **Filter** | Post-retrieval filtering applied at application level | | **Version** | Uses client-provided timestamp (milliseconds or nanoseconds) for concurrency control and event ordering | | **Offset** | Encoded string indicating the starting position for pagination | | **Limit** | Maximum number of results to return; 25 is recommended for production | ## Write-Time Optimization [Section titled “Write-Time Optimization”](#write-time-optimization) | Term | Description | | --------------- | ------------------------------------------------ | | **EdgeState** | Current edge state, accessed by Get queries | | **EdgeIndex** | Sorted index entries, accessed by Scan queries | | **EdgeCounter** | Pre-computed counters, accessed by Count queries | ## Data Pipeline [Section titled “Data Pipeline”](#data-pipeline) | Term | Description | | ------- | -------------------------------------------------- | | **WAL** | Write-Ahead Log for durability and recovery | | **CDC** | Change Data Capture for downstream synchronization | # Mutation > Understanding how mutations work in Actionbase Mutations in Actionbase allow you to insert, update, and delete edges in your graph. The mutation process ensures data consistency, durability, and write-time optimization. For the conceptual foundation, see [Core Concepts](/design/concepts/). ## Mutation Flow [Section titled “Mutation Flow”](#mutation-flow) The complete mutation process: ``` flowchart TD Request([Mutation Request with Event, Operation]) --> WAL[Write WAL] WAL --> Lock[Acquire Lock] Lock --> Read[Read State from datastore] Read --> Modify{State exists?} Modify -->|Yes| ApplyEvent[Apply Event] Modify -->|No| InitialState[Initial State] InitialState --> ApplyEvent ApplyEvent --> ComputeAdditionalInfo[Compute Index, Count based on changed States] ComputeAdditionalInfo --> Write[Write State with AdditionalInfo to datastore] Write --> Release[Release Lock] Release --> CDC[Write CDC] CDC --> Response([Response]) ``` ## Mutation Request [Section titled “Mutation Request”](#mutation-request) A mutation request contains: * **Event**: The data change to be applied (e.g., new property values, edge creation) * **Operation**: The type of operation: * **Insert**: Create a new edge * **Update**: Modify an existing edge * **Delete**: Remove an edge ## Mutation Process [Section titled “Mutation Process”](#mutation-process) ### 1. Write WAL (Write-Ahead Log) [Section titled “1. Write WAL (Write-Ahead Log)”](#1-write-wal-write-ahead-log) Before any changes are made to the datastore, the mutation is written to the Write-Ahead Log (WAL). This ensures durability and enables recovery in case of failures. In production, Actionbase uses Kafka as the WAL backend. The WAL can be replayed to restore the final state, even if events arrive out of order. ### 2. Acquire Lock [Section titled “2. Acquire Lock”](#2-acquire-lock) To prevent concurrent modifications, Actionbase acquires a lock on the specific edge being mutated: * **Unique edges**: Locks based on (source, target) pair * **Multi edges**: Locks based on edge ID ### 3. Read State [Section titled “3. Read State”](#3-read-state) The current state of the edge is read from the datastore, including all existing properties, timestamps, and metadata. ### 4. Apply Event [Section titled “4. Apply Event”](#4-apply-event) The event is applied to the state, transitioning it based on the operation type: * **INSERT**: Updates `createdAt` timestamp and sets properties. The state becomes active when `createdAt > deletedAt`. * **UPDATE**: Updates only the specified properties without changing timestamps. * **DELETE**: Updates `deletedAt` timestamp and marks properties as deleted. The state becomes inactive when `deletedAt >= createdAt`. The transition respects version ordering—newer versions override older ones. ### 5. Compute Indexes and Counters [Section titled “5. Compute Indexes and Counters”](#5-compute-indexes-and-counters) Based on the changed state, Actionbase computes additional structures for fast reads: * **Indexes**: Old indexes are deleted, new indexes are created based on the changed state * **Counters**: Counts are incremented or decremented This write-time optimization ensures read queries can be answered quickly without expensive computations. ### 6. Write to Storage [Section titled “6. Write to Storage”](#6-write-to-storage) The modified state along with indexes and counters is written atomically to the datastore. ### 7. Release Lock [Section titled “7. Release Lock”](#7-release-lock) Once the write is complete, the lock is released. ### 8. Write CDC (Change Data Capture) [Section titled “8. Write CDC (Change Data Capture)”](#8-write-cdc-change-data-capture) The mutation is recorded in the CDC system for downstream systems. In production, Actionbase uses Kafka as the CDC backend. CDC stores the resulting state after processing, enabling synchronization to external systems (e.g., Iceberg, MPP, Trino) for analytics. ## Event Ordering [Section titled “Event Ordering”](#event-ordering) Clients attach timestamps to events (typically millisecond or nanosecond precision). Even if network delays cause events to arrive out of order, Actionbase uses the original timestamps to compute the correct final state. **Example**: If ON/OFF events arrive as `ON, OFF, ON, OFF` but were originally `ON, ON, OFF, OFF`, Actionbase ensures the final state is correct based on timestamps. This mechanism ensures **eventual consistency**—the final state is always correct regardless of event arrival order. ## Write-Time Optimization [Section titled “Write-Time Optimization”](#write-time-optimization) During mutations, Actionbase pre-computes: | Structure | Purpose | Query Type | | ----------- | -------------------- | ---------- | | EdgeState | Current edge state | Get | | EdgeIndex | Sorted index entries | Scan | | EdgeCounter | Aggregated counts | Count | **Benefits**: * **Fast Reads**: No query-time computation needed * **Consistent Performance**: Read latency remains low regardless of data size * **Efficient Storage**: Indexes and counters are maintained incrementally By computing at write time, Actionbase shifts computational cost from reads to writes—ideal for read-heavy workloads. ## Consistency Guarantees [Section titled “Consistency Guarantees”](#consistency-guarantees) The mutation process ensures consistency through: | Mechanism | Guarantee | | ------------------ | ----------------------------------------------- | | Locking | Prevents concurrent modifications | | Atomic Writes | State and indexes written together | | WAL | Durability and recovery | | Read-Modify-Write | Mutations based on latest state | | Timestamp Ordering | Correct final state despite out-of-order events | ## Next Steps [Section titled “Next Steps”](#next-steps) * [Query](/design/query/): How to read the pre-computed data * [Datastore](/design/datastore/): Datastore backend details * [API Reference](/api-references/mutation/): Complete mutation API # Query > Understanding how queries work in Actionbase Queries in Actionbase retrieve data that was pre-computed during mutations. The query system accesses these structures directly, providing ultra-fast reads with consistent low latency. For the conceptual foundation, see [Core Concepts](/design/concepts/). ## Query Philosophy [Section titled “Query Philosophy”](#query-philosophy) Actionbase queries read optimized data structures created during mutations: | Structure | Created During | Accessed By | | ----------- | -------------- | ----------- | | EdgeState | Mutation | Get Query | | EdgeIndex | Mutation | Scan Query | | EdgeCounter | Mutation | Count Query | You explicitly choose the query type and specify the index to use, ensuring each query follows an optimized path prepared during write time. ## Query Types [Section titled “Query Types”](#query-types) ### Count Query [Section titled “Count Query”](#count-query) Returns the number of edges matching a source node. **Use case**: “How many products has this user viewed?” **Processing**: 1. Construct EdgeCounter key from source, table, and direction 2. Return pre-computed counter value **Example**: Answered instantly with a single get operation. ### Get Query [Section titled “Get Query”](#get-query) Retrieves edge state by source and target node IDs. **Use case**: “Has this user viewed this product?” **Processing**: 1. Construct EdgeState key from source and target 2. Return edge state directly **MGet Support**: * Multiple source or target IDs → multi-get operation * Maximum 25 edges per API request * Patterns: 1 source with N targets, or M sources with 1 target ### Scan Query [Section titled “Scan Query”](#scan-query) Scans edges using a pre-computed index with range filtering and pagination. **Use case**: “Recent products viewed by this user” **Processing**: 1. Construct EdgeIndex key prefix from source, table, direction, and index 2. Apply range filters to determine scan boundaries 3. Scan pre-computed index entries 4. Apply optional filters to results 5. Apply pagination (limit, offset) 6. Return matching edges **Index Requirement**: * You must specify which index to use * The index must be defined in the schema * Index entries are created during mutations ## Query Flow [Section titled “Query Flow”](#query-flow) ``` flowchart TD Request([Query Request]) --> Route{Query Type} Route -->|Count| CountQuery[Count Query] Route -->|Get| GetQuery[Get Query] Route -->|Scan| ScanQuery[Scan Query] CountQuery --> EdgeCounter[Read EdgeCounter Pre-computed during mutation] GetQuery --> EdgeState[Read EdgeState Pre-computed during mutation] ScanQuery --> EdgeIndex[Read EdgeIndex Pre-computed during mutation] EdgeCounter --> Response([Response]) EdgeState --> Response EdgeIndex --> Response ``` ## Index Ranges [Section titled “Index Ranges”](#index-ranges) Scan queries can specify ranges to filter data efficiently at the storage level. ### Key Concepts [Section titled “Key Concepts”](#key-concepts) | Concept | Description | | -------------- | ----------------------------------------------------- | | Explicit Index | You must specify which index to use | | Operators | `eq`, `gt`, `lt`, `between` determine scan boundaries | | Index Order | Ranges applied in order of fields defined in index | | Sort Direction | Operator meaning depends on ASC/DESC | ### Range vs Filter [Section titled “Range vs Filter”](#range-vs-filter) | Type | Level | Uses Index | Performance | | ------ | ----------- | ---------- | --------------- | | Range | Storage | Yes | Fast | | Filter | Application | No | After retrieval | **Ranges** work with the pre-computed index structure, filtering at the storage level. **Filters** are applied after retrieval and can use any field. ## Pagination [Section titled “Pagination”](#pagination) Scan queries support pagination: | Parameter | Description | | --------- | ------------------------------------------------- | | offset | Encoded string indicating starting position | | limit | Maximum results; 25 is recommended for production | | hasNext | Boolean indicating more results available | Pagination works with the sorted index structure for efficient large result sets. ## Query Direction [Section titled “Query Direction”](#query-direction) Queries support two directions: | Direction | Description | Example | | --------- | -------------- | ------------------------- | | OUT | Outgoing edges | Products a user liked | | IN | Incoming edges | Users who liked a product | Separate index entries and counters are maintained for each direction during mutations. ## Read Path [Section titled “Read Path”](#read-path) ```plaintext Client → Server → Engine → Storage → Response ``` 1. **Client**: Sends query via REST API, specifying query type and parameters 2. **Server**: Validates the request 3. **Engine**: Constructs storage key and retrieves pre-computed data 4. **Storage**: Returns EdgeState, EdgeIndex, or EdgeCounter 5. **Response**: Formatted results to client This simple path accesses pre-optimized data, ensuring consistent low-latency performance. ## Performance Characteristics [Section titled “Performance Characteristics”](#performance-characteristics) | Characteristic | Guarantee | | ------------------------- | ------------------------------------------------------- | | No Query-time Computation | Queries retrieve pre-computed data | | Explicit Access Patterns | You choose query type and index | | Consistent Structure | Data structure matches what was created during mutation | | Predictable Latency | Performance is consistent regardless of data size | ## Next Steps [Section titled “Next Steps”](#next-steps) * [Schema](/design/schema/): Define indexes for your queries * [Mutation](/design/mutation/): How pre-computed structures are created * [API Reference](/api-references/query/): Complete query API # Schema > Defining the structure of your interaction data Schema defines the structure and configuration of interaction data in Actionbase. Before storing any data, you must define how your edges are structured, what properties they have, and how they can be queried. For the conceptual foundation, see [Core Concepts](/design/concepts/). ## Schema Hierarchy [Section titled “Schema Hierarchy”](#schema-hierarchy) Actionbase organizes schemas in a hierarchical structure: ```plaintext Service ├── Label (defines edge schema) │ ├── Schema (source, target, properties) │ ├── Indexes (for efficient querying) │ └── Storage (physical storage) └── Alias (alternative name for Label) ``` * A **Service** groups related **Labels** and **Aliases** * A **Label** defines the complete schema for edges * An **Alias** provides an alternative name for a **Label** * A **Label** references a **Storage** for physical data storage ## Service [Section titled “Service”](#service) A **Service** is a logical namespace that contains labels and aliases, providing organization and isolation for related graph structures. **Properties:** * **name**: Service identifier (e.g., `myservice`) * **desc**: Description of the service * **active**: Whether the service is active Services group related labels together. For example, an e-commerce service might contain labels for `likes`, `views`, and `purchases`. ## Label [Section titled “Label”](#label) A **Label** defines the complete schema for edges, specifying the structure of relationships including source and target types, properties, and indexes. **Properties:** * **name**: Label identifier in the format `service.label` * **schema**: Defines the structure of edges * **src**: Source node type (e.g., user\_id) * **tgt**: Target node type (e.g., product\_id) * **fields**: List of edge properties * **indices**: List of indexes for efficient querying * **storage**: Storage name where data is stored * **dirType**: Direction type (directed/undirected) * **active**: Whether the label is active ### Schema Definition [Section titled “Schema Definition”](#schema-definition) The `schema` property defines what data edges can contain: * **src**: Source node type (STRING, LONG) - typically user\_id * **tgt**: Target node type (STRING, LONG) - typically product\_id, content\_id * **fields**: List of properties, each with: * name: Property name * type: Data type (STRING, LONG, BOOLEAN, etc.) * nullable: Whether the property can be null * description: Property description **Example: Recent Views** ```plaintext source: user_id (LONG) target: product_id (LONG) properties: - created_at (LONG) ``` **Example: Reactions** ```plaintext source: user_id (LONG) target: product_id (LONG) properties: - created_at (LONG) - reaction_type (STRING) - e.g., "heart", "care" ``` ### Index Definition [Section titled “Index Definition”](#index-definition) Indexes enable efficient querying. Each index: * Has a unique name within the label * Specifies a list of fields to index * Each field has a sort order (ASC/DESC) ```plaintext indexes: - name: by_created_at fields: [created_at DESC] - name: by_type_and_time fields: [reaction_type ASC, created_at DESC] ``` A `created_at DESC` index enables fast retrieval of “recent views” without scanning all edges. Indexes are pre-computed during writes and accessed directly during queries. For how indexes are used in queries, see [Query](/design/query/). ## Alias [Section titled “Alias”](#alias) An **Alias** provides an alternative name for a label, enabling different names for the same underlying data. **Properties:** * **name**: Alias identifier in the format `service.alias` * **target**: The label this alias points to (`service.label`) * **active**: Whether the alias is active Aliases are useful for: * Gradual migrations between label names * Providing domain-specific names for shared labels ## Storage [Section titled “Storage”](#storage) **Storage** defines where data is physically stored, specifying the storage backend configuration. **Properties:** * **type**: Storage type (e.g., `hbase`) * **name**: Storage identifier * **conf**: Configuration map with storage-specific settings * **active**: Whether the storage is active Labels reference a storage by name. For available datastore backends, see [Datastore](/design/datastore/). ## Naming Conventions [Section titled “Naming Conventions”](#naming-conventions) | Type | Format | Example | | ------- | ----------------- | ------------------- | | Service | Simple identifier | `myservice` | | Label | `service.label` | `myservice.likes` | | Alias | `service.alias` | `myservice.friends` | | Storage | Simple identifier | `hbase-storage-1` | ## Version Information [Section titled “Version Information”](#version-information) Actionbase has two versions of schema APIs: v2 and v3. Currently, **schema uses v2 API**, while mutation and query operations use v3. The terminology mapping: | v2 Term | v3 Term | | ------- | -------- | | Service | Database | | Label | Table | | Alias | Alias | Schema v3 support is planned for future releases. ## Schema and Operations [Section titled “Schema and Operations”](#schema-and-operations) Schema directly influences how operations work: * **Mutations**: Data must conform to the label’s schema * **Queries**: You must use indexes defined in the label * **Storage**: Configuration determines where data is stored When you perform a mutation, data follows the label’s schema. When you query, you specify an index that was defined in the label. This explicit relationship ensures queries follow optimized paths prepared during schema definition. ## Next Steps [Section titled “Next Steps”](#next-steps) * [Mutation](/design/mutation/): Write data following your schema * [Query](/design/query/): Query data using defined indexes * [API Reference](/api-references/metadata/): Complete API documentation # FAQ > Frequently asked questions about Actionbase ## General Questions [Section titled “General Questions”](#general-questions) ### What is Actionbase? [Section titled “What is Actionbase?”](#what-is-actionbase) Actionbase is a database for serving user interactions. It is designed to handle user activity data in real time. It is built for high-throughput, low-latency OLTP workloads and models user interactions as actor→target relationships with schema-defined properties. Actionbase is **not a general-purpose graph database** like Neo4j. Instead, it is a **graph model–based OLTP database specialized for serving user interactions**, with read patterns optimized at write time. ### Why is it called Actionbase? [Section titled “Why is it called Actionbase?”](#why-is-it-called-actionbase) The name *Actionbase* emphasizes that the system stores user actions, modeled and served as user interactions. ### What do “interactions” and “activities” mean in Actionbase? [Section titled “What do “interactions” and “activities” mean in Actionbase?”](#what-do-interactions-and-activities-mean-in-actionbase) In Actionbase, what is commonly called user activity is modeled as interactions. An interaction captures a user action (such as liking or viewing) as an explicit actor→target relationship with schema-defined properties. Internally, interactions are represented as edges in a graph for efficient querying. ### What problems does Actionbase solve? [Section titled “What problems does Actionbase solve?”](#what-problems-does-actionbase-solve) Actionbase addresses common challenges in storing and serving large-scale user interactions: * **Recent views**: Retrieve the most recent items a user has interacted with * **Likes and reactions**: Store and query reaction states efficiently * **Follows**: Manage directional relationships between users or entities Instead of reimplementing indexing, ordering, and counting logic in each service, Actionbase **pre-computes read-optimized structures at write time**, enabling fast and predictable reads using simple access patterns. ### Is Actionbase production-ready? [Section titled “Is Actionbase production-ready?”](#is-actionbase-production-ready) Yes. Actionbase is used across [Kakao](/project/about-kakao/) services—including KakaoTalk and KakaoShopping—to serve real-time user interactions at scale. It has been running in stable production for years, handling multi-terabyte datasets and serving tens of millions of users with predictable latency. ### What is the history of Actionbase? [Section titled “What is the history of Actionbase?”](#what-is-the-history-of-actionbase) Actionbase was developed internally at [Kakao](/project/about-kakao/) starting in April 2023. After being applied to major services including KakaoShopping and KakaoTalk, it was open-sourced in January 2026. See [Path to Open Source](/project/path-to-open-source/) for the full development history. ## Data Model [Section titled “Data Model”](#data-model) ### What data model does Actionbase use? [Section titled “What data model does Actionbase use?”](#what-data-model-does-actionbase-use) Actionbase uses a **Property Graph model** to represent user interactions: * **Source (actor)**: The entity performing the interaction (for example, a user) * **Target**: The entity being interacted with (for example, a product or content) * **Properties**: Schema-defined attributes such as `created_at`, `reaction_type`, and others This model naturally captures user interactions and supports efficient, property-based access patterns. ### What is the difference between unique-edge and multi-edge? [Section titled “What is the difference between unique-edge and multi-edge?”](#what-is-the-difference-between-unique-edge-and-multi-edge) Actionbase supports two edge types: * **Unique-edge**: One edge per (source, target) pair. The edge is identified by the source and target. * **Multi-edge**: Multiple edges per (source, target) pair. Each edge is identified by a unique `id`. Unique-edge is suitable for relationships like follows, likes, and recent views. Multi-edge is useful when tracking multiple interactions between the same entities, such as gift records. > **Note:** The current documentation primarily describes unique-edge behavior. Multi-edge documentation will be expanded in future releases. ### Does Actionbase support vertices (entity data)? [Section titled “Does Actionbase support vertices (entity data)?”](#does-actionbase-support-vertices-entity-data) Currently, Actionbase focuses on **edges** (interactions between entities). Entity data such as user profiles or product information is typically stored in an RDB or other systems. However, as Actionbase becomes a trusted standard within adopting organizations, there is growing demand to store entity data directly in Actionbase—reducing the need to maintain separate systems. **Vertex support is planned for future releases** to address this need. This will enable storing entity properties alongside interaction data, allowing Actionbase to serve as a more complete data layer. ### Does Actionbase support schemas? [Section titled “Does Actionbase support schemas?”](#does-actionbase-support-schemas) Yes. Actionbase is schema-based. Schemas define: * Source and target identifier types (int, long, string, etc.) * Interaction properties and their types Schemas are used to interpret interaction data and to determine which read-optimized structures (indexes, counts, ordering) are built at write time. For schema definition details, see [Schema](/design/schema/) and [Metadata API](/api-references/metadata/). ### What are some example interaction models? [Section titled “What are some example interaction models?”](#what-are-some-example-interaction-models) **Example 1: Recent views** * Source: `user_id` * Target: `product_id` * Properties: * `created_at` **Example 2: Reactions** * Source: `user_id` * Target: `content_id` * Properties: * `created_at` * `reaction_type` (like, heart, emoji, etc.) ## Architecture [Section titled “Architecture”](#architecture) ### How does Actionbase handle writes? [Section titled “How does Actionbase handle writes?”](#how-does-actionbase-handle-writes) Internally, Actionbase processes interactions using a **state-based mutation model**: 1. Read the current interaction state 2. Apply the incoming interaction as a state transition 3. Persist the resulting state and related read-optimized structures This approach ensures consistent final states even when interaction events arrive out of order (see [How does Actionbase handle out-of-order events?](#how-does-actionbase-handle-out-of-order-events) for details). ### What is write-time optimization? [Section titled “What is write-time optimization?”](#what-is-write-time-optimization) Actionbase’s core design principle is **write-time optimization**. When an interaction is written, Actionbase pre-computes several data structures: 1. **State** The current relationship between source and target 2. **Index** Ordered structures based on schema-defined properties (for example, `created_at DESC`) 3. **Count** Pre-computed counters such as: * Number of items a user interacted with * Number of users who interacted with an item As a result, reads can be satisfied using simple access patterns—such as retrieving recent items, checking existence, counting relationships, or traversing ordered results—without expensive computation at query time. ### How does Actionbase handle out-of-order events? [Section titled “How does Actionbase handle out-of-order events?”](#how-does-actionbase-handle-out-of-order-events) Clients attach timestamps to interaction events. Even if events arrive out of order due to network delays, Actionbase uses these timestamps to compute the correct final state. This guarantees consistency for interaction state transitions such as toggles, updates, or reversals. ## Storage and Infrastructure [Section titled “Storage and Infrastructure”](#storage-and-infrastructure) ### What storage backends does Actionbase support? [Section titled “What storage backends does Actionbase support?”](#what-storage-backends-does-actionbase-support) Actionbase currently uses **HBase** as its primary storage backend, leveraging its durability and horizontal scalability. HBase is suited for large-scale deployments. **[SlateDB](https://slatedb.io/)** is planned as an additional storage backend for small to mid-size deployments in future releases. ### What HBase versions are supported? [Section titled “What HBase versions are supported?”](#what-hbase-versions-are-supported) There are no strict version requirements. Actionbase has been tested and operated with HBase 2.4 and 2.5. ### Why use HBase? [Section titled “Why use HBase?”](#why-use-hbase) HBase provides: * Horizontal scalability * Strong durability and replication * Low-latency random reads and writes * Proven operation at very large scale Actionbase builds on these strengths and adds: * Interaction-aware data modeling * Write-time materialization of read patterns * A higher-level abstraction for serving user interactions In short, Actionbase **builds on proven storage engines while focusing on interaction modeling and serving**. ## Streaming and Data Pipelines [Section titled “Streaming and Data Pipelines”](#streaming-and-data-pipelines) ### What are WAL and CDC? [Section titled “What are WAL and CDC?”](#what-are-wal-and-cdc) Actionbase emits two types of logs during writes: * **WAL (Write-Ahead Log)** WAL records incoming interaction events as-is, enabling replay and recovery. * **CDC (Change Data Capture)** CDC records the resulting interaction state after mutation, making it easy to synchronize data to downstream systems. Both WAL and CDC streams are accessible via Kafka consumers. ### How does Actionbase support analytics and pipelines? [Section titled “How does Actionbase support analytics and pipelines?”](#how-does-actionbase-support-analytics-and-pipelines) WAL and CDC streams can be used for: * Recovery and replay * Feeding analytics systems (OLAP) * Building asynchronous processors and background jobs * Migrating or rebuilding data without downtime ### Can Actionbase handle high write throughput? [Section titled “Can Actionbase handle high write throughput?”](#can-actionbase-handle-high-write-throughput) Yes. For high-frequency interactions (such as recent views): 1. Interaction requests are logged to WAL 2. Responses are returned immediately 3. State, index, and count updates are processed asynchronously This design minimizes request latency while sustaining high write throughput. ## Comparison [Section titled “Comparison”](#comparison) ### How does Actionbase compare to Neo4j? [Section titled “How does Actionbase compare to Neo4j?”](#how-does-actionbase-compare-to-neo4j) Actionbase is **not a competitor to Neo4j**. * **Neo4j** is a general-purpose graph database * **Actionbase** is a specialized OLTP database for serving user interactions Actionbase focuses on a specific interaction-serving workload rather than supporting arbitrary graph queries. ### How does Actionbase compare to traditional RDBMS? [Section titled “How does Actionbase compare to traditional RDBMS?”](#how-does-actionbase-compare-to-traditional-rdbms) Actionbase is **not a replacement for traditional RDBMS**. * **Traditional RDBMS** is a general-purpose relational database * **Actionbase** is a specialized database for serving user interactions at scale Actionbase focuses on a specific interaction-serving workload, leveraging horizontally scalable storage and pre-computing read patterns at write time to deliver predictable, low-latency reads. ## Use Cases [Section titled “Use Cases”](#use-cases) ### What are typical use cases for Actionbase? [Section titled “What are typical use cases for Actionbase?”](#what-are-typical-use-cases-for-actionbase) **1. Interaction Ledger (OLTP)** Store and serve interaction data previously managed in relational databases, while scaling horizontally. **2. Serving Systems (Feeds, Timelines, Recommendations)** Use Actionbase directly as a read-optimized source for feeds, timelines, and recommendation inputs—without separate ETL pipelines or caches. ## Getting Started [Section titled “Getting Started”](#getting-started) ### What are the system requirements? [Section titled “What are the system requirements?”](#what-are-the-system-requirements) * **Runtime**: Java 17 * **Memory**: 4 GB or more recommended * **OS**: Linux (standard server environment) ### How do I get started? [Section titled “How do I get started?”](#how-do-i-get-started) See the [Quick Start Guide](/quick-start/) for installation and setup instructions. ### Do I need to set up HBase separately? [Section titled “Do I need to set up HBase separately?”](#do-i-need-to-set-up-hbase-separately) Yes. Actionbase currently requires HBase as a storage backend. Additional storage backends are planned for future releases. See [HBase Setup](/operations/hbase/) for details. ### What programming languages are supported? [Section titled “What programming languages are supported?”](#what-programming-languages-are-supported) Actionbase provides a **REST API**, making it usable from any language that supports HTTP, including Python, Go, and Java. See [Query API](/api-references/query/) and [Mutation API](/api-references/mutation/) for endpoint details and usage examples. Internally, Actionbase is implemented using Java, Kotlin, and Scala, but users are not exposed to these details. ## Contributing [Section titled “Contributing”](#contributing) ### How can I contribute? [Section titled “How can I contribute?”](#how-can-i-contribute) We welcome contributions. See our [Contributing](/community/contributing/) page for contribution guidelines and development workflow details. # For RDB Users > Understanding Actionbase from a relational database perspective This document is for users familiar with relational databases (RDB) who want to understand how Actionbase fits alongside an RDB-based system. Actionbase is not a general-purpose database. It focuses on relationship data—such as user–item or user–user connections—and models these relationships as interactions. ## Why Consider Actionbase with an RDB? [Section titled “Why Consider Actionbase with an RDB?”](#why-consider-actionbase-with-an-rdb) Relational databases are commonly used to manage transactional data. As a service grows, tables that store large volumes of relationships or interaction histories often present scaling challenges: * Shard key management and hot entities * High read and write throughput * Cross-shard queries and rebalancing Actionbase addresses these challenges by modeling relationships as interactions and leveraging horizontally scalable storage such as HBase. For more details, see [Introduction](/introduction/). ## From Tables to Interactions [Section titled “From Tables to Interactions”](#from-tables-to-interactions) The difference lies in how relationships are represented and queried, not in how entities are defined. ### Relationship Tables in an RDB [Section titled “Relationship Tables in an RDB”](#relationship-tables-in-an-rdb) In an RDB, relationships are typically modeled using tables that: * Connect two entity identifiers * Store limited relationship metadata * Exist mainly to support relationship-based queries Examples include: * User–user relationships (follows, blocks) * User–item relationships (likes, recent views, wishes) * User–content relationships (reactions, comments) These tables are often accessed from one side of the relationship and grow large over time. ### Interactions in Actionbase [Section titled “Interactions in Actionbase”](#interactions-in-actionbase) In Actionbase: * Entities are identified by source and target * Relationships are modeled as interactions with schema-defined properties * Read-optimized structures (indexes, counts) are pre-computed at write time This design enables fast lookups such as: * Listing items a user interacted with * Counting interactions per user or per item * Checking whether a specific interaction exists RDB tables whose primary role is to represent relationships can often be mapped directly to interactions. ## When Actionbase Is a Good Fit [Section titled “When Actionbase Is a Good Fit”](#when-actionbase-is-a-good-fit) Actionbase is a good fit when: * Interaction data dominates table volume * Queries focus on listing or traversing relationships * Scaling these tables in an RDB becomes operationally complex Examples include: * Social graphs (follows, blocks) * User–item interactions (likes, recent views, wishes) * User–content interactions (reactions, comments) See [FAQ - What problems does Actionbase solve?](/faq/#what-problems-does-actionbase-solve) for more examples. ## Using Actionbase Together with an RDB [Section titled “Using Actionbase Together with an RDB”](#using-actionbase-together-with-an-rdb) Actionbase is designed to complement an RDB, not replace it. **Best practice**: If you are considering Actionbase, start by migrating only the large-scale user interaction tables that present scaling challenges—such as likes, follows, and recent views. Keep transactional and domain data in your RDB. A common pattern is: 1. Transactional and domain data remains in the RDB 2. Large-scale user interaction data is stored in Actionbase 3. Interaction-centric queries are served from Actionbase This separates transactional workloads from interaction-heavy access patterns. ## Example: Mapping an RDB Table to Actionbase [Section titled “Example: Mapping an RDB Table to Actionbase”](#example-mapping-an-rdb-table-to-actionbase) ### RDB Table [Section titled “RDB Table”](#rdb-table) ```sql CREATE TABLE user_product_wish ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(255), product_id VARCHAR(255), created_at TIMESTAMP, visibility VARCHAR(50) ); ``` ``` erDiagram user_product_wish { BIGINT id PK VARCHAR user_id VARCHAR product_id TIMESTAMP created_at VARCHAR visibility } ``` ### Actionbase Table (Label) [Section titled “Actionbase Table (Label)”](#actionbase-table-label) In Actionbase, the same data is modeled as edges in a graph: ``` graph LR Alice((Alice)) -->|wishes| Phone((📱 Phone)) Alice -->|wishes| Laptop((💻 Laptop)) Bob((Bob)) -->|wishes| Phone ``` Each arrow represents an interaction (edge) with properties such as `created_at` and `visibility`. This table maps to a Label with the following schema: * **source**: user\_id (STRING) * **target**: product\_id (STRING) * **properties**: * created\_at (LONG) * visibility (STRING) The `id` column is not needed—Actionbase identifies edges by source and target. Indexes can be defined for efficient queries: * `created_at DESC` for retrieving recent wishes * `visibility ASC, created_at DESC` for filtering by visibility For schema definition details, see [Schema](/design/schema/). For a complete walkthrough, see [Quick Start](/quick-start/). # Build Your Commerce App with Live FOMO Counters > Build FOMO-based commerce features using real-time aggregation This guide will demonstrate how to build FOMO (Fear Of Missing Out) based commerce features using Actionbase’s **real-time aggregation** capabilities. ## Features to Implement [Section titled “Features to Implement”](#features-to-implement) * “N users viewing now” - real-time viewer count * “N users wished recently” - time-windowed wish count * Time-bucketed activity aggregation (hourly, minutely, etc.) * Property-based aggregation ## Actionbase Capability: Real-Time Aggregation [Section titled “Actionbase Capability: Real-Time Aggregation”](#actionbase-capability-real-time-aggregation) Actionbase supports real-time aggregation of user interactions: * **Time buckets**: Aggregate by hour, minute, or custom intervals * **Discrete values**: Aggregate by property values * **Activity counts**: Count user interactions in real time * **Property aggregation**: Sum, count, and other aggregations on edge properties This enables commerce features like live viewer counts and trending indicators without external caching or batch processing. ## Coming Soon [Section titled “Coming Soon”](#coming-soon) This guide will be available in a future release. Start with the [Build Your Social Media App](/guides/build-your-social-media-app/) guide to learn Actionbase’s core functionality first. # Build Your Social Gifting App > Build social-commerce gifting features using multi-edge This guide will demonstrate how to build social-commerce gifting features using Actionbase’s **multi-edge** capabilities. ## Features to Implement [Section titled “Features to Implement”](#features-to-implement) * Gift sending and receiving history * Multiple gifts between the same users * Gift statistics and recommendations ## Actionbase Capability: Multi-Edge [Section titled “Actionbase Capability: Multi-Edge”](#actionbase-capability-multi-edge) By default, Actionbase edges are **unique-edge**—identified by (source, target) pairs. Multi-edge extends this model: * **Unique-edge**: One edge per (source, target) pair * **Multi-edge**: Multiple edges per (source, target) pair, each identified by a unique `id` Multi-edge is useful when you need to track multiple interactions between the same entities, such as gift records where the same user can send multiple gifts to the same recipient. ## Coming Soon [Section titled “Coming Soon”](#coming-soon) This guide will be available in a future release. Start with the [Build Your Social Media App](/guides/build-your-social-media-app/) guide to learn Actionbase’s core functionality first. # Build Your Social Media App > Build social features using Actionbase core functionality This guide walks through building a simple social media application using Actionbase. It focuses on modeling and serving activity data for core features such as likes, feeds, and timelines, and demonstrates how Actionbase can be used as the foundation for activity-driven services. Detailed, step-by-step instructions will be added as the hands-on guide evolves. # Encoding > Internal data encoding and storage details This document describes how Actionbase encodes and stores data internally. This information is primarily for contributors and those who need to understand the low-level storage format. For high-level concepts, see [Core Concepts](/design/concepts/). ## Row Types [Section titled “Row Types”](#row-types) Actionbase stores edge data using multiple row types in the datastore backend. Each row type serves a specific query purpose. | Row Type | Type Code | Purpose | Query Type | | ---------- | --------- | ------------------ | ---------- | | Edge State | -3 | Current edge state | Get | | Edge Index | -4 | Index entries | Scan | | Edge Count | -2 | Edge counts | Count | ## Edge State (Type Code: -3) [Section titled “Edge State (Type Code: -3)”](#edge-state-type-code--3) Stores the current state of edges for Get queries. ### Key Structure [Section titled “Key Structure”](#key-structure) ```plaintext [4-byte hash] + [1-byte + source] + [1-byte + table code] + [1-byte + type code(-3)] + [1-byte + target] ``` ### Value Structure [Section titled “Value Structure”](#value-structure) | Field | Type | Description | | ---------- | ------- | ----------------------------------------- | | active | Boolean | Edge active status | | version | Long | Edge version | | properties | Map | Property values with version per property | | createdAt | Long | Creation timestamp | | deletedAt | Long | Deletion timestamp | ## Edge Index (Type Code: -4) [Section titled “Edge Index (Type Code: -4)”](#edge-index-type-code--4) Stores index entries for Scan queries. Uses **Narrow Row** format for high-cardinality indexes. ### Key Structure [Section titled “Key Structure”](#key-structure-1) ```plaintext [4-byte hash] + [1-byte + directed source] + [1-byte + table code] + [1-byte + type code(-4)] + [1-byte + direction] + [1-byte + index code] + [(1-byte + N) * # index values] + [1-byte + directed target] ``` ### Value Structure [Section titled “Value Structure”](#value-structure-1) | Field | Type | Description | | ---------- | ---- | --------------- | | version | Long | Edge version | | properties | Map | Property values | ## Edge Count (Type Code: -2) [Section titled “Edge Count (Type Code: -2)”](#edge-count-type-code--2) Stores counters for Count queries. Uses the datastore backend’s `increment` operation. ### Key Structure [Section titled “Key Structure”](#key-structure-2) ```plaintext [4-byte hash] + [1-byte + directed source] + [1-byte + table code] + [1-byte + type code(-2)] + [1-byte + direction] ``` ### Value Structure [Section titled “Value Structure”](#value-structure-2) | Field | Type | Description | | ----- | ---- | ----------- | | count | Long | Edge count | ## Row Key Encoding [Section titled “Row Key Encoding”](#row-key-encoding) All row keys follow a common pattern: ```plaintext [4-byte hash] + [1-byte + source] + [1-byte + table code] + [1-byte + type code] + [additional fields...] ``` | Component | Size | Purpose | | ------------- | -------- | --------------------------------- | | Hash | 4 bytes | xxhash32 for region distribution | | Type Code | 1 byte | Identifies data type (-2, -3, -4) | | Source/Target | Variable | Prefixed with 1-byte length | The hash prefix ensures even distribution across HBase regions, preventing hotspots. ## Value Encoding [Section titled “Value Encoding”](#value-encoding) Actionbase uses byte headers to maintain type information: ### Format [Section titled “Format”](#format) ```plaintext [1-byte type information] + [actual value] ``` ### Type Information [Section titled “Type Information”](#type-information) | Aspect | Description | | ---------- | -------------------------------------------------- | | Type Code | Distinguishes NULL, STRING, INT, FLOAT, JSON, etc. | | Sort Order | Includes ASC/DESC for index ordering | | Encoding | Values encoded according to sort order | ### Version Tracking [Section titled “Version Tracking”](#version-tracking) | Row Type | Version Scope | | --------- | -------------------- | | EdgeState | Version per property | | EdgeIndex | Version per edge | ## Key Design Principles [Section titled “Key Design Principles”](#key-design-principles) ### Hash Prefix [Section titled “Hash Prefix”](#hash-prefix) The 4-byte xxhash32 prefix ensures: * Even distribution across storage regions * Prevention of write hotspots * Balanced read/write load ### Negative Type Codes [Section titled “Negative Type Codes”](#negative-type-codes) Type codes use negative values (-2, -3, -4) to: * Separate from user data * Enable efficient key range scanning * Provide clear type identification ### Length-Prefixed Strings [Section titled “Length-Prefixed Strings”](#length-prefixed-strings) All variable-length fields use 1-byte length prefix: * Enables efficient parsing * Supports binary-safe encoding * Allows prefix-based scanning # Introduction > Introduction to Actionbase Actionbase is a database for serving user interactions. It is designed to serve interaction-driven data in real time and is production-proven across Kakao services. ## Overview [Section titled “Overview”](#overview) Actionbase is built for high-throughput, low-latency OLTP workloads where user interactions are continuously written and queried. Actionbase focuses on serving interaction-derived data—such as **recent views**, **likes**, **reactions**, and **follows**—that power product listings, recommendations, feeds, and other interaction-driven surfaces in large-scale services. User interactions naturally form actor→target relationships with associated properties. Actionbase models these relationships using a graph data model and materializes read-optimized structures at write time, enabling fast and predictable queries without expensive read-time computation. When backed by HBase, Actionbase inherits strong durability and horizontal scalability, and provides a higher-level abstraction tailored for real-time interaction serving. ## Design Goals [Section titled “Design Goals”](#design-goals) * **Shared Interaction Layer** Provide a unified platform for storing and serving user interactions as a shared, canonical interaction graph. * **Natural Interaction Modeling** Model interactions as actor→target relationships with schema-defined properties, closely reflecting how user interactions appear in real applications. * **Write-Time Optimization** Optimize for reads at write time, enabling fast and predictable queries without expensive read-time computation. * **Leverage Proven Storage** Build on the strengths of existing storage engines (for example, HBase), allowing Actionbase to focus on interaction modeling and serving rather than reimplementing durability, scalability, or distribution. ## Key Features [Section titled “Key Features”](#key-features) * **Write-Time Materialization** Pre-compute the data required for fast, predictable reads at write time, eliminating service-specific indexing and counting logic. * **Interaction-Oriented Graph Model** Represent user interactions as actor→target edges with schema-defined properties for efficient querying. * **Unified REST API** Expose a simple API for querying and mutating interaction-driven data. * **WAL / CDC Integration** Emit write-ahead logs and change data capture streams for recovery, replay, asynchronous processing, and downstream data pipelines. ## Datastore [Section titled “Datastore”](#datastore) Actionbase currently uses HBase as its primary storage backend, leveraging its durability and horizontal scalability. HBase is suited for large-scale deployments. [SlateDB](https://slatedb.io/) is planned as an additional storage backend for small to mid-size deployments in future releases. ## Production Usage [Section titled “Production Usage”](#production-usage) Actionbase is used across Kakao services—including KakaoTalk and KakaoShopping—to power real-time user interaction serving at scale. It has been running in stable production for years, delivering predictable reads, consistent writes, and reliable handling of multi-terabyte datasets. # llms.txt > Authoritative Actionbase documentation for LLM and RAG systems This document is for configuring LLM or RAG systems. The linked `llms-*.txt` files are intended for machine consumption and define the authoritative Actionbase documentation. ## Documentation Files [Section titled “Documentation Files”](#documentation-files) Add **one** of the following to your LLM or RAG system: * **Quick / Overview**: [`/llms-small.txt`](/llms-small.txt) Core concepts and common usage. * **Full / Technical Reference**: [`/llms-full.txt`](/llms-full.txt) Complete APIs, semantics, and edge cases. ## Scope [Section titled “Scope”](#scope) * The `llms-*.txt` files are the **single source of truth**. * Do not infer behavior not explicitly documented. * If information is missing, treat it as unspecified. ## Recommended System Prompt [Section titled “Recommended System Prompt”](#recommended-system-prompt) ```plaintext You are an assistant answering questions about Actionbase. Answer using only the provided Actionbase documentation. If the documentation does not specify the answer, say so explicitly. ``` # Benchmarks > Performance benchmarks and metrics for Actionbase This page provides performance benchmarks and metrics for Actionbase. Actionbase is production-proven, serving tens of millions of users in [Kakao](/project/about-kakao/) services including KakaoTalk and KakaoShopping. **Note:** At this initial release stage, we are providing benchmark results at this level. The results presented here contain internal information and are provided in approximate values. We plan to provide transparent benchmark results using generalized datasets in future releases. ## Current Configuration [Section titled “Current Configuration”](#current-configuration) The current infrastructure configuration: * **Ingress**: VM-based nodes, 3 nodes in each of 3 regions (9 nodes total) * **Worker**: PM-based nodes, 3 nodes in each of 3 regions (9 nodes total) * **HBase**: PM-based nodes, 3 nodes in each of 3 regions (9 nodes total) ## Performance in Production Traffic [Section titled “Performance in Production Traffic”](#performance-in-production-traffic) In a typical production workload with approximately 15% write and 85% read operations (based on average characteristics of Kakao services), Actionbase achieves: * **Throughput**: 60k+ TPS (Read \~5ms, Write \~20ms) ## Scalability [Section titled “Scalability”](#scalability) Internal experiments have confirmed linear scalability. Performance increases proportionally as additional Ingress and Worker nodes are added to the cluster. # CLI > Command-line interface for managing and maintaining Actionbase clusters This guide introduces the Actionbase CLI, which is used to interact with Actionbase during local development and hands-on exercises. The CLI will provide basic commands for initializing projects, running examples, and inspecting activity data. More detailed usage instructions and command references will be added as the CLI evolves. # HBase Configuration > HBase setup and configuration guide for Actionbase HBase configuration guides and best practices will be provided in future releases. Actionbase uses HBase as its primary storage backend. At Kakao, Actionbase is deployed with HBase in production environments. This documentation will include setup instructions, configuration best practices, performance tuning, monitoring, and troubleshooting guides. For now, see [Local Provisioning](/provisioning/local/) to run HBase locally for development and testing. # About Kakao > Learn about Kakao, the company behind Actionbase Kakao Corporation (카카오) is a leading internet and mobile platform company in Korea. KakaoTalk is the most widely used messaging service in the country. The company operates major services in messaging, e-commerce, and payments, serving tens of millions of users. Actionbase was developed at Kakao to serve real-time user interactions across these services at scale. It is now open source and available for the community. ## Learn More [Section titled “Learn More”](#learn-more) * [Kakao Corporation](https://www.kakaocorp.com/) * [Kakao Tech Blog](https://tech.kakao.com/) # Path to Open Source > Actionbase project development history before open source This document covers the development history of Actionbase before it was open-sourced. Future development will be tracked in public repositories. ## MVP Phase [Section titled “MVP Phase”](#mvp-phase) The MVP phase represents the entire period from project inception to open-source release. During this period, Actionbase was applied to various services and evolved through real-world use. ### Phase 1: Foundation Building (April 2023) [Section titled “Phase 1: Foundation Building (April 2023)”](#phase-1-foundation-building-april-2023) **Focus:** Validating technical feasibility and establishing a technical foundation. This period was invested to ensure a technical base that would support stable development in subsequent phases. Key milestones: * Project kickoff and initial planning * Core design and implementation ### Phase 2: Service Integration (March 2024) [Section titled “Phase 2: Service Integration (March 2024)”](#phase-2-service-integration-march-2024) **Focus:** Validating through real-world service deployment. The system was safely integrated into production by maintaining the existing system, applying dual writes, and gradually migrating read functionality after sufficient validation. Key activities: * First production service write/read migration ### Phase 3: Stabilization (July 2024) [Section titled “Phase 3: Stabilization (July 2024)”](#phase-3-stabilization-july-2024) **Focus:** System stabilization and tenant expansion. Starting from a multi-tenant architecture, this phase introduced support for independent tenant configurations to ensure service isolation and enable isolated deployments. Key improvements: * System stabilization and tenant expansion * Support for independent tenant configurations to isolate services * Operations automation and monitoring enhancement * Applied across KakaoShopping ### Phase 4: Building Sustainable Operations (January 2025 - April 2025) [Section titled “Phase 4: Building Sustainable Operations (January 2025 - April 2025)”](#phase-4-building-sustainable-operations-january-2025---april-2025) **Focus:** Improving operational efficiency and developer experience, addressing technical debt. During this phase, Actionbase continued to be applied to various services while improving its operational foundation. Key efforts included reducing operational overhead, improving developer tooling, and addressing accumulated technical debt. ### Phase 5: Preparing for Open Source (May 2025 - December 2025) [Section titled “Phase 5: Preparing for Open Source (May 2025 - December 2025)”](#phase-5-preparing-for-open-source-may-2025---december-2025) **Focus:** Preparing for open-source release and expanding adoption across Kakao’s major services. Building on the foundation from Phase 4, this phase focused on making Actionbase ready for open source. The reimplementation process led to the decision to open-source Actionbase. While legacy code remains, the reimplementation continues to progress toward a more maintainable codebase. During this period, Actionbase expanded its adoption across [Kakao](/project/about-kakao/)’s major services including KakaoShopping and KakaoTalk. Key changes: * Maintained API compatibility with existing systems * Redesigned API structure * Repository separation and organization * Separated internal and open-source areas * Expanded adoption to major services in KakaoShopping and KakaoTalk ## Open Source Release (January 2026) [Section titled “Open Source Release (January 2026)”](#open-source-release-january-2026) After this MVP period, during which Actionbase was applied to various services, Actionbase was open-sourced in January 2026. The codebase released is the same one that was developed, operated, and refined inside Kakao. Only minimal modifications were applied to remove security-sensitive or organization-specific details, and some internal features will be opened gradually as they are reviewed. ### Why We Open-Sourced Actionbase [Section titled “Why We Open-Sourced Actionbase”](#why-we-open-sourced-actionbase) **1. Transparent real-world history** The codebase grew organically in production environments. By releasing it as-is (with only essential sanitization), we aim to share its genuine journey rather than a rewritten or polished version. **2. Long-term sustainability** The system has gone through multiple generations of requirements, contributors, and architectural changes. Open-sourcing it helps create a healthier long-term lifecycle and encourages community-driven improvement. ### Acknowledgements [Section titled “Acknowledgements”](#acknowledgements) This work was supported by Kakao’s leaders and engineers, who helped the team build and operate the system. Special appreciation goes to the HBase engineers at Kakao. Actionbase’s reliability benefits from the stability provided by HBase. This release reflects the efforts of those who contributed to the system and the teams who supported its development. We look forward to continuing this project together with the open-source community. # Kubernetes Provisioning > Kubernetes-based deployment guide for Actionbase Kubernetes-based provisioning guides and best practices will be provided in future releases. At Kakao, Actionbase is currently provisioned using Kubernetes. This documentation will include deployment manifests, high availability configurations, performance tuning, monitoring setup, and security best practices. For now, please refer to [Quick Start](/quick-start/) or [Local Provisioning](/provisioning/local/) to get started. # Local Provisioning > Setting up Actionbase for local development and testing This guide describes how to set up a local development environment for Actionbase. It will cover the basic prerequisites and steps required to run Actionbase locally for development and hands-on exercises. More detailed setup instructions will be added as the local development workflow is finalized. # Quick Start > Run Actionbase locally and define your first interactions. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * Java 17 ## Start Actionbase [Section titled “Start Actionbase”](#start-actionbase) Clone the repository and start the server: ```bash git clone https://github.com/kakao/actionbase.git cd actionbase ./gradlew :server:bootRun ``` ```plaintext _ _ _ _ / \ ___| |_(_) ___ _ __ | |__ __ _ ___ ___ / _ \ / __| __| |/ _ \| '_ \| '_ \ / _` / __|/ _ \ / ___ \ (__| |_| | (_) | | | | |_) | (_| \__ \ __/ /_/ \_\___|\__|_|\___/|_| |_|_.__/ \__,_|___/\___| (0.0.1-SNAPSHOT, ...................) * Java: 17.0.13 * Tenant: ab-none * startUp: ................... * activeProfiles: default * Datastore: MEMORY {} ``` > ✅ The server is now running at `http://localhost:8080` and ready to accept requests. > ℹ️ `bootRun` runs in the foreground. Use another terminal for the next steps. ## Define metadata [Section titled “Define metadata”](#define-metadata) > Note > > Metadata management currently uses **v2 APIs** (service / label), while data read & write operations use **v3 APIs** (database / table). Before writing any interaction data, you must define its schema and indexes using metadata APIs. In Actionbase, interactions are stored as edges in a graph model. Create a service and label to define the structure of your edges. 1. **Create a service** Create a namespace for grouping labels. In this guide, we use `awesome` as an example service name. See [Metadata API Reference](/api-references/metadata/#13-create-service) for details. ```bash curl -X POST "http://localhost:8080/graph/v2/service/awesome" \ -H "Content-Type: application/json" \ -d '{"desc":"Sample"}' ``` Expected response: ```json { "status": "CREATED", "result": { "active": true, "name": "awesome", "desc": "Sample" } } ``` 2. **Create a label** Define the edge schema (source, target, properties) and indexes. See [Metadata API Reference](/api-references/metadata/#23-create-label) for details. Key fields: * `src`, `tgt`: source and target node IDs * `fields`: edge properties * `indices`: used for ordered scans ```bash curl -X POST "http://localhost:8080/graph/v2/service/awesome/label/likes" \ -H "Content-Type: application/json" \ -d '{ "desc":"Like", "type":"INDEXED", "schema":{ "src":{"type":"STRING"}, "tgt":{"type":"STRING"}, "fields":[{"name":"created_at","type":"LONG","nullable":false}] }, "dirType":"BOTH", "storage":"datastore://awesome/likes", "indices":[{"name":"created_at_desc","fields":[{"name":"created_at","order":"DESC"}]}] }' ``` Expected response: ```json { "status": "CREATED", "result": { "active": true, "name": "awesome.likes", "desc": "Like", "type": "INDEXED", "schema": { "src": {"type": "STRING", "desc": ""}, "tgt": {"type": "STRING", "desc": ""}, "fields": [{"name": "created_at", "type": "LONG", "nullable": false, "desc": ""}] }, "dirType": "BOTH", "storage": "datastore://awesome/likes", "groups": [], "indices": [{ "name": "created_at_desc", "fields": [{"name": "created_at", "order": "DESC"}], "desc": "" }], "event": false, "readOnly": false, "mode": "SYNC" } } ``` ## Write edges [Section titled “Write edges”](#write-edges) Insert interaction edges to record user actions. 1. **Insert edges** Record user actions as edges. See [Mutation API Reference](/api-references/mutation/#1-edge-mutation) for details. Key fields: * `version`: edge version used for concurrency control and out-of-order event handling * `created_at`: application-level timestamp stored as a property In this example, we use the same timestamp for both `version` and `created_at` for simplicity. Use the timestamp when the user performed the action. See [FAQ - How does Actionbase handle out-of-order events?](/faq/#how-does-actionbase-handle-out-of-order-events) for details. ``` graph LR Alice((Alice)) -->|likes| Phone((📱 Phone)) Alice -->|likes| Laptop((💻 Laptop)) Bob((Bob)) -->|likes| Phone ``` ```bash curl -X POST "http://localhost:8080/graph/v3/databases/awesome/tables/likes/edges" \ -H "Content-Type: application/json" \ -d '{ "mutations":[ {"type":"INSERT","edge":{"version":1767571200000,"source":"Alice","target":"Phone","properties":{"created_at":1767571200000}}}, {"type":"INSERT","edge":{"version":1767571200000,"source":"Alice","target":"Laptop","properties":{"created_at":1767571200000}}}, {"type":"INSERT","edge":{"version":1767571200000,"source":"Bob","target":"Phone","properties":{"created_at":1767571200000}}} ] }' ``` Expected response: ```json { "results": [ {"source": "Alice", "target": "Laptop", "status": "CREATED", "count": 1}, {"source": "Alice", "target": "Phone", "status": "CREATED", "count": 1}, {"source": "Bob", "target": "Phone", "status": "CREATED", "count": 1} ] } ``` ## Read edges [Section titled “Read edges”](#read-edges) Query edges using get, scan, and count operations. 1. **Get** Retrieve a specific edge by source and target. See [Query API Reference](/api-references/query/#2-get) for details. ```bash curl -X GET "http://localhost:8080/graph/v3/databases/awesome/tables/likes/edges/get?source=Alice&target=Phone" ``` Expected response: ```json { "edges": [{ "version": 1767571200000, "source": "Alice", "target": "Phone", "properties": {"created_at": 1767571200000}, "context": {} }], "count": 1, "total": 1, "offset": null, "hasNext": false, "context": {} } ``` 2. **Scan (Out)** List outgoing edges from a source, ordered by index. See [Query API Reference](/api-references/query/#3-scan) for details. ```bash curl -X GET "http://localhost:8080/graph/v3/databases/awesome/tables/likes/edges/scan/created_at_desc?start=Alice&direction=OUT" ``` Expected response: ```json { "edges": [ { "version": 1767571200000, "source": "Alice", "target": "Laptop", "properties": {"created_at": 1767571200000}, "context": {} }, { "version": 1767571200000, "source": "Alice", "target": "Phone", "properties": {"created_at": 1767571200000}, "context": {} } ], "count": 2, "total": -1, "offset": null, "hasNext": false, "context": {} } ``` 3. **Scan (In)** List incoming edges to a target, ordered by index. See [Query API Reference](/api-references/query/#3-scan) for details. ```bash curl -X GET "http://localhost:8080/graph/v3/databases/awesome/tables/likes/edges/scan/created_at_desc?start=Phone&direction=IN" ``` Expected response: ```json { "edges": [ { "version": 1767571200000, "source": "Alice", "target": "Phone", "properties": {"created_at": 1767571200000}, "context": {} }, { "version": 1767571200000, "source": "Bob", "target": "Phone", "properties": {"created_at": 1767571200000}, "context": {} } ], "count": 2, "total": -1, "offset": null, "hasNext": false, "context": {} } ``` 4. **Count** Count edges for a specific direction. See [Query API Reference](/api-references/query/#1-count) for details. ```bash curl -X GET "http://localhost:8080/graph/v3/databases/awesome/tables/likes/edges/count?start=Alice&direction=OUT" ``` Expected response: ```json { "start": "Alice", "direction": "OUT", "count": 2, "context": {} } ``` Count incoming edges: ```bash curl -X GET "http://localhost:8080/graph/v3/databases/awesome/tables/likes/edges/count?start=Phone&direction=IN" ``` Expected response: ```json { "start": "Phone", "direction": "IN", "count": 2, "context": {} } ``` ## Stop Actionbase [Section titled “Stop Actionbase”](#stop-actionbase) Terminate the `bootRun` process (Ctrl+C) to stop the server.