Designing Developer-First APIs: Beyond Technical Implementation
Overview
Designing a REST API involves more than just selecting a framework or hosting on a cloud provider. A truly great API acts as a seamless interface that developers enjoy using, yet even major tech companies often fail at basic usability. This guide explores the architectural decisions and best practices that transform a functional API into a professional-grade product, focusing on standards, consistency, and the implementation of advanced features like metadata merging.
Prerequisites
To get the most out of this tutorial, you should have a solid grasp of Python, basic HTTP methods (GET, POST, etc.), and the fundamentals of JSON. Familiarity with FastAPI and SQLAlchemy will help when we dive into the code walkthrough for custom data handling.
Key Libraries & Tools
- : The industry-standard specification for describing and documenting RESTful APIs.
- : A modern, high-performance Python web framework that automatically generates OpenAPI schemas.
- : A powerful Python SQL Toolkit and ORM used here to manage database models.
- : Data validation and settings management using Python type annotations.
Mastering Standards and Consistency
Standards provide a shared vocabulary between the provider and the consumer. Adopting the specification allows you to generate interactive documentation automatically. This transparency reduces the friction of integration. Beyond documentation, adherence to naming conventions—using plural nouns like /customers instead of singular /customer—creates a predictable environment.

Consistency is the hallmark of a mature API. If your /orders endpoint returns a payer_id, your /invoices endpoint should not suddenly switch to calling that same entity a recipient without an ID. Map out your resource relationships early. Ensure that pagination, error handling, and date formats remain uniform across every single endpoint.
Code Walkthrough: Implementing Stripe-Style Metadata
One of the most powerful features for third-party integration is the ability to store custom metadata. This allows users to link your resources to IDs in their other systems (like an accounting ID or a CRM link).
The Base Model with Custom Data Magic
In this implementation, we use and to create a base class that handles "merge" logic for metadata, similar to the .
import json
from sqlalchemy.orm import declarative_base
from pydantic import BaseModel, validator
class BaseCustomData:
def update_custom_data(self, new_data: dict):
# 1. Load existing stringified JSON from the DB
current_data = json.loads(self.custom_data or "{}")
# 2. Iterate and merge logic
for key, value in new_data.items():
if value is None:
current_data.pop(key, None) # Unset if value is null
else:
current_data[key] = value # Merge new key-values
# 3. Save back as string
self.custom_data = json.dumps(current_data)
Explanation of the Logic
- The Merge Operation: Instead of overwriting the entire
custom_datafield, the method loads the existing JSON, updates specific keys, and preserves others. This prevents accidental data loss during partial updates. - The Null Deletion Pattern: By checking if a value is
None, the API follows the convention where sending{"my_key": null}explicitly removes that key from the database. - Serialization: We store the data as a string in the database for compatibility but expose it as a dictionary in the API layer for ease of use.
Syntax Notes & Best Practices
When defining field names, stick to snake_case. It is significantly more readable than mashing words together. Furthermore, utilize sensible defaults for arguments. If a user searches for transactions, default the end_date to the current time rather than forcing them to provide it. This reduces the cognitive load on the developer using your tool.
Tips & Gotchas
- Version Your API: Always include the version in the URL (e.g.,
/v1/) to prevent breaking changes for existing users. - Clear Error Bodies: Don't just return a 400 error. Provide a JSON response body explaining why the request failed.
- Navigation: Ensure resources are interconnected. An order object should include a link or ID for the customer, and vice-versa. Avoid creating "data islands" where resources cannot be reached from related objects.
- 13%· products
- 13%· products
- 13%· products
- 6%· companies
- 6%· products
- Other topics
- 50%

6 Easy Tips to Design an AWESOME REST API
WatchArjanCodes // 23:32
On this channel, I post videos about programming and software design to help you take your coding skills to the next level. I'm an entrepreneur and a university lecturer in computer science, with more than 20 years of experience in software development and design. If you're a software developer and you want to improve your development skills, and learn more about programming in general, make sure to subscribe for helpful videos. I post a video here every Friday. If you have any suggestion for a topic you'd like me to cover, just leave a comment on any of my videos and I'll take it under consideration. Thanks for watching!