Project Description: Generate code to integrate with external APIs
Current Step: collab → generate_code
This document provides a comprehensive, detailed, and professional output for the "Generate Code" step of your API Integration Builder workflow. The goal of this step is to provide you with production-ready, well-commented code that facilitates robust integration with external APIs.
For demonstration purposes, we will use a common pattern for interacting with RESTful APIs and illustrate it with a well-known public API: [JSONPlaceholder](https://jsonplaceholder.typicode.com/). This example will cover common operations such as fetching data, creating records, updating records, and deleting records.
The generated code is designed to be easily adaptable to various REST APIs by modifying the base URL, endpoints, authentication methods, and data payloads.
Integrating with external APIs typically involves the following core components:
Content-Type, Authorization), and request body (payload).JSONPlaceholder is a free online REST API that you can use whenever you need some fake data. It's excellent for prototyping and demonstrating API interactions without needing actual backend infrastructure or authentication.
https://jsonplaceholder.typicode.com * /posts: Get all posts, create a new post.
* /posts/{id}: Get, update, or delete a specific post.
We will provide a Python-based solution using the popular requests library, known for its simplicity and power.
import requests
import json
import os
from typing import Dict, Any, List, Optional
# --- Configuration ---
# It's best practice to manage sensitive information (like API keys) using environment variables.
# For JSONPlaceholder, no API key is strictly required, but this pattern is crucial for real APIs.
# Example: API_KEY = os.getenv("YOUR_API_KEY", "your_default_api_key_if_not_set")
# BASE_URL = os.getenv("JSONPLACEHOLDER_BASE_URL", "https://jsonplaceholder.typicode.com")
# For simplicity, we'll define it directly for this example.
BASE_URL = "https://jsonplaceholder.typicode.com"
# --- Utility Functions ---
def _handle_api_response(response: requests.Response) -> Dict[str, Any]:
"""
Handles common API response patterns, including error checking and JSON parsing.
Raises an exception for non-2xx status codes or invalid JSON.
"""
try:
response.raise_for_status() # Raises HTTPError for bad responses (4xx or 5xx)
except requests.exceptions.HTTPError as e:
error_message = f"API Request failed: {response.status_code} - {response.reason}"
try:
error_details = response.json()
error_message += f"\nDetails: {json.dumps(error_details, indent=2)}"
except json.JSONDecodeError:
error_message += f"\nResponse body: {response.text}"
raise APIIntegrationError(error_message, status_code=response.status_code) from e
except requests.exceptions.RequestException as e:
raise APIIntegrationError(f"Network or request error: {e}", status_code=None) from e
try:
return response.json()
except json.JSONDecodeError as e:
raise APIIntegrationError(f"Failed to decode JSON response: {e}\nResponse: {response.text}",
status_code=response.status_code) from e
# --- Custom Exception for API Integration ---
class APIIntegrationError(Exception):
"""Custom exception for API integration failures."""
def __init__(self, message: str, status_code: Optional[int] = None):
super().__init__(message)
self.status_code = status_code
# --- API Interaction Functions ---
class JSONPlaceholderAPI:
"""
A client class for interacting with the JSONPlaceholder API.
Encapsulates common API operations.
"""
def __init__(self, base_url: str = BASE_URL, api_key: Optional[str] = None):
self.base_url = base_url
self.headers = {
"Content-Type": "application/json",
"Accept": "application/json"
}
if api_key:
# For APIs requiring an API key, it's often passed in a header like Authorization or X-API-Key
# self.headers["Authorization"] = f"Bearer {api_key}" # Example for Bearer token
# self.headers["X-API-Key"] = api_key # Example for custom API key header
pass # JSONPlaceholder does not require an API key
def _get_url(self, endpoint: str) -> str:
"""Constructs the full URL for a given endpoint."""
return f"{self.base_url}{endpoint}"
def get_posts(self) -> List[Dict[str, Any]]:
"""
Fetches all posts from the API.
Returns a list of post dictionaries.
"""
url = self._get_url("/posts")
print(f"Fetching all posts from: {url}")
response = requests.get(url, headers=self.headers)
return _handle_api_response(response)
def get_post_by_id(self, post_id: int) -> Dict[str, Any]:
"""
Fetches a single post by its ID.
Returns a dictionary representing the post.
Raises APIIntegrationError if the post is not found (e.g., 404).
"""
url = self._get_url(f"/posts/{post_id}")
print(f"Fetching post with ID {post_id} from: {url}")
response = requests.get(url, headers=self.headers)
return _handle_api_response(response)
def create_post(self, title: str, body: str, user_id: int) -> Dict[str, Any]:
"""
Creates a new post.
Returns the newly created post data, including its assigned ID.
"""
url = self._get_url("/posts")
payload = {
"title": title,
"body": body,
"userId": user_id
}
print(f"Creating new post at: {url} with payload: {payload}")
response = requests.post(url, headers=self.headers, json=payload)
return _handle_api_response(response)
def update_post(self, post_id: int, title: Optional[str] = None, body: Optional[str] = None) -> Dict[str, Any]:
"""
Updates an existing post using PATCH (partial update).
Only sends fields that are provided.
Returns the updated post data.
"""
url = self._get_url(f"/posts/{post_id}")
payload = {}
if title is not None:
payload["title"] = title
if body is not None:
payload["body"] = body
if not payload:
print(f"No update data provided for post ID {post_id}. Skipping update.")
return self.get_post_by_id(post_id) # Return current state if no changes
print(f"Updating post ID {post_id} at: {url} with payload: {payload}")
response = requests.patch(url, headers=self.headers, json=payload)
return _handle_api_response(response)
def delete_post(self, post_id: int) -> Dict[str, Any]:
"""
Deletes a post by its ID.
Returns an empty dictionary for JSONPlaceholder on successful deletion (status 200).
Some APIs might return a confirmation message.
"""
url = self._get_url(f"/posts/{post_id}")
print(f"Deleting post ID {post_id} from: {url}")
response = requests.delete(url, headers=self.headers)
return _handle_api_response(response) # JSONPlaceholder returns {} for successful delete
# --- Example Usage ---
if __name__ == "__main__":
print("--- Initializing JSONPlaceholder API Client ---")
api_client = JSONPlaceholderAPI()
print("\n--- Testing GET All Posts ---")
try:
posts = api_client.get_posts()
print(f"Successfully fetched {len(posts)} posts. First 3 posts:")
for i, post in enumerate(posts[:3]):
print(f" Post {i+1}: ID={post.get('id')}, Title='{post.get('title')[:50]}...'")
except APIIntegrationError as e:
print(f"Error fetching posts: {e}")
print("\n--- Testing GET Post by ID (Existing) ---")
try:
single_post = api_client.get_post_by_id(1)
print(f"Successfully fetched post ID 1: Title='{single_post.get('title')}'")
except APIIntegrationError as e:
print(f"Error fetching post ID 1: {e}")
print("\n--- Testing GET Post by ID (Non-existent) ---")
try:
non_existent_post = api_client.get_post_by_id(99999)
print(f"Fetched non-existent post: {non_existent_post}")
except APIIntegrationError as e:
print(f"Expected error for non-existent post (ID 99999): {e}")
assert e.status_code == 404, "Expected 404 error for non-existent post"
print("\n--- Testing CREATE Post ---")
new_post_data = {
"title": "My New Awesome Post Title",
"body": "This is the content of my new post, generated by the API Integration Builder!",
"userId": 1
}
try:
created_post = api_client.create_post(**new_post_data)
print(f"Successfully created post: ID={created_post.get('id')}, Title='{created_post.get('title')}'")
# Store the ID to use for update/delete
post_to_manipulate_id = created_post.get('id')
except APIIntegrationError as e:
print(f"Error creating post: {e}")
post_to_manipulate_id = None # Ensure we don't try to manipulate a non-existent post
if post_to_manipulate_id:
print(f"\n--- Testing UPDATE Post (ID: {post_to_manipulate_id}) ---")
try:
updated_post = api_client.update_post(
post_id=post_to_manipulate_id,
title="Updated Title from Builder",
body="The content has been revised by the integration."
)
print(f"Successfully updated post: ID={updated_post.get('id')}, Title='{updated_post.get('title')}'")
except APIIntegrationError as e:
print(f"Error updating post ID {post_to_manipulate_id}: {e}")
print(f"\n--- Testing DELETE Post (ID: {post_to_manipulate_id}) ---")
try:
delete_result = api_client.delete_post(post_to_manipulate_id)
print(f"Successfully deleted post ID {post_to_manipulate_id}. Response: {delete_result}")
# Verify it's gone (should result in 404)
print(f"Attempting to fetch deleted post ID {post_to_manipulate_id} to confirm deletion...")
api_client.get_post_by_id(post_to_manipulate_id)
except APIIntegrationError as e:
print(f"Expected error when fetching deleted post (ID {post_to_manipulate_id}): {e}")
assert e.status_code == 404, "Expected 404 error after deletion"
print("\n--- API Interaction Demo Complete ---")
* requests: The primary library for making HTTP requests.
* json: For handling JSON serialization/deserialization (though requests handles most of it automatically).
* os: Used for environment variable management (best practice for sensitive data).
* typing: For type hints, improving code readability and maintainability.
* BASE_URL: The root URL of the API. This should be easily configurable.
* Environment Variables: The comments highlight the best practice of using os.getenv() for API keys and other sensitive configurations. This keeps credentials out of your codebase.
_handle_api_response Function:* Centralized Error Handling: This private utility function encapsulates the logic for checking HTTP status codes and parsing JSON responses.
* response.raise_for_status(): A powerful requests method that automatically raises an HTTPError for 4xx or 5xx responses.
* Detailed Error Messages: It attempts to extract error details from the API's JSON response body, providing more context for debugging.
* APIIntegrationError: A custom exception class is used to provide a consistent way to handle and catch API-specific errors throughout your application.
APIIntegrationError Custom Exception: * A custom exception class derived from Exception. This allows you to catch specific API-related errors distinctly from other potential application errors. It includes a status_code attribute for more context.
JSONPlaceholderAPI Class:* Encapsulation: This class encapsulates all API interaction logic, making it reusable and organized.
* __init__: Initializes the client with the base_url and sets up default
This document outlines the professional project plan for initiating your API integration, leveraging our "API Integration Builder" capabilities. As step 2 of 2, this deliverable details the foundational elements required to successfully integrate with your chosen external API(s).
Project Title: API Integration Project - [Customer Specific API Name, e.g., Salesforce, Stripe, Custom ERP]
Purpose: To establish a robust, secure, and efficient integration between your internal systems/applications and the specified external API(s). This project aims to unlock new functionalities, streamline data flow, and enhance operational efficiency by leveraging external services.
Objective: To successfully design, develop, test, and deploy a custom API integration solution that meets your defined business requirements and technical specifications.
This section defines the boundaries of the integration project and outlines the initial information required to proceed.
To effectively initiate this project, we require the following details:
The API integration project will proceed through the following key phases:
* API endpoints to be used.
* Authentication strategy.
* Data mapping specifications.
* Error handling strategy.
* Proposed architecture and technology stack.
* Security considerations.
Upon completion of the respective phases, you will receive the following:
To proceed with the "API Integration Project," we kindly request the following:
We look forward to partnering with you to build a successful and valuable API integration solution.