# API Implementation Patterns

Common patterns and code examples for integrating different types of APIs across multiple programming languages.

---

## Table of Contents

- [REST APIs](#rest-apis)
  - [JavaScript/Node.js](#javascriptnodejs)
  - [Python](#python)
  - [PHP](#php)
  - [Go](#go)
- [GraphQL APIs](#graphql-apis)
- [gRPC APIs](#grpc-apis)
- [Authentication Patterns](#authentication-patterns)
- [Error Handling & Retries](#error-handling--retries)
- [Rate Limiting](#rate-limiting)

---

## REST APIs

### JavaScript/Node.js

#### Using fetch (modern)

```javascript
// Basic GET request
async function getData() {
  try {
    const response = await fetch('https://api.example.com/data', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${process.env.API_KEY}`,
        'Content-Type': 'application/json'
      }
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error fetching data:', error);
    throw error;
  }
}

// POST request with body
async function createResource(payload) {
  const response = await fetch('https://api.example.com/resources', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });

  return await response.json();
}
```

#### Using axios

```javascript
const axios = require('axios');

// Create configured instance
const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  headers: {
    'Authorization': `Bearer ${process.env.API_KEY}`,
    'Content-Type': 'application/json'
  },
  timeout: 10000
});

// GET request
async function getData() {
  try {
    const response = await apiClient.get('/data');
    return response.data;
  } catch (error) {
    if (error.response) {
      // Server responded with error status
      console.error('Error:', error.response.status, error.response.data);
    } else if (error.request) {
      // No response received
      console.error('No response:', error.request);
    } else {
      console.error('Error:', error.message);
    }
    throw error;
  }
}

// POST request
async function createResource(data) {
  const response = await apiClient.post('/resources', data);
  return response.data;
}
```

### Python

#### Using requests library

```python
import requests
import os
from typing import Dict, Any

class APIClient:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        })

    def get(self, endpoint: str) -> Dict[str, Any]:
        """GET request"""
        response = self.session.get(f'{self.base_url}/{endpoint}')
        response.raise_for_status()
        return response.json()

    def post(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
        """POST request"""
        response = self.session.post(f'{self.base_url}/{endpoint}', json=data)
        response.raise_for_status()
        return response.json()

    def put(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
        """PUT request"""
        response = self.session.put(f'{self.base_url}/{endpoint}', json=data)
        response.raise_for_status()
        return response.json()

    def delete(self, endpoint: str) -> bool:
        """DELETE request"""
        response = self.session.delete(f'{self.base_url}/{endpoint}')
        response.raise_for_status()
        return response.status_code == 204

# Usage
client = APIClient('https://api.example.com', os.getenv('API_KEY'))
data = client.get('users/123')
```

#### Using httpx (async)

```python
import httpx
import asyncio
from typing import Dict, Any

class AsyncAPIClient:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url
        self.headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }

    async def get(self, endpoint: str) -> Dict[str, Any]:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f'{self.base_url}/{endpoint}',
                headers=self.headers
            )
            response.raise_for_status()
            return response.json()

    async def post(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
        async with httpx.AsyncClient() as client:
            response = await client.post(
                f'{self.base_url}/{endpoint}',
                headers=self.headers,
                json=data
            )
            response.raise_for_status()
            return response.json()

# Usage
async def main():
    client = AsyncAPIClient('https://api.example.com', os.getenv('API_KEY'))
    data = await client.get('users/123')
    print(data)

asyncio.run(main())
```

### PHP

```php
<?php

class APIClient {
    private $baseUrl;
    private $apiKey;

    public function __construct($baseUrl, $apiKey) {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->apiKey = $apiKey;
    }

    private function makeRequest($method, $endpoint, $data = null) {
        $url = $this->baseUrl . '/' . ltrim($endpoint, '/');

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $this->apiKey,
            'Content-Type: application/json'
        ]);

        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            if ($data) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            }
        } elseif ($method === 'PUT') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
            if ($data) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            }
        } elseif ($method === 'DELETE') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode >= 400) {
            throw new Exception("HTTP Error: $httpCode - $response");
        }

        return json_decode($response, true);
    }

    public function get($endpoint) {
        return $this->makeRequest('GET', $endpoint);
    }

    public function post($endpoint, $data) {
        return $this->makeRequest('POST', $endpoint, $data);
    }

    public function put($endpoint, $data) {
        return $this->makeRequest('PUT', $endpoint, $data);
    }

    public function delete($endpoint) {
        return $this->makeRequest('DELETE', $endpoint);
    }
}

// Usage
$client = new APIClient('https://api.example.com', getenv('API_KEY'));
$data = $client->get('users/123');
```

### Go

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

type APIClient struct {
    BaseURL    string
    APIKey     string
    HTTPClient *http.Client
}

func NewAPIClient(baseURL, apiKey string) *APIClient {
    return &APIClient{
        BaseURL: baseURL,
        APIKey:  apiKey,
        HTTPClient: &http.Client{
            Timeout: 10 * time.Second,
        },
    }
}

func (c *APIClient) makeRequest(method, endpoint string, body interface{}) ([]byte, error) {
    url := c.BaseURL + "/" + endpoint

    var reqBody io.Reader
    if body != nil {
        jsonData, err := json.Marshal(body)
        if err != nil {
            return nil, err
        }
        reqBody = bytes.NewBuffer(jsonData)
    }

    req, err := http.NewRequest(method, url, reqBody)
    if err != nil {
        return nil, err
    }

    req.Header.Set("Authorization", "Bearer "+c.APIKey)
    req.Header.Set("Content-Type", "application/json")

    resp, err := c.HTTPClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    respBody, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    if resp.StatusCode >= 400 {
        return nil, fmt.Errorf("HTTP error %d: %s", resp.StatusCode, string(respBody))
    }

    return respBody, nil
}

func (c *APIClient) Get(endpoint string, result interface{}) error {
    data, err := c.makeRequest("GET", endpoint, nil)
    if err != nil {
        return err
    }
    return json.Unmarshal(data, result)
}

func (c *APIClient) Post(endpoint string, body, result interface{}) error {
    data, err := c.makeRequest("POST", endpoint, body)
    if err != nil {
        return err
    }
    return json.Unmarshal(data, result)
}

// Usage
func main() {
    client := NewAPIClient("https://api.example.com", os.Getenv("API_KEY"))

    var user map[string]interface{}
    err := client.Get("users/123", &user)
    if err != nil {
        panic(err)
    }
    fmt.Println(user)
}
```

---

## GraphQL APIs

### JavaScript/Node.js

```javascript
// Using graphql-request
const { GraphQLClient, gql } = require('graphql-request');

const client = new GraphQLClient('https://api.example.com/graphql', {
  headers: {
    authorization: `Bearer ${process.env.API_KEY}`,
  },
});

// Query example
const query = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`;

async function getUser(id) {
  const variables = { id };
  const data = await client.request(query, variables);
  return data.user;
}

// Mutation example
const mutation = gql`
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`;

async function createUser(name, email) {
  const variables = {
    input: { name, email }
  };
  const data = await client.request(mutation, variables);
  return data.createUser;
}
```

### Python

```python
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport

# Configure transport
transport = RequestsHTTPTransport(
    url='https://api.example.com/graphql',
    headers={'Authorization': f'Bearer {os.getenv("API_KEY")}'},
)

# Create client
client = Client(transport=transport, fetch_schema_from_transport=True)

# Query
query = gql("""
    query GetUser($id: ID!) {
        user(id: $id) {
            id
            name
            email
        }
    }
""")

params = {"id": "123"}
result = client.execute(query, variable_values=params)
print(result['user'])
```

---

## gRPC APIs

### Python

```python
import grpc
import your_service_pb2
import your_service_pb2_grpc

# Create channel
channel = grpc.insecure_channel('api.example.com:50051')
stub = your_service_pb2_grpc.YourServiceStub(channel)

# Make request
request = your_service_pb2.YourRequest(id=123)
response = stub.YourMethod(request)

print(response)
```

### Go

```go
import (
    "context"
    "google.golang.org/grpc"
    pb "your/proto/package"
)

conn, err := grpc.Dial("api.example.com:50051", grpc.WithInsecure())
if err != nil {
    panic(err)
}
defer conn.Close()

client := pb.NewYourServiceClient(conn)

req := &pb.YourRequest{Id: 123}
resp, err := client.YourMethod(context.Background(), req)
if err != nil {
    panic(err)
}
```

---

## Authentication Patterns

### API Key (Bearer Token)

```javascript
// Header-based
headers: {
  'Authorization': `Bearer ${API_KEY}`
}

// Query parameter
const url = `https://api.example.com/data?api_key=${API_KEY}`;
```

### OAuth2

```javascript
// Using simple-oauth2 library
const { AuthorizationCode } = require('simple-oauth2');

const client = new AuthorizationCode({
  client: {
    id: process.env.CLIENT_ID,
    secret: process.env.CLIENT_SECRET
  },
  auth: {
    tokenHost: 'https://oauth.example.com',
    tokenPath: '/oauth/token',
    authorizePath: '/oauth/authorize'
  }
});

// Get authorization URL
const authorizationUri = client.authorizeURL({
  redirect_uri: 'http://localhost:3000/callback',
  scope: 'user:read',
});

// Exchange code for token
async function getToken(code) {
  const tokenParams = {
    code,
    redirect_uri: 'http://localhost:3000/callback',
  };

  const accessToken = await client.getToken(tokenParams);
  return accessToken.token.access_token;
}
```

### Basic Auth

```python
import requests
from requests.auth import HTTPBasicAuth

response = requests.get(
    'https://api.example.com/data',
    auth=HTTPBasicAuth('username', 'password')
)
```

---

## Error Handling & Retries

### Exponential Backoff (JavaScript)

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      // Retry on 5xx errors
      if (response.status >= 500 && attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      return await response.json();
    } catch (error) {
      if (attempt === maxRetries - 1) {
        throw error;
      }

      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
```

### Python Retry with Backoff

```python
import time
from functools import wraps

def retry_with_backoff(max_retries=3, backoff_base=2):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except requests.exceptions.RequestException as e:
                    if attempt == max_retries - 1:
                        raise

                    delay = backoff_base ** attempt
                    print(f"Attempt {attempt + 1} failed. Retrying in {delay}s...")
                    time.sleep(delay)
        return wrapper
    return decorator

@retry_with_backoff(max_retries=3)
def make_api_call():
    response = requests.get('https://api.example.com/data')
    response.raise_for_status()
    return response.json()
```

---

## Rate Limiting

### Token Bucket (JavaScript)

```javascript
class RateLimiter {
  constructor(tokensPerInterval, interval) {
    this.tokensPerInterval = tokensPerInterval;
    this.interval = interval;
    this.tokens = tokensPerInterval;
    this.lastRefill = Date.now();
  }

  async acquire() {
    this.refill();

    if (this.tokens > 0) {
      this.tokens--;
      return;
    }

    // Wait until tokens are available
    const timeToWait = this.interval - (Date.now() - this.lastRefill);
    await new Promise(resolve => setTimeout(resolve, timeToWait));
    return this.acquire();
  }

  refill() {
    const now = Date.now();
    const timePassed = now - this.lastRefill;

    if (timePassed >= this.interval) {
      this.tokens = this.tokensPerInterval;
      this.lastRefill = now;
    }
  }
}

// Usage: 100 requests per minute
const limiter = new RateLimiter(100, 60000);

async function makeRateLimitedRequest() {
  await limiter.acquire();
  return fetch('https://api.example.com/data');
}
```

---

## Common Patterns by Service

### Stripe

```javascript
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

// Create payment intent
const paymentIntent = await stripe.paymentIntents.create({
  amount: 2000, // $20.00
  currency: 'usd',
  payment_method_types: ['card'],
});

// Create customer
const customer = await stripe.customers.create({
  email: 'customer@example.com',
  name: 'John Doe',
});
```

### Twilio

```javascript
const twilio = require('twilio');
const client = twilio(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN
);

// Send SMS
await client.messages.create({
  body: 'Hello from Twilio!',
  from: process.env.TWILIO_PHONE_NUMBER,
  to: '+1234567890'
});
```

### OpenAI

```javascript
const { Configuration, OpenAIApi } = require('openai');

const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

// Chat completion
const completion = await openai.createChatCompletion({
  model: 'gpt-4',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Hello!' }
  ],
});
```

---

## Notes

- Always store API keys in environment variables, never hardcode them
- Implement proper error handling and logging
- Use retry logic with exponential backoff for transient failures
- Respect rate limits to avoid API bans
- Validate and sanitize all input data before sending to APIs
- Use HTTPS for all API communications
- Consider implementing request/response logging for debugging
