gRPC API Documentation

Note: The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

MDDB provides a high-performance gRPC API alongside the HTTP/JSON API. The gRPC API offers significant performance improvements through binary protocol buffers and HTTP/2.

Table of Contents

Overview

gRPC Endpoint: localhost:11024
Protocol: HTTP/2 + Protocol Buffers
Reflection: Enabled (for grpcurl and debugging)

Why gRPC?

Performance Benefits

FeatureHTTP/JSONgRPC
Payload Size100%~30% (70% smaller)
SerializationJSON textBinary protobuf
TransportHTTP/1.1HTTP/2
CompressionOptional gzipBuilt-in
StreamingLimitedFull duplex
Type SafetyRuntimeCompile-time

Use Cases

Use gRPC when:

  • Performance is critical
  • You need type safety
  • Building microservices
  • High-throughput operations
  • Streaming large datasets

Use HTTP when:

  • Debugging with curl
  • Browser-based clients
  • Simple integrations
  • Human-readable logs

Getting Started

Prerequisites

brew install protobuf make install-grpc-tools

Testing with grpcurl

brew install grpcurl grpcurl -plaintext localhost:11024 list grpcurl -plaintext localhost:11024 describe mddb.MDDB grpcurl -plaintext -d '{"collection":"blog","key":"test","lang":"en_US"}' \ localhost:11024 mddb.MDDB/Get

Service Definition

The complete service definition is in proto/mddb.proto.

Available RPCs (58 total)

CategoryRPCRequest โ†’ Response
Document ManagementAddAddRequest โ†’ Document
AddBatchAddBatchRequest โ†’ AddBatchResponse
UpdateDocumentUpdateDocumentRequest โ†’ Document
UpdateBatchUpdateBatchRequest โ†’ UpdateBatchResponse
DeleteDocumentDeleteDocumentRequest โ†’ DeleteDocumentResponse
DeleteBatchDeleteBatchRequest โ†’ DeleteBatchResponse
DeleteCollectionDeleteCollectionRequest โ†’ DeleteCollectionResponse
GetGetRequest โ†’ Document
GetDocumentMetaGetDocumentMetaRequest โ†’ GetDocumentMetaResponse
SearchSearchRequest โ†’ SearchResponse
IngestIngestRequest โ†’ IngestResponse
ImportURLImportURLRequest โ†’ Document
SetTTLSetTTLRequest โ†’ Document
Full-Text SearchFTSFTSRequest โ†’ FTSResponse
Vector / SemanticVectorSearchVectorSearchRequest โ†’ VectorSearchResponse
VectorReindexVectorReindexRequest โ†’ VectorReindexResponse
VectorStatsVectorStatsRequest โ†’ VectorStatsResponse
Hybrid & CrossHybridSearchHybridSearchRequest โ†’ HybridSearchResponse
CrossSearchCrossSearchRequest โ†’ CrossSearchResponse
AnalysisClassifyClassifyRequest โ†’ ClassifyResponse
FindDuplicatesFindDuplicatesRequest โ†’ FindDuplicatesResponse
GetChecksumGetChecksumRequest โ†’ GetChecksumResponse
GetMetaKeysGetMetaKeysRequest โ†’ GetMetaKeysResponse
RevisionsListRevisionsListRevisionsRequest โ†’ ListRevisionsResponse
RestoreRevisionRestoreRevisionRequest โ†’ Document
TruncateTruncateRequest โ†’ TruncateResponse
Export & BackupExportExportRequest โ†’ stream ExportChunk
BackupBackupRequest โ†’ BackupResponse
RestoreRestoreRequest โ†’ RestoreResponse
FTS ConfigListSynonymsListSynonymsRequest โ†’ ListSynonymsResponse
AddSynonymAddSynonymRequest โ†’ AddSynonymResponse
DeleteSynonymDeleteSynonymRequest โ†’ DeleteSynonymResponse
ListStopwordsListStopwordsRequest โ†’ ListStopwordsResponse
AddStopwordsAddStopwordsRequest โ†’ AddStopwordsResponse
DeleteStopwordsDeleteStopwordsRequest โ†’ DeleteStopwordsResponse
SchemasSetSchemaSetSchemaRequest โ†’ SetSchemaResponse
GetSchemaGetSchemaRequest โ†’ GetSchemaResponse
DeleteSchemaDeleteSchemaRequest โ†’ DeleteSchemaResponse
ListSchemasListSchemasRequest โ†’ ListSchemasResponse
ValidateDocumentValidateDocumentRequest โ†’ ValidateDocumentResponse
WebhooksRegisterWebhookRegisterWebhookRequest โ†’ WebhookProto
ListWebhooksListWebhooksRequest โ†’ ListWebhooksResponse
DeleteWebhookDeleteWebhookRequest โ†’ DeleteWebhookResponse
AutomationListAutomationListAutomationRequest โ†’ ListAutomationResponse
CreateAutomationCreateAutomationRequest โ†’ AutomationRuleProto
GetAutomationGetAutomationRequest โ†’ AutomationRuleProto
UpdateAutomationUpdateAutomationRequest โ†’ AutomationRuleProto
DeleteAutomationDeleteAutomationRequest โ†’ DeleteAutomationResponse
TestAutomationTestAutomationRequest โ†’ TestAutomationResponse
GetAutomationLogsGetAutomationLogsRequest โ†’ GetAutomationLogsResponse
Collection ConfigGetCollectionConfigGetCollectionConfigRequest โ†’ GetCollectionConfigResponse
SetCollectionConfigSetCollectionConfigRequest โ†’ SetCollectionConfigResponse
ListCollectionConfigsListCollectionConfigsRequest โ†’ ListCollectionConfigsResponse
SystemStatsStatsRequest โ†’ StatsResponse

Full service definition: proto/mddb.proto

Message Types

Document

message Document { string id = 1; string key = 2; string lang = 3; map<string, MetaValues> meta = 4; string content_md = 5; int64 added_at = 6; int64 updated_at = 7;
}

AddRequest

message AddRequest { string collection = 1; string key = 2; string lang = 3; map<string, MetaValues> meta = 4; string content_md = 5;
}

Client Examples

Go Client

package main import ( "context" "log" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" pb "mddb/proto"
) func main() { // Connect to server conn, err := grpc.Dial("localhost:11024", grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Fatal(err) } defer conn.Close() client := pb.NewMDDBClient(conn) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Add a document doc, err := client.Add(ctx, &pb.AddRequest{ Collection: "blog", Key: "hello-world", Lang: "en_US", Meta: map[string]*pb.MetaValues{ "category": {Values: []string{"blog", "tutorial"}}, "author": {Values: []string{"John Doe"}}, }, ContentMd: "# Hello World\n\nWelcome to MDDB!", }) if err != nil { log.Fatal(err) } log.Printf("Document added: %s", doc.Id) // Get a document doc, err = client.Get(ctx, &pb.GetRequest{ Collection: "blog", Key: "hello-world", Lang: "en_US", Env: map[string]string{ "year": "2024", }, }) if err != nil { log.Fatal(err) } log.Printf("Content: %s", doc.ContentMd) // Search documents resp, err := client.Search(ctx, &pb.SearchRequest{ Collection: "blog", FilterMeta: map[string]*pb.MetaValues{ "category": {Values: []string{"blog"}}, }, Sort: "updatedAt", Asc: false, Limit: 10, }) if err != nil { log.Fatal(err) } log.Printf("Found %d documents", len(resp.Documents)) // Get stats stats, err := client.Stats(ctx, &pb.StatsRequest{}) if err != nil { log.Fatal(err) } log.Printf("Total documents: %d", stats.TotalDocuments)
}

Python Client

import grpc
import mddb_pb2
import mddb_pb2_grpc channel = grpc.insecure_channel('localhost:11024')
client = mddb_pb2_grpc.MDD BStub(channel) doc = client.Add(mddb_pb2.AddRequest( collection='blog', key='hello-world', lang='en_US', meta={ 'category': mddb_pb2.MetaValues(values=['blog', 'tutorial']), 'author': mddb_pb2.MetaValues(values=['John Doe']) }, content_md='# Hello World\n\nWelcome to MDDB!'
))
print(f'Document added: {doc.id}') doc = client.Get(mddb_pb2.GetRequest( collection='blog', key='hello-world', lang='en_US', env={'year': '2024'}
))
print(f'Content: {doc.content_md}') resp = client.Search(mddb_pb2.SearchRequest( collection='blog', filter_meta={ 'category': mddb_pb2.MetaValues(values=['blog']) }, sort='updatedAt', asc=False, limit=10
))
print(f'Found {len(resp.documents)} documents') stats = client.Stats(mddb_pb2.StatsRequest())
print(f'Total documents: {stats.total_documents}')

Node.js Client

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader'); // Load proto file
const packageDefinition = protoLoader.loadSync('mddb.proto', { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true
});
const mddb = grpc.loadPackageDefinition(packageDefinition).mddb; // Create client
const client = new mddb.MDDB('localhost:11024', grpc.credentials.createInsecure()); // Add a document
client.Add({ collection: 'blog', key: 'hello-world', lang: 'en_US', meta: { category: { values: ['blog', 'tutorial'] }, author: { values: ['John Doe'] } }, content_md: '# Hello World\n\nWelcome to MDDB!'
}, (err, doc) => { if (err) throw err; console.log(`Document added: ${doc.id}`);
}); // Get a document
client.Get({ collection: 'blog', key: 'hello-world', lang: 'en_US', env: { year: '2024' }
}, (err, doc) => { if (err) throw err; console.log(`Content: ${doc.content_md}`);
}); // Search documents
client.Search({ collection: 'blog', filter_meta: { category: { values: ['blog'] } }, sort: 'updatedAt', asc: false, limit: 10
}, (err, resp) => { if (err) throw err; console.log(`Found ${resp.documents.length} documents`);
}); // Get stats
client.Stats({}, (err, stats) => { if (err) throw err; console.log(`Total documents: ${stats.total_documents}`);
});

Schema Validation RPCs

Schema validation enforces structure on document metadata per collection. See the Schema Validation Guide for full details.

Message Types

SchemaPropertyRule

message SchemaPropertyRule { string type = 1; repeated string enum_values = 2; string pattern = 3; int32 min_items = 4; int32 max_items = 5;
}

Schema

message Schema { repeated string required = 1; map<string, SchemaPropertyRule> properties = 2;
}

SetSchemaRequest / SetSchemaResponse

message SetSchemaRequest { string collection = 1; Schema schema = 2;
} message SetSchemaResponse { string status = 1;
}

GetSchemaRequest / GetSchemaResponse

message GetSchemaRequest { string collection = 1;
} message GetSchemaResponse { string collection = 1; Schema schema = 2;
}

DeleteSchemaRequest / DeleteSchemaResponse

message DeleteSchemaRequest { string collection = 1;
} message DeleteSchemaResponse { string status = 1;
}

ListSchemasRequest / ListSchemasResponse

message ListSchemasRequest {} message ListSchemasResponse { repeated CollectionSchema schemas = 1;
} message CollectionSchema { string collection = 1; Schema schema = 2;
}

ValidateDocumentRequest / ValidateDocumentResponse

message ValidateDocumentRequest { string collection = 1; map<string, MetaValues> meta = 2;
} message ValidateDocumentResponse { bool valid = 1; repeated string errors = 2;
}

grpcurl Examples

grpcurl -plaintext -d '{ "collection": "blog", "schema": { "required": ["category", "author"], "properties": { "category": {"type": "string", "enum_values": ["blog", "tutorial", "news"]}, "author": {"type": "string"} } }
}' localhost:11024 mddb.MDDB/SetSchema grpcurl -plaintext -d '{"collection": "blog"}' \ localhost:11024 mddb.MDDB/GetSchema grpcurl -plaintext -d '{"collection": "blog"}' \ localhost:11024 mddb.MDDB/DeleteSchema grpcurl -plaintext -d '{}' \ localhost:11024 mddb.MDDB/ListSchemas grpcurl -plaintext -d '{ "collection": "blog", "meta": { "category": {"values": ["blog"]}, "author": {"values": ["John Doe"]} }
}' localhost:11024 mddb.MDDB/ValidateDocument

Go Client Example

// Set a schema
_, err := client.SetSchema(ctx, &pb.SetSchemaRequest{ Collection: "blog", Schema: &pb.Schema{ Required: []string{"category", "author"}, Properties: map[string]*pb.SchemaPropertyRule{ "category": { Type: "string", EnumValues: []string{"blog", "tutorial", "news"}, }, "author": { Type: "string", }, }, },
}) // Validate a document before adding
resp, err := client.ValidateDocument(ctx, &pb.ValidateDocumentRequest{ Collection: "blog", Meta: map[string]*pb.MetaValues{ "category": {Values: []string{"blog"}}, "author": {Values: []string{"Jane Doe"}}, },
})
if resp.Valid { log.Println("Metadata is valid")
} else { log.Printf("Validation errors: %v", resp.Errors)
}

Performance Comparison

Payload Size Example

HTTP/JSON (Add Request):

{ "collection": "blog", "key": "hello-world", "lang": "en_US", "meta": { "category": ["blog", "tutorial"], "author": ["John Doe"] }, "contentMd": "# Hello World\n\nWelcome to MDDB!"
}

Size: ~180 bytes

gRPC/Protobuf (Same Request): Binary representation: ~55 bytes (70% smaller)

Benchmark Results

Operation HTTP/JSON gRPC Improvement
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Add Document 2.5ms 0.8ms 3.1x faster
Get Document 1.8ms 0.5ms 3.6x faster
Search (10 docs) 5.2ms 1.4ms 3.7x faster
Bulk Add (100) 250ms 75ms 3.3x faster

Best Practices

Connection Management

// โœ… Good: Reuse connections
var ( conn *grpc.ClientConn client pb.MDD BClient
) func init() { var err error conn, err = grpc.Dial("localhost:11024", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(), ) if err != nil { log.Fatal(err) } client = pb.NewMDDBClient(conn)
} // โŒ Bad: Create new connection for each request
func badExample() { conn, _ := grpc.Dial("localhost:11024", ...) defer conn.Close() client := pb.NewMDDBClient(conn) // Use client...
}

Error Handling

import "google.golang.org/grpc/status" doc, err := client.Get(ctx, req)
if err != nil { st, ok := status.FromError(err) if ok { switch st.Code() { case codes.NotFound: log.Println("Document not found") case codes.InvalidArgument: log.Println("Invalid request:", st.Message()) case codes.PermissionDenied: log.Println("Permission denied:", st.Message()) default: log.Println("Error:", st.Message()) } } return err
}

Timeouts

// Set reasonable timeouts
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() doc, err := client.Get(ctx, req)

Metadata

import "google.golang.org/grpc/metadata" // Add metadata to request
md := metadata.Pairs( "client-id", "my-app", "version", "1.0.0",
)
ctx := metadata.NewOutgoingContext(context.Background(), md) doc, err := client.Get(ctx, req)

Generating Client Code

Go

cd services/mddbd
./generate.sh

Python

python -m grpc_tools.protoc \ -I proto \ --python_out=. \ --grpc_python_out=. \ proto/mddb.proto

Node.js

npm install @grpc/grpc-js @grpc/proto-loader

Other Languages

See gRPC documentation for language-specific guides.

Debugging

Enable Logging

import "google.golang.org/grpc/grpclog" grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stderr, os.Stderr))

Use grpcurl

grpcurl -plaintext localhost:11024 list mddb.MDDB grpcurl -plaintext localhost:11024 describe mddb.MDDB.Add grpcurl -plaintext -d @ localhost:11024 mddb.MDDB/Add <<EOF
{ "collection": "blog", "key": "test", "lang": "en_US", "content_md": "# Test"
}
EOF

Reflection

The server has reflection enabled, allowing tools like grpcurl to discover services without .proto files.

Migration from HTTP

Side-by-Side

Both HTTP and gRPC APIs run simultaneously:

  • HTTP: localhost:11023
  • gRPC: localhost:11024

You can gradually migrate clients from HTTP to gRPC.

API Parity

All HTTP endpoints have equivalent gRPC methods with the same functionality.

See Also

License

BSD 3-Clause License - see LICENSE for details.