GCP API Gateway and Cloud Endpoints
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 computeWhen to Use Each
| Use Case | API Gateway | Cloud Endpoints |
|---|---|---|
| Cloud Functions | Yes | No |
| Cloud Run | Yes | Yes |
| GKE | No | Yes |
| Compute Engine | No | Yes |
| gRPC APIs | No | Yes |
| WebSocket | No | Yes |
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 foundTerraform 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: SuccessKubernetes 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: 10Key Takeaways
-
Choose wisely: API Gateway for serverless, Cloud Endpoints for containers/VMs
-
OpenAPI first: Both services require OpenAPI specifications
-
Authentication built-in: Firebase, JWT, and API key support out of the box
-
Service accounts: Use proper IAM for service-to-service communication
-
Quotas and limits: Implement rate limiting to protect backends
-
Monitoring: Enable Cloud Monitoring for API metrics
-
Version management: Use API config versioning for safe deployments
-
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.