Application Programming Interface (API) is a set of rules and protocols that allows different software applications to communicate with each other. Think of it as a waiter in a restaurant who takes your order to the kitchen and brings back your food.
- Separation of Concerns: Frontend and backend can be developed independently
- Scalability: Multiple applications can use the same API
- Flexibility: Different frontends (web, mobile, desktop) can use the same backend
- Team Collaboration: Web developers and Python developers can work together seamlessly
REST (Representational State Transfer) is an architectural style for designing networked applications. It uses standard HTTP methods to perform operations on resources.
- GET: Retrieve data (Read)
- POST: Create new data (Create)
- PUT: Update existing data (Update/Replace)
- PATCH: Partially update data (Partial Update)
- DELETE: Remove data (Delete)
- 200 OK: Success
- 201 Created: Resource created successfully
- 400 Bad Request: Invalid request data
- 401 Unauthorized: Authentication required
- 404 Not Found: Resource doesn't exist
- 500 Internal Server Error: Server error
from flask import Flask, request, jsonify
app = Flask(__name__)
# Sample data
users = [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"}
]
# GET all users
@app.route('/api/users', methods=['GET'])
def get_users():
return jsonify(users)
# GET single user
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = next((u for u in users if u['id'] == user_id), None)
if user:
return jsonify(user)
return jsonify({"error": "User not found"}), 404
# POST new user
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
new_user = {
"id": len(users) + 1,
"name": data.get('name'),
"email": data.get('email')
}
users.append(new_user)
return jsonify(new_user), 201
if __name__ == '__main__':
app.run(debug=True)from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
name: str
email: str
class UserResponse(BaseModel):
id: int
name: str
email: str
users = [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"}
]
@app.get("/api/users")
def get_users():
return users
@app.post("/api/users", response_model=UserResponse)
def create_user(user: User):
new_user = {
"id": len(users) + 1,
"name": user.name,
"email": user.email
}
users.append(new_user)
return new_user// GET request
async function getUsers() {
try {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const users = await response.json();
console.log(users);
return users;
} catch (error) {
console.error('Error fetching users:', error);
}
}
// POST request
async function createUser(userData) {
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error('Failed to create user');
}
const newUser = await response.json();
console.log('User created:', newUser);
return newUser;
} catch (error) {
console.error('Error creating user:', error);
}
}
// Usage
getUsers();
createUser({ name: "Charlie", email: "charlie@example.com" });// GET request
axios.get('/api/users')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('Error:', error);
});
// POST request
axios.post('/api/users', {
name: "Diana",
email: "diana@example.com"
})
.then(response => {
console.log('User created:', response.data);
})
.catch(error => {
console.error('Error:', error);
});# Python (Flask-JWT-Extended)
from flask_jwt_extended import JWTManager, create_access_token, jwt_required
app.config['JWT_SECRET_KEY'] = 'your-secret-key'
jwt = JWTManager(app)
@app.route('/api/login', methods=['POST'])
def login():
# Verify credentials (simplified)
username = request.json.get('username')
password = request.json.get('password')
if username == 'admin' and password == 'password':
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token)
return jsonify({"error": "Invalid credentials"}), 401
@app.route('/api/protected', methods=['GET'])
@jwt_required()
def protected():
return jsonify({"message": "This is a protected route"})// JavaScript - Storing and using tokens
localStorage.setItem('token', response.data.access_token);
// Include token in requests
const token = localStorage.getItem('token');
fetch('/api/protected', {
headers: {
'Authorization': `Bearer ${token}`
}
})- Use clear, descriptive URLs:
/api/users/123not/api/getUser?id=123 - Use HTTP methods correctly: GET for reading, POST for creating
- Return consistent response formats
- Include proper error messages
- Version your APIs:
/api/v1/users
# Python
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "Resource not found"}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({"error": "Internal server error"}), 500// JavaScript
function handleApiError(error) {
if (error.response) {
// Server responded with error status
console.error('API Error:', error.response.status, error.response.data);
} else if (error.request) {
// Request made but no response
console.error('Network Error:', error.request);
} else {
// Something else happened
console.error('Error:', error.message);
}
}# Python with Pydantic (FastAPI)
from pydantic import BaseModel, validator
class User(BaseModel):
name: str
email: str
age: int
@validator('email')
def validate_email(cls, v):
if '@' not in v:
raise ValueError('Invalid email format')
return v
@validator('age')
def validate_age(cls, v):
if v < 0 or v > 150:
raise ValueError('Age must be between 0 and 150')
return vimport pytest
from your_app import app
@pytest.fixture
def client():
app.config['TESTING'] = True
with app.test_client() as client:
yield client
def test_get_users(client):
response = client.get('/api/users')
assert response.status_code == 200
assert len(response.get_json()) >= 0
def test_create_user(client):
user_data = {"name": "Test User", "email": "test@example.com"}
response = client.post('/api/users', json=user_data)
assert response.status_code == 201
assert response.get_json()['name'] == "Test User"// Mock fetch for testing
global.fetch = jest.fn();
test('should fetch users successfully', async () => {
const mockUsers = [
{ id: 1, name: 'Alice', email: 'alice@example.com' }
];
fetch.mockResolvedValueOnce({
ok: true,
json: async () => mockUsers
});
const users = await getUsers();
expect(users).toEqual(mockUsers);
expect(fetch).toHaveBeenCalledWith('/api/users');
});Create a simple API with the following endpoints:
GET /api/books- List all booksGET /api/books/{id}- Get a specific bookPOST /api/books- Create a new bookPUT /api/books/{id}- Update a bookDELETE /api/books/{id}- Delete a book
Create a simple frontend that:
- Displays a list of books from the API
- Has a form to add new books
- Allows editing existing books
- Provides delete functionality
- Python developers create the API
- Web developers create the frontend
- Test the integration together
- Handle errors gracefully on both sides
- Postman: Test API endpoints
- Insomnia: Alternative API testing tool
- Thunder Client: VS Code extension for API testing
- Swagger/OpenAPI: API documentation
- Flask: Lightweight web framework
- FastAPI: Modern, fast API framework
- Requests: HTTP library for making API calls
- SQLAlchemy: Database ORM
- Axios: HTTP client library
- Fetch API: Built-in browser API
- SWR/React Query: Data fetching libraries for React
- APIs are the bridge between frontend and backend applications
- REST principles provide a standard way to design APIs
- Both Python and JavaScript developers need to understand API concepts
- Security and validation are crucial for production APIs
- Testing ensures your APIs work correctly
- Documentation helps other developers use your APIs
- Error handling improves user experience
- Practice building simple APIs with Python
- Practice consuming APIs with JavaScript
- Learn about GraphQL as an alternative to REST
- Explore microservices architecture
- Study API security in depth
- Learn about API rate limiting and caching
Remember: The best way to learn APIs is by building and using them. Start small, practice regularly, and gradually tackle more complex scenarios!