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
- Why gRPC?
- Getting Started
- Service Definition
- Client Examples
- Schema Validation RPCs
- Performance Comparison
- Best Practices
Overview
gRPC Endpoint: localhost:11024
Protocol: HTTP/2 + Protocol Buffers
Reflection: Enabled (for grpcurl and debugging)
Why gRPC?
Performance Benefits
| Feature | HTTP/JSON | gRPC |
|---|---|---|
| Payload Size | 100% | ~30% (70% smaller) |
| Serialization | JSON text | Binary protobuf |
| Transport | HTTP/1.1 | HTTP/2 |
| Compression | Optional gzip | Built-in |
| Streaming | Limited | Full duplex |
| Type Safety | Runtime | Compile-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)
| Category | RPC | Request โ Response |
|---|---|---|
| Document Management | Add | AddRequest โ Document |
AddBatch | AddBatchRequest โ AddBatchResponse | |
UpdateDocument | UpdateDocumentRequest โ Document | |
UpdateBatch | UpdateBatchRequest โ UpdateBatchResponse | |
DeleteDocument | DeleteDocumentRequest โ DeleteDocumentResponse | |
DeleteBatch | DeleteBatchRequest โ DeleteBatchResponse | |
DeleteCollection | DeleteCollectionRequest โ DeleteCollectionResponse | |
Get | GetRequest โ Document | |
GetDocumentMeta | GetDocumentMetaRequest โ GetDocumentMetaResponse | |
Search | SearchRequest โ SearchResponse | |
Ingest | IngestRequest โ IngestResponse | |
ImportURL | ImportURLRequest โ Document | |
SetTTL | SetTTLRequest โ Document | |
| Full-Text Search | FTS | FTSRequest โ FTSResponse |
| Vector / Semantic | VectorSearch | VectorSearchRequest โ VectorSearchResponse |
VectorReindex | VectorReindexRequest โ VectorReindexResponse | |
VectorStats | VectorStatsRequest โ VectorStatsResponse | |
| Hybrid & Cross | HybridSearch | HybridSearchRequest โ HybridSearchResponse |
CrossSearch | CrossSearchRequest โ CrossSearchResponse | |
| Analysis | Classify | ClassifyRequest โ ClassifyResponse |
FindDuplicates | FindDuplicatesRequest โ FindDuplicatesResponse | |
GetChecksum | GetChecksumRequest โ GetChecksumResponse | |
GetMetaKeys | GetMetaKeysRequest โ GetMetaKeysResponse | |
| Revisions | ListRevisions | ListRevisionsRequest โ ListRevisionsResponse |
RestoreRevision | RestoreRevisionRequest โ Document | |
Truncate | TruncateRequest โ TruncateResponse | |
| Export & Backup | Export | ExportRequest โ stream ExportChunk |
Backup | BackupRequest โ BackupResponse | |
Restore | RestoreRequest โ RestoreResponse | |
| FTS Config | ListSynonyms | ListSynonymsRequest โ ListSynonymsResponse |
AddSynonym | AddSynonymRequest โ AddSynonymResponse | |
DeleteSynonym | DeleteSynonymRequest โ DeleteSynonymResponse | |
ListStopwords | ListStopwordsRequest โ ListStopwordsResponse | |
AddStopwords | AddStopwordsRequest โ AddStopwordsResponse | |
DeleteStopwords | DeleteStopwordsRequest โ DeleteStopwordsResponse | |
| Schemas | SetSchema | SetSchemaRequest โ SetSchemaResponse |
GetSchema | GetSchemaRequest โ GetSchemaResponse | |
DeleteSchema | DeleteSchemaRequest โ DeleteSchemaResponse | |
ListSchemas | ListSchemasRequest โ ListSchemasResponse | |
ValidateDocument | ValidateDocumentRequest โ ValidateDocumentResponse | |
| Webhooks | RegisterWebhook | RegisterWebhookRequest โ WebhookProto |
ListWebhooks | ListWebhooksRequest โ ListWebhooksResponse | |
DeleteWebhook | DeleteWebhookRequest โ DeleteWebhookResponse | |
| Automation | ListAutomation | ListAutomationRequest โ ListAutomationResponse |
CreateAutomation | CreateAutomationRequest โ AutomationRuleProto | |
GetAutomation | GetAutomationRequest โ AutomationRuleProto | |
UpdateAutomation | UpdateAutomationRequest โ AutomationRuleProto | |
DeleteAutomation | DeleteAutomationRequest โ DeleteAutomationResponse | |
TestAutomation | TestAutomationRequest โ TestAutomationResponse | |
GetAutomationLogs | GetAutomationLogsRequest โ GetAutomationLogsResponse | |
| Collection Config | GetCollectionConfig | GetCollectionConfigRequest โ GetCollectionConfigResponse |
SetCollectionConfig | SetCollectionConfigRequest โ SetCollectionConfigResponse | |
ListCollectionConfigs | ListCollectionConfigsRequest โ ListCollectionConfigsResponse | |
| System | Stats | StatsRequest โ 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
- API Documentation - HTTP/JSON API reference
- Examples - More code examples
- Protocol Buffers - Protobuf documentation
- gRPC - gRPC documentation
License
BSD 3-Clause License - see LICENSE for details.