gRPC API Documentation¶
Last Updated: 2025-11-23
This document describes the gRPC API implementation in ocmonica using ConnectRPC, including service definitions, protocol support, and client usage.
Table of Contents¶
- Overview
- Protocol Support
- Authentication
- Available Services
- Code Generation
- Client Usage
- Key Differences from REST
Overview¶
Ocmonica's gRPC API is built with ConnectRPC v1.19.1 and provides type-safe, high-performance RPC endpoints for modern web apps and native clients.
Key Features: - ✅ Compile-time type safety from protobuf schemas - ✅ Shared types between backend and frontend - ✅ Automatic serialization (no manual JSON marshalling) - ✅ Binary Protocol Buffers (smaller payload) - ✅ HTTP/2 multiplexing - ✅ Server streaming (SearchStream) - ✅ Built-in compression
Base Path: /ocmonica.v1.*/*
Protocol Support¶
ConnectRPC supports three protocols on the same endpoint:
1. Connect Protocol (Recommended)¶
- JSON or binary encoding
- Browser-friendly (uses standard HTTP/POST)
- Best for web applications
- Easy to debug with JSON format
Example:
const transport = createConnectTransport({
baseUrl: 'http://localhost:8080',
useBinaryFormat: false, // JSON format for debugging
});
2. gRPC Protocol¶
- Binary Protocol Buffers encoding
- Requires HTTP/2
- Best for server-to-server communication
- Smallest payload size
Example:
const transport = createConnectTransport({
baseUrl: 'http://localhost:8080',
useBinaryFormat: true, // Binary protobuf
});
3. gRPC-Web Protocol¶
- Compatible with gRPC proxies (e.g., Envoy)
- Works with existing gRPC infrastructure
- Supports browser clients
Authentication¶
gRPC API uses JWT authentication via interceptors, similar to REST middleware.
gRPC Authentication (internal/api/grpc/interceptors/auth.go):
// Interceptor validates JWT token from Authorization header
func NewAuthInterceptor(tokenService *service.TokenService) connect.UnaryInterceptorFunc {
return func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
token := extractBearerToken(req.Header())
claims, err := tokenService.ValidateToken(token)
// ... adds claims to context
}
}
}
Using Tokens¶
Include the JWT access token in request headers:
const transport = createConnectTransport({
baseUrl: 'http://localhost:8080',
interceptors: [
(next) => async (req) => {
req.header.set('Authorization', `Bearer ${accessToken}`);
return await next(req);
}
]
});
Note: Obtain access tokens via REST API login endpoint (POST /api/v1/auth/login).
Available Services¶
FileService¶
Implementation: internal/api/grpc/file_service.go
Protobuf: proto/file/v1/file_service.proto
Methods:
- UploadFile - Upload a new file
- DownloadFile - Download file content
- GetFile - Get file metadata
- ListFiles - List files in directory
- DeleteFile - Delete a file
- MoveFile - Move file to different directory
- CopyFile - Copy file to different location
- UpdateFile - Update file metadata
Status: ✅ Complete parity with REST API
DirectoryService¶
Implementation: internal/api/grpc/directory_service.go
Protobuf: proto/directory/v1/directory_service.proto
Methods:
- CreateDirectory - Create new directory
- GetDirectory - Get directory metadata
- ListDirectories - List subdirectories
- DeleteDirectory - Delete directory
- MoveDirectory - Move directory
Status: ✅ Complete parity with REST API
SearchService¶
Implementation: internal/api/grpc/search_service.go
Protobuf: proto/search/v1/search_service.proto
Methods:
- Search - Search files and directories (single response)
- SearchStream - Search with server streaming (gRPC advantage)
Status: ✅ Complete with additional streaming capability
Server Streaming Example:
const stream = client.searchStream({ query: 'report' });
for await (const result of stream) {
console.log('Found:', result.name);
}
GroupService¶
Implementation: internal/api/grpc/group_service.go
Protobuf: proto/group/v1/group_service.proto
Methods:
- CreateGroup - Create new group
- GetGroup - Get group details
- ListGroups - List all groups
- UpdateGroup - Update group metadata
- DeleteGroup - Delete group
- AddMember - Add user to group
- RemoveMember - Remove user from group
- ListMembers - List group members
Status: ✅ Complete parity with REST API
WatchedFolderService¶
Implementation: internal/api/grpc/watched_folder_service.go
Protobuf: proto/watched_folder/v1/watched_folder_service.proto
Methods:
- CreateWatchedFolder - Create watched folder
- GetWatchedFolder - Get watched folder details
- ListWatchedFolders - List watched folders
- UpdateWatchedFolder - Update watched folder
- DeleteWatchedFolder - Delete watched folder
- TriggerScan - Manually trigger folder scan
- GetIndexedFiles - Get files indexed from folder
Status: ✅ Complete parity with REST API
Authorization (RBAC)¶
Both REST and gRPC APIs enforce RBAC via similar patterns.
gRPC Interceptor (internal/api/grpc/interceptors/permission.go):
func NewPermissionInterceptor(permissionService *service.PermissionService, requiredPermission string) connect.UnaryInterceptorFunc {
return func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
if !permissionService.HasPermission(ctx, requiredPermission) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
return next(ctx, req)
}
}
}
Permission Format: resource:action (e.g., file:read, file:write, org:admin)
ConnectRPC Error Codes:
- connect.CodeNotFound - 404
- connect.CodePermissionDenied - 403
- connect.CodeUnauthenticated - 401
- connect.CodeInvalidArgument - 400
- connect.CodeInternal - 500
Code Generation¶
Backend (Go)¶
Configuration: buf.gen.yaml
version: v2
plugins:
- remote: buf.build/connectrpc/go:v1.19.1
out: gen
opt:
- paths=source_relative
- remote: buf.build/protocolbuffers/go:v1.36.10
out: gen
opt:
- paths=source_relative
Generates:
- gen/ocmonica/v1/*_pb.go - Message types
- gen/ocmonica/v1/*_connect.go - Service interfaces and clients
Command: task proto:gen
Frontend (TypeScript)¶
Configuration: web/buf.gen.yaml
version: v2
plugins:
- remote: buf.build/connectrpc/es:v2.9.0
out: src/gen
opt:
- target=ts
- remote: buf.build/protocolbuffers/es:v2.9.0
out: src/gen
opt:
- target=ts
Generates:
- src/gen/ocmonica/v1/*_pb.ts - Message types
- src/gen/ocmonica/v1/*_connect.ts - Service clients
Command: task web:gen
Client Usage¶
TypeScript/React Example¶
Setup Transport:
import { createConnectTransport } from '@connectrpc/connect-web';
import { createPromiseClient } from '@connectrpc/connect';
import { FileService } from '@/gen/ocmonica/v1/file_service_connect';
const transport = createConnectTransport({
baseUrl: 'http://localhost:8080',
useBinaryFormat: false,
});
const client = createPromiseClient(FileService, transport);
Upload File:
const response = await client.uploadFile({
filename: 'document.pdf',
content: fileBuffer,
directoryId: 'dir-123',
});
console.log('Uploaded:', response.file);
List Files:
const response = await client.listFiles({
directoryId: 'dir-123',
limit: 20,
});
console.log('Files:', response.files);
Stream Search Results:
const stream = client.searchStream({ query: 'report' });
for await (const result of stream) {
console.log('Found:', result.file.name);
}
With TanStack Query¶
import { useQuery } from '@tanstack/react-query';
function FileList({ directoryId }: { directoryId: string }) {
const { data, isLoading } = useQuery({
queryKey: ['files', directoryId],
queryFn: () => client.listFiles({ directoryId }),
});
if (isLoading) return <div>Loading...</div>;
return (
<ul>
{data?.files.map(file => (
<li key={file.id}>{file.name}</li>
))}
</ul>
);
}
Key Differences from REST¶
Type Safety¶
gRPC (ConnectRPC): - ✅ Compile-time type safety from protobuf schemas - ✅ Shared types between backend and frontend - ✅ Automatic serialization (no manual JSON marshalling) - ✅ Breaking changes caught at build time
REST: - ⚠️ Manual type definitions in both Go and TypeScript - ⚠️ Runtime errors for type mismatches - ⚠️ Manual JSON marshalling/unmarshalling - ⚠️ API drift between backend and frontend possible
Performance¶
gRPC (ConnectRPC): - ✅ Binary Protocol Buffers (smaller payload) - ✅ HTTP/2 multiplexing (multiple requests on one connection) - ✅ Server streaming (SearchStream) - ✅ Built-in compression
REST: - ⚠️ JSON encoding (larger payload) - ⚠️ HTTP/1.1 or HTTP/2 - ❌ No streaming support - ⚠️ Manual compression configuration
Developer Experience¶
gRPC (ConnectRPC): - ✅ Auto-generated clients (no manual API client code) - ✅ IDE autocomplete for all methods and types - ✅ Protobuf docs embedded in generated code - ⚠️ Requires protobuf knowledge - ⚠️ Additional build step (code generation)
REST: - ✅ Simple HTTP/JSON (universally understood) - ✅ Easy to test with curl/Postman - ✅ No build steps for clients - ⚠️ Manual client code - ⚠️ OpenAPI/Swagger needed for docs
Client Compatibility¶
gRPC (ConnectRPC): - ✅ Modern web browsers (Connect protocol) - ✅ Node.js clients - ✅ Go clients - ✅ gRPC clients (via gRPC protocol) - ⚠️ Requires generated client libraries
REST: - ✅ Any HTTP client (curl, wget, browsers, mobile apps) - ✅ No client library required - ✅ Universal compatibility - ✅ Works with legacy systems
Feature Gaps¶
Not Available via gRPC¶
The following endpoints are REST-only:
- Authentication Management
- User registration, login, logout
- Token refresh
- Profile management
-
Password changes
-
Organization Management
- Create/update/delete organizations
-
Organization membership
-
Role Management (RBAC)
- Create/update/delete roles
-
Role assignment
-
API Key Management
-
Create/revoke API keys
-
Health & Monitoring
- Health checks (
/health) - Prometheus metrics (
/metrics)
Recommendation: Use REST API for authentication and administrative tasks. Use gRPC API for file operations and core application features.