API 是程序之间互相沟通的方式。一个提出请求,另一个作出响应。我们以一个电商平台为例,看看请求由哪些部分组成、怎样读 JSON 响应,以及 Claude Code 连接外部数据源时该检查什么。最后再简单说说 FastAPI 会在哪里出现。

什么是 API

API(Application Programming Interface)是一种程序接口,规定了有哪些可用操作、怎样调用它们。API 不只属于某个应用程序,程序库、操作系统、浏览器也都有自己的 API。程序可以通过它请求另一个程序(或其中一部分)做点什么,再拿到结果。这里主要讲的是网络 API,也就是通过地址访问的那种。

想象一家餐厅。作为客人,您不会直接走进厨房,跟厨师解释该怎么做菜 - 您告诉服务员想要菜单上的哪道菜,服务员把订单传给厨房,再把做好的菜端给您。API 就是这个服务员。客户端程序通过它下单,厨房(另一个程序)负责完成。您不需要知道内部是怎么运作的,只要清楚菜单上有什么、点了会得到什么就够了。

API 本质上是一份契约:"按这样的方式来找我,我就按那样的格式给你结果"。

当 Claude Code 提议连接某个服务时,它需要先弄清楚能请求什么、会得到什么响应。API 文档描述的正是这份"菜单"。

还有一个英文的搞笑短片 - "How API work"。它只是给同一个想法配了个画面,并不是对这个主题的讲解。需要的内容下面的文字里都有。

以电商平台为例看请求和响应

想象一个电商平台,供应商通过自己的程序往里面传商品。靠网站上的按钮一件件搬上千种商品太麻烦,所以平台提供了 API 操作。比如 POST /products 用来添加商品,GET /reports 用来取报表。

可用操作的地址叫 endpoint。理解一个请求,既要看地址,也要看 HTTP 方法。GET /products 和 POST /products 可能执行不同的操作。GET 一般用来读取,POST 用来把数据交给服务器处理。

数据常用 JSON 格式传输。花括号把一个对象的字段括在一起,字符串加引号,对象列表放进方括号:

{
  "name": "台灯",
  "price": 15000,
  "count": 350
}

客户端发送请求,服务器处理请求并返回响应。下面是简化后的交换流程,不是完整的 HTTP 包:

POST /products
{ "name": "台灯", "price": 15000, "count": 350 }

201 Created
{ "id": 4567, "status": "added" }

201 Created 是响应状态码,下面的 JSON 是带结果的响应体。真正的 API 文档会写明 endpoint、方法、必填参数、请求头、响应格式和可能出现的错误。更稳妥的做法是先拿一个测试对象试试,而不是一上来就用完整数据集。

API 是一份契约。Claude Code 可以写出请求代码,但代码是否符合契约,要靠最新文档和服务的真实响应来验证。

Frontend、backend 以及它们之间的 API

API 不只是用来连接不同的应用程序。很多时候,一个应用内部本身就由两个程序组成,它们之间也是通过 API 沟通的。

Frontend 是您在浏览器里打交道的那部分应用,包括按钮、图片、菜单、表单和页面的行为。其中的程序逻辑通常用 JavaScript 编写。它既能自己完成复杂计算,也能在需要数据或服务器端操作时去请求服务器。

Backend 是应用的服务器端部分,负责处理支付、保存订单、校验权限、查找商品。开发阶段服务器可能就跑在您自己的电脑上,发布之后则跑在托管平台上。用户拿到的页面里不包含它的源代码,浏览器收到的只是请求的响应。

它们之间怎么沟通?还是靠 API。Backend 提供的接口不仅面向外部供应商,也面向自己的 frontend:

  • 您打开页面 - frontend 调用接口 /products/list,backend 返回商品列表,frontend 把它漂漂亮亮地画出来;
  • 您点了"购买" - frontend 把数据发到接口 /orders,backend 创建订单。
  • 浏览器里的操作
  • frontend 发起请求
  • backend 处理
  • 带数据的响应

可见,通过 API 沟通的既可以是不同的应用,也可以是同一个应用内部的不同部分。在这张图里,浏览器发出请求,backend 处理数据并返回结果。数据库不一定要直接对浏览器开放。

浏览器发起的请求

前面说 frontend 会"调用"backend 的接口,现在看看它在代码里长什么样。浏览器内置了一个函数 fetch,它向指定地址发出 HTTP 请求并返回响应。响应体通常是 JSON,用 .json() 方法把它解析成对象。

来看一份语言区域(locale)列表,也就是语言和地区设置的各种组合。假设 backend 已经能在地址 /locales 上返回列表,frontend 要把它显示出来。比如服务器返回:

[
  { "code": "en-GB", "language": "English", "country": "United Kingdom" },
  { "code": "de-DE", "language": "Deutsch", "country": "Germany" }
]

响应是怎么到手的

下面这段代码在页面的异步处理函数内部执行,负责拿到数据;渲染表格的部分这里没有展示。

const res = await fetch("/locales");
if (!res.ok) {
  throw new Error(`HTTP ${res.status}`);
}
const locales = await res.json();
// 到这里 locales 就是表格要用的数据了

读取数据之前先检查 res.ok。404 或 500 这类 HTTP 错误本身并不会让 fetch 抛出异常。网络请求本身没发出去,或者 JSON 解析失败,是另外两种情况。这段代码抛出的错误应该由页面代码捕获,再显示一句用户能看懂的提示。虽然原因不同,但对用户来说结果都一样 - 没拿到数据。详情见 MDN 的 fetch 指南。

这里有几个容易漏掉的细节:

  • fetch 默认发 GET 请求,也就是取数据。别的操作要显式指定方法;比如我们这个电商平台就是用 POST 接收新商品的;
  • 地址 /locales 是相对路径。在普通 HTTP 页面里,它指向同一个 origin 的根路径。Origin 由协议、主机和端口共同决定;服务器上必须有这条路由;
  • fetch 和 .json() 都不会立即返回结果,所以前面要加 await - 意思是"等响应回来"。

如果用 file:// 直接打开 HTML 文件,/locales 不会变成您的 HTTP 服务器地址。这个例子要通过实现了该路由的服务器打开页面才行,HTML 文件本身创建不出这条路由。

Frontend 和 API 也可能不在同一个 origin 上。这种情况下,服务器要通过 CORS 放行,浏览器才能读到响应。CORS 是一种跨站访问机制,不是随便什么网络错误的代称。把页面和 API 放在同一个 origin 能让例子更简单,但并不是所有应用都必须这样做。详情见 MDN 的 CORS 说明。

谁在给浏览器答复

谁在响应 fetch("/locales")?是 backend,而它不一定是用 Python 加 FastAPI 写的。API 是程序之间的约定,跟具体用什么语言无关。同样的 endpoint 也可以用 Node.js、Go 或 Java 实现。比如在 Node.js 服务器的处理函数里,可以直接返回准备好的数组 locales。下面只是处理函数的片段,不是完整的服务器:

// req 和 res 由处理函数提供,locales 提前准备好了
if (req.method === "GET" && req.url === "/locales") {
  res.statusCode = 200;
  res.setHeader("Content-Type", "application/json; charset=utf-8");
  res.end(JSON.stringify(locales));
}

从外面看,这仍然是同一个 HTTP 请求 GET /locales,响应里带着状态码 200 和 JSON。Frontend 不需要知道 backend 用什么语言写的,它只需要知道地址、请求规则和响应格式。

当 Claude Code 写出 fetch("/locales") 时,请它也把另一头展示出来,让智能体指出是哪段代码在响应这个请求。然后再检查一下,如果响应没送到,用户会看到什么。

API 有哪些类型

可以按两个维度来划分 API:

  • 本地和网络 - 比如程序库的 API 是从程序代码里直接调用的,而网络 API 要经过网络访问。这个网络也可以是内部网络,不一定要连到公网;
  • 公开和内部 - 公开 API 面向外部开发者,内部 API 用来连接系统内部的各个部分。公开 API 也可能需要注册、付费或获得授权。

Web API 常常基于 HTTP - 一种请求响应协议。您会在文档里看到 REST 这个词,它描述的是一种设计 API 的方式;不是所有 HTTP API 都自动符合 REST。现在更值得花时间学会的是读懂响应里的状态码:

  • 200 - 一切正常;
  • 201 - 已创建(比如 POST 之后新建了一个对象);
  • 404 - 没找到;
  • 401 - 请求里没有合适的身份验证信息;
  • 429 - 请求太频繁;
  • 500 - 服务器内部错误。

现在没必要深挖细节。重要的是抓住"请求换响应"这个核心思路,目前这样就够了。

状态码不用死记硬背。网站 http.cat 用一张猫咪图片来展示每一个 HTTP 状态码。

服务为什么需要访问密钥

很多服务一开始就要求您先拿到一个 API 密钥 - 一串跟您的账号或项目绑定的字符串。传递方式由文档规定,通常放在请求头里。服务靠它验证访问权限,还能统计您发了多少请求。有些集成里会用到令牌(token)这个词,作用类似,但获取方式、有效期和权限范围可能不一样。

没有合适的访问凭证,请求就不会成功,比如服务器可能返回 401。请求太频繁通常用 429 表示。响应的具体含义和允许的额度,要看对应服务的文档。也有一些开放数据源根本不需要密钥。

密钥是保密的,它能在授予它的权限范围内访问服务,有时产生的费用还要由您来承担。不要把它贴进聊天记录、公开代码或 Git 仓库。在服务器端,可以通过进程环境传递这个值;一旦它出现在浏览器的 JavaScript 里,就再也藏不住了。具体在哪里设置、怎样传给程序,是密钥和环境变量那篇文章的话题。

去哪里找现成的 API

通过 API,应用可以接入地图、天气、支付和物流。开放的文档能帮您在真正接入之前先了解服务能做什么。最好先从需求本身出发:确定需要什么数据、多久更新一次、打算拿它做什么。

找 API 时可以看看 Postman Public API Network,这是一个 API 和请求示例的目录。找到的示例只是个起点,地址、格式、权限和限制都还是要以服务提供方的官方文档为准。

FastAPI 在这里扮演什么角色

FastAPI 是用 Python 创建 API 和 Web 服务器的框架之一。它能把 endpoint 跟您的服务器代码连起来,接收请求、返回 JSON 响应。只有当您要自己动手做 Python backend 时才需要用到 FastAPI;如果只是使用现成的 API,不需要专门去学它。在第 7 级,知道这个名字、理解这条界限就够了。

这跟 Claude Code 有什么关系

有了这些概念,评估智能体的工作就容易多了。它可以读懂别人的 API 文档、写出客户端请求,也可以用 FastAPI 搭出您自己的 API。您需要盯住的是从请求到页面上出现数据这条具体路径:谁发请求、谁来响应、响应又是怎么处理的?

比如地震监测有一个 USGS 提供的公开 GeoJSON feed。过去一小时数据列表的地址是:

GET https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson

下面是一个关于响应结构的小示例,事件是虚构的,字段也做了精简,不是 USGS 的实时数据:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": { "mag": 4.2, "place": "示例地点" },
      "geometry": { "type": "Point", "coordinates": [-122.5, 38.0, 8.0] }
    }
  ]
}

features 里放的是一个事件数组。路径 features[0].properties.mag 指向第一个事件的震级,索引 0 表示数组的第一个元素。坐标的顺序是"经度、纬度、深度(千米)"。智能体可以用震级决定点的大小,用坐标决定点的位置。如果数组是空的,就没有第一个元素;如果震级是 null,表示这个值未知,不代表震级为零。

剩下要决定的是拿到响应之后该显示什么:

拿到了什么该显示什么
成功响应,带事件数据当前这批数据。它可能跟上一次一样,因为不是每次请求都会出现新事件。
成功响应,但数组是空的说明选定的时间窗口里没有事件。旧的点可能已经超出这个窗口,不该再当作当前数据显示。
获取或解析响应出错提示出错信息。如果之前有成功获取的快照,可以保留它,并标注时间和"数据可能过期"的提示;如果连快照都没有,那就暂时没什么可显示的。

请求还在进行时,页面可以显示加载状态。测试各种情况时,用示例响应会更方便,不用干等服务真出故障或者真发生地震。

给 Claude Code 的任务现在可以这样写:

把指定 API 的数据接入页面。
先查官方文档:地址、方法、
响应格式和访问条件。说明会用到哪些字段。
处理好加载中、空列表和更新失败这几种情况。
如果因为出错而使用了旧数据,在页面上标注出来。
不要把密钥放进客户端代码。
检查结果,并说明验证过哪些状态。

完成之后,请亲自看看实际发出的请求和验证结果,不要只看最终效果图。还要分清楚智能体的访问和页面本身的访问是两回事:MCP 能把工具接入 Claude Code,但已发布的应用需要自己单独的集成。如果站点自己实现了这些请求,那么关闭智能体的会话不应该让它们停下来。