API設計って何?一言でいうと「他人(未来の自分)が使いやすい“入口”を設計すること」
ここでいう「API」は、Web API だけじゃなく、
「関数・クラス・モジュールの“使い方の顔”」も含めた広い意味での API だと思ってください。
Python で言えば、
どんな関数名・引数名にするか
どんな戻り値にするか
例外をどう扱うか
どんな単位でモジュールを分けるか
こういった「外から見えるインターフェース」をどう設計するかが、API設計です。
重要なのは、「使う側の気持ち」で考えることです。
書く側の都合ではなく、「こう書けたら気持ちいいよね」という視点で形を決めていきます。
まずは「使いにくいAPI」と「使いやすいAPI」を比べてみる
使いにくい関数の例
次のような関数を考えてみます。
def do_user(a, b, c, d=False, e=None):
# a: name, b: email, c: age
# d: send_mail, e: logger
...
Python一見すると「何でもできそう」ですが、使う側からするとかなりつらいです。
何が必須で、何がオプションなのか分かりにくい。a, b, c が何を意味しているのか、毎回コメントを見ないと分からない。d が True のときに何が起きるのか、コードを読まないと分からない。
こういう API は、「一度書いた本人しか分からない」状態になりがちです。
使いやすい関数にリファクタリングしてみる
同じ処理を、API設計を意識して書き直してみます。
from dataclasses import dataclass
from typing import Protocol
@dataclass
class UserInput:
name: str
email: str
age: int
class MailSender(Protocol):
def send_welcome(self, email: str) -> None:
...
def register_user(
data: UserInput,
mail_sender: MailSender | None = None,
) -> None:
...
Pythonここでは、次のようなことを意識しています。
引数に意味のある名前を付ける。
関連する値は一つのオブジェクト(UserInput)にまとめる。
「メールを送るかどうか」というフラグではなく、「送る手段(MailSender)」を渡す。
使う側のコードは、かなり読みやすくなります。
input_data = UserInput(name="Taro", email="taro@example.com", age=20)
register_user(input_data)
Python「何をしているか」が、コードを見ただけで分かるようになります。
これが API設計の第一歩です。
良いAPI設計の軸その1:名前と引数で「意図」が伝わること
関数名は「何をするか」をそのまま書く
関数名は、コメントの代わりです。
「何をする関数なのか」が、名前だけで分かるのが理想です。
例えば、
process や handle のようなふわっとした名前より、register_user や send_reset_mail のように、具体的な動詞+対象の方が、使う側の理解が早くなります。
Python では、動詞+名詞をスネークケースで書くのが定番です。
create_ordercancel_ordercalculate_total
こういう名前は、それだけで「何をするか」が伝わります。
引数名は「何を渡せばいいか」が分かるようにする
引数名も同じです。
a, b, c ではなく、name, email, age のように、意味が分かる名前にします。
さらに、型ヒントを付けると、より安心感が増します。
def register_user(name: str, email: str, age: int) -> None:
...
Pythonこれだけで、「この関数は名前・メール・年齢を受け取るんだな」と分かります。
良いAPI設計の軸その2:戻り値と例外の扱いを「一貫させる」
成功・失敗をどう表現するかを決める
API設計でよく迷うのが、「失敗したときどうするか」です。
戻り値で True / False を返すのか。
例外を投げるのか。
エラーコードを返すのか。
大事なのは、「一貫性」です。
同じモジュールの中で、似たような関数がバラバラのルールを持っていると、使う側が混乱します。
例えば、「ユーザー登録系の関数は、失敗したら例外を投げる」と決めたなら、
そのモジュール内ではそれを徹底します。
class UserAlreadyExistsError(Exception):
...
def register_user(data: UserInput) -> None:
if exists_user(data.email):
raise UserAlreadyExistsError
...
Python使う側は、「例外で失敗を知る」と分かっているので、こう書けます。
try:
register_user(input_data)
except UserAlreadyExistsError:
print("すでに登録されています")
Python「成功したら True、失敗したら False、たまに例外」みたいな API は、
使う側にとって地雷原です。
None を返すか、例外を投げるか
例えば、「ユーザーをIDで検索する」関数を考えます。
def find_user(user_id: int) -> User | None:
...
Python見つからないときに None を返すのか、UserNotFoundError を投げるのか。
ここも、「どう使われるか」を考えて決めます。
「見つからないことが普通にありうる」なら、None を返すのが自然です。
「見つからないのは異常事態」なら、例外の方が自然です。
どちらにしても、APIとして「どちらの方針か」を揃えることが大事です。
良いAPI設計の軸その3:呼び出し側のコードが「読みやすいか」を最優先する
呼び出し側から逆算して設計する
API設計で一番強い考え方は、「先に呼び出し側を書く」です。
例えば、「ユーザー登録API」を作りたいとき、
まずは理想の呼び出しコードを書いてみます。
client = UserApiClient(base_url="https://example.com")
user = client.register(name="Taro", email="taro@example.com")
print(user.id)
Pythonこう書けたら気持ちいいな、という形を先に決めてしまう。
そのあとで、「この形を実現するには、中身をどう作ればいいか」を考えます。
関数やクラスの設計を、「中身から」ではなく「使い方から」考える。
これが API設計の本質です。
「引数が多すぎる」と感じたら、オブジェクトにまとめる
呼び出し側のコードを見ていて、
引数が多すぎて読みづらいと感じたら、それは API の改善ポイントです。
例えば、
register_user(
name="Taro",
email="taro@example.com",
age=20,
address="Tokyo",
phone="000-0000-0000",
send_mail=True,
send_sms=False,
)
Pythonのように増えてきたら、
「ユーザー登録リクエスト」というオブジェクトにまとめた方が読みやすくなります。
@dataclass
class RegisterUserRequest:
name: str
email: str
age: int
address: str
phone: str
send_mail: bool = True
send_sms: bool = False
def register_user(req: RegisterUserRequest) -> User:
...
Python呼び出し側はこうなります。
req = RegisterUserRequest(
name="Taro",
email="taro@example.com",
age=20,
address="Tokyo",
phone="000-0000-0000",
)
user = register_user(req)
Python「何を渡しているか」が一目で分かるようになります。
Web APIとしてのAPI設計も、考え方は同じ
エンドポイントの設計も「使う側の目線」で考える
Web API(HTTP の API)も、本質は同じです。
「クライアントから見て、分かりやすく・一貫性があるか」が大事です。
例えば、ユーザー関連の API を考えます。
POST /users でユーザーを作る。GET /users/{id} でユーザーを取得する。PATCH /users/{id} でユーザー情報を更新する。
こういう「動詞+名詞」の組み合わせを、REST ではよく使います。
レスポンスの JSON も、Python のドメインモデルと対応させると分かりやすくなります。
{
"id": 1,
"name": "Taro",
"email": "taro@example.com"
}
これを Python 側では User クラスとして扱う。
Web API の設計と、Python の API設計は、実はかなり地続きです。
API設計とテスト・品質の関係
良いAPIは「テストしやすい」
APIがきれいに設計されていると、テストも自然と書きやすくなります。
例えば、さっきの register_user のように、
入力を一つのオブジェクトにまとめる
外部依存(メール送信など)はインターフェースで受け取る
という形にしておくと、テストはこう書けます。
class DummyMailSender:
def __init__(self) -> None:
self.sent_to: list[str] = []
def send_welcome(self, email: str) -> None:
self.sent_to.append(email)
def test_register_user_sends_mail():
mailer = DummyMailSender()
data = UserInput(name="Taro", email="taro@example.com", age=20)
register_user(data, mail_sender=mailer)
assert mailer.sent_to == ["taro@example.com"]
PythonAPIが「きれいに分かれている」ほど、
テスト対象を小さく切り出せます。
逆に、APIがごちゃごちゃしていると、
テストもごちゃごちゃして、壊れやすくなります。
良いAPIは「変更に強い」
API設計をちゃんとしておくと、
内部の実装を変えても、外から見える形を変えずに済むことが増えます。
例えば、UserApiClient の内部実装を、
requests から httpx に変える
同期処理から非同期処理に変える
といった変更をしても、client.register(name=..., email=...) という API を変えなければ、
呼び出し側のコードはそのまま動きます。
API設計は、「外から見える約束」を決めることです。
その約束を守り続ける限り、中身は好きなだけ改善できます。
これが品質と開発速度の両方に効いてきます。
初心者がAPI設計を練習するときのおすすめのやり方
先に「理想の呼び出しコード」を書いてみる
何か機能を作るとき、
いきなり中身を書き始めるのではなく、
まずは「こう使えたらいいな」という呼び出し側を書いてみてください。
例えば、Todo アプリなら、
todo = TodoService()
todo.add("買い物に行く")
for item in todo.list():
print(item.title)
Pythonのように、「こう書けたら気持ちいい」という形を先に決める。
それを実現するために、クラスや関数の設計を考える。
この練習を繰り返すと、自然と API設計の感覚が育っていきます。
「このAPIを初めて見る人は、迷わず使えるか?」と自問する
自分の書いた関数やクラスを見て、
こう自分に問いかけてみてください。
名前だけ見て、何をするか分かるか。
引数名と型ヒントだけ見て、何を渡せばいいか分かるか。
戻り値と例外のルールが、一貫しているか。
ここで「うーん…」と感じたら、
それは API設計を見直すチャンスです。
まとめ(API設計は「使う側の気持ちで形を決める」設計の技術)
初心者目線でまとめると、API設計はこういうものです。
API設計は、関数・クラス・モジュール、そしてWeb APIなどの「外から見える顔」を、使う側の目線で分かりやすく・一貫性を持って設計すること。
名前・引数・戻り値・例外の扱いを丁寧に決めることで、呼び出し側のコードが読みやすくなり、テストしやすくなり、内部実装を変えても壊れにくい「強い約束」を作れる。
練習としては、「先に理想の呼び出しコードを書く」「初めて見る人が迷わず使えるかを自問する」といった小さな習慣から始めると、API設計のセンスがじわじわ育っていく。
