Skip to main content
Back to Blog
2 August 202513 min read

GCP API Gateway and Cloud Endpoints

GCPAPI GatewayCloud EndpointsCloud

Building and managing APIs on Google Cloud Platform. API Gateway configuration, Cloud Endpoints, authentication strategies, and integration patterns.


GCP API Gateway and Cloud Endpoints

Google Cloud Platform offers multiple options for API management. API Gateway provides a fully managed solution for serverless backends, while Cloud Endpoints offers more control for container-based services. Understanding when to use each enables building scalable, secure APIs.

API Gateway vs Cloud Endpoints

Comparison Overview

GCP API Management Options:

API Gateway (Fully Managed)
├── Best for: Serverless backends (Cloud Functions, Cloud Run)
├── Management: Fully managed by Google
├── OpenAPI: Required for configuration
├── Authentication: Built-in Firebase, API keys, JWT
└── Pricing: Per million calls

Cloud Endpoints (ESP/ESPv2)
├── Best for: GKE, Compute Engine, custom deployments
├── Management: Self-managed sidecar proxy
├── OpenAPI/gRPC: Both supported
├── Authentication: Flexible, customizable
└── Pricing: Based on underlying compute

When to Use Each

Use CaseAPI GatewayCloud Endpoints
Cloud FunctionsYesNo
Cloud RunYesYes
GKENoYes
Compute EngineNoYes
gRPC APIsNoYes
WebSocketNoYes

API Gateway Configuration

OpenAPI Specification

# api-config.yaml swagger: "2.0" info: title: User Service API version: "1.0.0" description: API for user management host: "api-gateway-xyz.ew.gateway.dev" schemes: - https x-google-backend: address: https://user-service-abc123-ew.a.run.app securityDefinitions: firebase: authorizationUrl: "" flow: implicit type: oauth2 x-google-issuer: "https://securetoken.google.com/my-project" x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com" x-google-audiences: "my-project" api_key: type: apiKey name: x-api-key in: header paths: /users: get: summary: List users operationId: listUsers security: - firebase: [] x-google-backend: address: https://user-service-abc123-ew.a.run.app/users deadline: 30.0 parameters: - name: limit in: query type: integer default: 20 - name: cursor in: query type: string responses: "200": description: List of users "401": description: Unauthorized post: summary: Create user operationId: createUser security: - firebase: [] x-google-backend: address: https://user-service-abc123-ew.a.run.app/users deadline: 30.0 parameters: - name: body in: body required: true schema: type: object required: - email - name properties: email: type: string name: type: string responses: "201": description: User created "400": description: Validation error /users/{userId}: get: summary: Get user operationId: getUser security: - firebase: [] parameters: - name: userId in: path required: true type: string responses: "200": description: User details "404": description: Not found

Terraform Deployment

# api-gateway.tf resource "google_api_gateway_api" "main" { provider = google-beta api_id = "${var.project}-api" project = var.project_id } resource "google_api_gateway_api_config" "main" { provider = google-beta api = google_api_gateway_api.main.api_id api_config_id = "config-${formatdate("YYYYMMDDhhmmss", timestamp())}" project = var.project_id openapi_documents { document { path = "openapi.yaml" contents = base64encode(templatefile("${path.module}/api-config.yaml", { backend_url = google_cloud_run_service.backend.status[0].url project_id = var.project_id })) } } gateway_config { backend_config { google_service_account = google_service_account.api_gateway.email } } lifecycle { create_before_destroy = true } } resource "google_api_gateway_gateway" "main" { provider = google-beta gateway_id = "${var.project}-gateway" api_config = google_api_gateway_api_config.main.id region = var.region project = var.project_id } # Service account for API Gateway to call backend resource "google_service_account" "api_gateway" { account_id = "api-gateway-invoker" display_name = "API Gateway Service Account" project = var.project_id } resource "google_cloud_run_service_iam_member" "api_gateway_invoker" { service = google_cloud_run_service.backend.name location = var.region role = "roles/run.invoker" member = "serviceAccount:${google_service_account.api_gateway.email}" } output "gateway_url" { value = google_api_gateway_gateway.main.default_hostname }

Cloud Endpoints

ESPv2 Configuration

# openapi-spec.yaml for Cloud Endpoints swagger: "2.0" info: title: User Service API version: "1.0.0" host: "user-service.endpoints.my-project.cloud.goog" basePath: "/" x-google-endpoints: - name: "user-service.endpoints.my-project.cloud.goog" target: "user-service.my-project.internal" allowCors: true x-google-management: metrics: - name: "read_requests" displayName: "Read Requests" valueType: INT64 metricKind: DELTA - name: "write_requests" displayName: "Write Requests" valueType: INT64 metricKind: DELTA quota: limits: - name: "read-requests-limit" metric: "read_requests" unit: "1/min/{project}" values: STANDARD: 1000 - name: "write-requests-limit" metric: "write_requests" unit: "1/min/{project}" values: STANDARD: 100 securityDefinitions: auth0: authorizationUrl: "" flow: implicit type: oauth2 x-google-issuer: "https://your-tenant.auth0.com/" x-google-jwks_uri: "https://your-tenant.auth0.com/.well-known/jwks.json" x-google-audiences: "your-audience" paths: /users: get: summary: List users operationId: listUsers security: - auth0: [] x-google-quota: metricCosts: read_requests: 1 responses: "200": description: Success

Kubernetes Deployment with ESPv2

# esp-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: user-service spec: replicas: 3 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: # ESPv2 sidecar - name: esp image: gcr.io/endpoints-release/endpoints-runtime-serverless:2 args: - --listener_port=8080 - --backend=grpc://127.0.0.1:9000 - --service=user-service.endpoints.my-project.cloud.goog - --rollout_strategy=managed - --cors_preset=cors_with_regex - --cors_allow_origin_regex=.*\.example\.com$ ports: - containerPort: 8080 readinessProbe: httpGet: path: /healthz port: 8080 resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "500m" # Backend service - name: backend image: gcr.io/my-project/user-service:latest ports: - containerPort: 9000 env: - name: PORT value: "9000" resources: requests: memory: "256Mi" cpu: "200m" limits: memory: "512Mi" cpu: "1000m"

Authentication Patterns

Firebase Authentication

// Verifying Firebase tokens in Cloud Function import { Request, Response } from 'express'; import * as admin from 'firebase-admin'; admin.initializeApp(); export const verifyFirebaseToken = async ( req: Request, res: Response, next: Function ) => { const authHeader = req.headers.authorization; if (!authHeader?.startsWith('Bearer ')) { return res.status(401).json({ error: 'Missing authorization header' }); } const token = authHeader.split('Bearer ')[1]; try { const decodedToken = await admin.auth().verifyIdToken(token); req.user = { uid: decodedToken.uid, email: decodedToken.email, emailVerified: decodedToken.email_verified }; next(); } catch (error) { return res.status(401).json({ error: 'Invalid token' }); } }; // Using with API Gateway - token already verified export const handleRequest = async (req: Request, res: Response) => { // API Gateway sets these headers after validation const userId = req.headers['x-apigateway-api-userinfo']; if (userId) { const userInfo = JSON.parse( Buffer.from(userId as string, 'base64').toString() ); // userInfo contains the decoded JWT claims } // Process request res.json({ message: 'Success' }); };

Service-to-Service Authentication

// Calling authenticated Cloud Run service import { GoogleAuth } from 'google-auth-library'; const auth = new GoogleAuth(); export const callAuthenticatedService = async ( serviceUrl: string, path: string ): Promise<any> => { // Get ID token for the target service const client = await auth.getIdTokenClient(serviceUrl); const response = await client.request({ url: `${serviceUrl}${path}`, method: 'GET' }); return response.data; };

Rate Limiting and Quotas

API Gateway Rate Limiting

# api-config.yaml with quotas x-google-management: quota: limits: - name: "requests-per-minute" metric: "servicecontrol.googleapis.com/api_request_count" unit: "1/min/{project}" values: STANDARD: 100 paths: /expensive-operation: post: operationId: expensiveOperation x-google-quota: metricCosts: servicecontrol.googleapis.com/api_request_count: 10

Key Takeaways

  1. Choose wisely: API Gateway for serverless, Cloud Endpoints for containers/VMs

  2. OpenAPI first: Both services require OpenAPI specifications

  3. Authentication built-in: Firebase, JWT, and API key support out of the box

  4. Service accounts: Use proper IAM for service-to-service communication

  5. Quotas and limits: Implement rate limiting to protect backends

  6. Monitoring: Enable Cloud Monitoring for API metrics

  7. Version management: Use API config versioning for safe deployments

  8. CORS configuration: Handle CORS at the gateway level

GCP's API management options provide flexibility for different deployment models. Choose based on your backend infrastructure and feature requirements.

Share this article