Skip to main content

Overview

The CallerService acts as a proxy to external HTTP APIs, providing a gRPC interface for making HTTP GET requests. This service demonstrates asynchronous database logging and timeout handling patterns. Base Path: /caller.v1.CallerService Authentication: Not required (internal service) Middleware: None (internal-only access)

RPCs

CallExternal

Makes an HTTP GET request to a specified URL and returns the status code and response body length. Call logs are asynchronously written to the database. Endpoint: POST /caller.v1.CallerService/CallExternal Signature:
rpc CallExternal(CallExternalRequest) returns (CallExternalResponse)

Request

url
string
required
The target URL to call. Must be a valid HTTP/HTTPS URL. The service will perform a GET request to this endpoint.Example: https://httpbin.org/get

Response

status_code
int32
required
The HTTP status code returned by the external API
body_length
int32
required
The size of the response body in bytes

Go Client Example

import (
  "connectrpc.com/connect"
  callerv1 "github.com/hackz-megalo-cup/microservices-app/services/gen/go/caller/v1"
  "github.com/hackz-megalo-cup/microservices-app/services/gen/go/caller/v1/callerv1connect"
)

// Create caller client
callerClient := callerv1connect.NewCallerServiceClient(
  &http.Client{Timeout: 3 * time.Second},
  "http://caller-service.microservices:8081",
)

// Make request
resp, err := callerClient.CallExternal(
  ctx,
  connect.NewRequest(&callerv1.CallExternalRequest{
    Url: "https://httpbin.org/get",
  }),
)
if err != nil {
  // Handle error
}

fmt.Printf("Status: %d, Body Length: %d\n", 
  resp.Msg.GetStatusCode(), 
  resp.Msg.GetBodyLength(),
)

cURL Example (h2c)

curl -X POST http://caller-service.microservices:8081/caller.v1.CallerService/CallExternal \
  --http2-prior-knowledge \
  -H "Content-Type: application/json" \
  -H "Connect-Protocol-Version: 1" \
  -d '{"url": "https://httpbin.org/get"}'

Example Response

{
  "status_code": 200,
  "body_length": 1456
}

Implementation Details

Database Integration

The CallExternal RPC performs an asynchronous database write to the call_logs table:
INSERT INTO call_logs (url, status_code, body_length) 
VALUES ($1, $2, $3)
The database write happens in a goroutine after the response is returned, which means:
  • Lower latency: Response is returned immediately without waiting for the database
  • ⚠️ Potential log loss: If the database write fails, the log entry may be lost (error is only logged)
This pattern is suitable for non-critical audit logs where performance is prioritized over guaranteed persistence.

HTTP Client Configuration

  • Timeout: 2 seconds per HTTP request
  • Method: GET only
  • Response handling: Body is discarded after reading (only length is tracked)

Error Handling

The service returns Connect RPC error codes:
Error CodeCondition
CodeInvalidArgumentInvalid URL format
CodeDeadlineExceededHTTP request timeout (2 seconds)
CodeUnavailableNetwork error or target unreachable
CodeInternalFailed to build request or read response

URL Validation

The service validates that the provided URL is a valid HTTP/HTTPS URI using Go’s url.ParseRequestURI(). URLs that fail validation will return CodeInvalidArgument.

Service Architecture

Internal-Only Access

The CallerService is designed as an internal service and is not exposed through the Traefik ingress. It can only be accessed by other services within the Kubernetes cluster:
  • Default endpoint: http://caller-service.microservices:8081
  • No authentication required (relies on network isolation)

Service Dependencies

  • PostgreSQL: Stores call logs in the caller_db.call_logs table (optional)
  • External APIs: Makes HTTP GET requests to user-specified URLs

Timeouts

  • HTTP request timeout: 2 seconds
  • HTTP client timeout: 2 seconds
  • RPC context timeout: 2 seconds (enforced by caller)

Database Schema

The service logs calls to the following table structure:
CREATE TABLE call_logs (
  id SERIAL PRIMARY KEY,
  url TEXT NOT NULL,
  status_code INTEGER NOT NULL,
  body_length INTEGER NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Service Configuration

Environment VariableDefaultDescription
PORT8081Service port
DATABASE_URL-PostgreSQL connection string (optional)
OTEL_EXPORTER_OTLP_ENDPOINT-OpenTelemetry collector endpoint

Observability

The CallerService is instrumented with OpenTelemetry for distributed tracing:
  • HTTP calls are traced with span attributes
  • Errors are recorded as span events
  • Service metrics include request duration and error rates

Common Use Cases

  1. External API Integration: Use CallerService as a proxy for external HTTP APIs with built-in logging
  2. Health Checks: Check external service availability
  3. Data Fetching: Retrieve data from external sources with automatic retry and timeout handling

Build docs developers (and LLMs) love