✍️ 필사 모드: API Schema Landscape 2026 — JSON Schema, OpenAPI 3.1, AsyncAPI, GraphQL, gRPC, Smithy, TypeSpec in One Map
EnglishPrologue — schemas now come before code
A 2026 backend meeting usually opens with one sentence: "Don't sketch the endpoint yet — show me the schema."
There was an era where you wrote the handler first and then auto-generated an OpenAPI document out of it. Spec-from-code — the code was truth, the spec was a byproduct. It felt easy. It also broke. Two teams guessed at each other's response shapes and produced month-long integration bugs, client SDKs were always one step behind, and the internal "current API" wiki page was half lies.
2026 inverted that. Spec-first — write the schema first, then generate types, docs, server stubs, client SDKs, test fixtures and AI tool definitions out of it. The truth lives in a schema file.
So this article maps it out. "API" looks like one word but the right tool changes with the protocol. REST uses OpenAPI 3.1. Events use AsyncAPI 3.0. Service-to-service uses Protobuf. Frontend-friendly graphs use GraphQL. The grammar underneath all of them is JSON Schema 2020-12. Then add AWS's Smithy, Microsoft's TypeSpec, the contract-testing tool Pact — many names, but the roles are crisp.
This piece is the 2026 schema atlas. The identity and strengths of each format, the same domain model rendered in four, the new role JSON Schema took on inside AI tool calling, and an honest decision frame for "what should my team actually use."
1. JSON Schema 2020-12 — the foundation of everything
One line first: JSON Schema is a standard for describing the shape of a JSON value in more JSON. "This field is an integer, this one is between 1 and 120, this one is an ISO 8601 date" — those constraints written as declarations.
Draft 2020-12 is the de-facto stable baseline. Work after it continues, but as of 2026 the version with the deepest compatibility and tooling is 2020-12. OpenAPI 3.1 aligned fully with this draft, and the tiny historic frictions (the old nullable/example differences that used to bite us) are gone.
Why JSON Schema matters in 2026: it shows up in three different jobs at once.
- REST APIs: the inside of OpenAPI 3.1's
components.schemasis literally JSON Schema 2020-12. - AI tool calling (function calling): the tool definition formats of OpenAI, Anthropic and Gemini are all JSON Schema subsets. The model returns
argumentsfilled in as JSON, and the host validates them against the same schema. - Config validation: the standard you most often see when validating a kubeconfig, a CI workflow, or an app config file.
JSON Schema is not a format, it is a grammar. Learn it once and you use it in all three places. It is arguably the single standard a backend engineer touches most often in 2026.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/user.json",
"type": "object",
"required": ["id", "email"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 130 },
"roles": {
"type": "array",
"items": { "type": "string", "enum": ["admin", "member", "guest"] },
"uniqueItems": true
}
},
"additionalProperties": false
}
This small file slots into OpenAPI, into an AI tool definition, into a standalone validator. The reusable unit is the truth.
2. OpenAPI 3.1 — the REST standard is settled, and only just starting
The headline difference between OpenAPI 3.0 and 3.1 is one line. 3.1 aligned fully with JSON Schema 2020-12. The 3.0-era nullable: true is gone — the standard is "type": ["string", "null"]. The semantics of example/examples are cleaned up. $ref got freer — it can point at any JSON Schema.
In 2026 OpenAPI 3.1 is the de-facto standard for REST API specs. The Swagger-era residue mostly washed out, and new projects almost universally start at 3.1. OpenAPI 3.2 is in motion (tidying smaller surfaces around forms, headers, multi-content responses), but 3.1 and 3.2 evolve without breaking each other. Starting at 3.1 today is safe.
openapi: 3.1.0
info:
title: Orders API
version: 1.0.0
paths:
/users/{userId}/orders:
get:
summary: List orders for a user
parameters:
- name: userId
in: path
required: true
schema: { type: string, format: uuid }
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Order'
components:
schemas:
Order:
type: object
required: [id, userId, total]
properties:
id: { type: string, format: uuid }
userId:{ type: string, format: uuid }
total: { type: number, minimum: 0 }
status:
type: string
enum: [pending, paid, shipped, cancelled]
What gets generated out of this single file: TypeScript/Go/Python client SDKs, server stubs, Postman/Bruno collections, interactive docs, mock servers, contract-test fixtures, AI tool definitions an agent can call. That is the 2026 workflow — one spec, every artifact.
Swagger UI still runs, but the new defaults are Stoplight Elements and Scalar. Both treat OpenAPI 3.1 as native, both render dark mode, search, "try it" and code samples far more cleanly. Scalar in particular spread fast because of its one-script-tag embed. Swagger UI is now an optional choice, not the default.
3. AsyncAPI 3.0 — the OpenAPI of events
REST has OpenAPI. So what about Kafka topics, MQTT channels, WebSocket streams, Server-Sent Events? The 2026 answer is crisp — AsyncAPI 3.0.
The loudest critique of AsyncAPI 2.x was the ambiguity of "channel equals topic equals operation bundle." Publish and subscribe were tangled inside the same channel definition. 3.0 separated those into explicit operations. A channel is the path the messages flow over, an operation is the act of sending or receiving. That single split alone made systems like Kafka much cleaner to model.
The core vocabulary of AsyncAPI 3.0:
- channels — the paths messages flow over (topic, queue, channel).
- operations — what happens on a channel (send, receive). Explicit.
- messages — the payload schemas (reusing JSON Schema directly).
- servers — Kafka brokers, MQTT brokers, WebSocket endpoints — protocol-specific bindings.
asyncapi: 3.0.0
info:
title: Orders Events
version: 1.0.0
servers:
kafka-prod:
host: kafka.prod.example.com:9092
protocol: kafka
channels:
orderCreated:
address: orders.created.v1
messages:
OrderCreated:
$ref: '#/components/messages/OrderCreated'
operations:
publishOrderCreated:
action: send
channel: { $ref: '#/channels/orderCreated' }
components:
messages:
OrderCreated:
payload:
type: object
required: [orderId, userId, createdAt]
properties:
orderId: { type: string, format: uuid }
userId: { type: string, format: uuid }
createdAt: { type: string, format: date-time }
total: { type: number, minimum: 0 }
Two reasons AsyncAPI 3.0 settled in 2026. First, event-driven systems are not a special case anymore — almost every microservices shop runs Kafka, NATS or MQTT. Second, the AsyncAPI ecosystem stopped imitating OpenAPI and grew its own tools — Studio, generator (server stubs, clients), validator, catalog. The usability gap mostly closed.
When REST and events coexist in the same system — which is normal in 2026 — the standard pattern is to keep OpenAPI and AsyncAPI side by side in one repo. Both stand on JSON Schema, so sharing message payload schemas across them is easy.
4. GraphQL SDL — the peace treaty with the frontend
GraphQL was published by Facebook in 2015, moved to the GraphQL Foundation in 2019, and as of 2026 still holds a strong niche. Especially where many clients need different shapes of data — BFFs, content platforms with several mobile apps, dashboards.
GraphQL schemas are written in their own DSL — SDL (Schema Definition Language). Unlike OpenAPI, AsyncAPI and JSON Schema, it is not JSON.
type User {
id: ID!
email: String!
age: Int
orders(status: OrderStatus, limit: Int = 20): [Order!]!
}
type Order {
id: ID!
total: Float!
status: OrderStatus!
createdAt: DateTime!
}
enum OrderStatus {
PENDING
PAID
SHIPPED
CANCELLED
}
type Query {
user(id: ID!): User
orders(userId: ID!, status: OrderStatus): [Order!]!
}
scalar DateTime
GraphQL's charm is introspection — the schema itself is queryable from the API. Client tools point at a live server, pull the schema, and codegen typed hooks. Apollo Studio, GraphiQL, GraphQL Code Generator, urql, Relay — the toolbox is thick.
2026 GraphQL trends:
- Federation v2 is settled — the standard for composing many services' GraphQLs into a single supergraph.
- Persisted queries became the security and performance default — clients ship a hash of a build-time-registered query, not arbitrary text.
- Coexistence with REST. The "rewrite everything in GraphQL" wave is over. A common pattern: GraphQL where the frontend churns, gRPC/REST between backends.
Be honest about the weaknesses. Caching is awkward — one URL means HTTP caches do not help much. N+1 must be solved with patterns like DataLoader. Without query cost analysis, a single client can knock the server over. And it does not fit every place — for a simple CRUD API, OpenAPI is almost always simpler.
5. gRPC plus Protobuf — still the default between services
Service-to-service in 2026? gRPC plus Protobuf is still the default. The reasons are simple — small, fast, polyglot, streaming is first-class.
Protobuf is a binary schema. Define messages and services in a .proto file, and protoc (or more often Buf) generates per-language code. On the wire it is far smaller than JSON (field names compress into integer tags) and far faster to parse.
syntax = "proto3";
package shop.v1;
import "google/protobuf/timestamp.proto";
message User {
string id = 1;
string email = 2;
int32 age = 3;
repeated string roles = 4;
}
message Order {
string id = 1;
string user_id = 2;
double total = 3;
OrderStatus status = 4;
google.protobuf.Timestamp created_at = 5;
}
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_PAID = 2;
ORDER_STATUS_SHIPPED = 3;
ORDER_STATUS_CANCELLED = 4;
}
service OrdersService {
rpc GetOrders(GetOrdersRequest) returns (GetOrdersResponse);
rpc StreamOrders(stream GetOrdersRequest) returns (stream Order);
}
message GetOrdersRequest { string user_id = 1; }
message GetOrdersResponse { repeated Order orders = 1; }
The 2026 center of gravity for the Protobuf ecosystem is Buf. buf build, buf lint, buf breaking, buf generate — the entire usability gap of raw protoc is closed. Buf Schema Registry plays the role of "GitHub for protobuf schemas" — versions, releases, dependencies.
When you want gRPC:
- Service-to-service — the internal mesh.
- Bidirectional streaming — chat, live data, game servers.
- High performance where it really matters — very high RPS, big payloads.
When you do not:
- Direct from the browser — gRPC-Web exists, but it is awkward. REST or GraphQL is friendlier.
- Public third-party APIs — too high a bar for outside developers.
- Debug by curl — the wire is binary, you cannot just
curl | jqit.
A notable trend: Connect (the protocol Buf authored) lets the same .proto expose both gRPC and REST/JSON. Internal mesh in gRPC, external clients hitting the same handlers in JSON — one codebase, two birds.
6. Same model, four formats — User-with-orders
Stop comparing abstractly and move the same domain across formats. A user owns an array of orders.
6.1 JSON Schema (standalone)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"Order": {
"type": "object",
"required": ["id", "total", "status"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"total": { "type": "number", "minimum": 0 },
"status": { "type": "string", "enum": ["pending","paid","shipped","cancelled"] }
}
}
},
"type": "object",
"required": ["id", "email", "orders"],
"properties": {
"id": { "type": "string", "format": "uuid" },
"email": { "type": "string", "format": "email" },
"orders":{ "type": "array", "items": { "$ref": "#/$defs/Order" } }
}
}
6.2 OpenAPI 3.1 (REST)
paths:
/users/{id}:
get:
parameters:
- { name: id, in: path, required: true, schema: { type: string, format: uuid } }
responses:
'200':
content:
application/json:
schema: { $ref: '#/components/schemas/UserWithOrders' }
components:
schemas:
UserWithOrders:
type: object
required: [id, email, orders]
properties:
id: { type: string, format: uuid }
email: { type: string, format: email }
orders:
type: array
items: { $ref: '#/components/schemas/Order' }
Order:
type: object
required: [id, total, status]
properties:
id: { type: string, format: uuid }
total: { type: number, minimum: 0 }
status: { type: string, enum: [pending, paid, shipped, cancelled] }
6.3 GraphQL SDL
type User {
id: ID!
email: String!
orders: [Order!]!
}
type Order {
id: ID!
total: Float!
status: OrderStatus!
}
enum OrderStatus { PENDING PAID SHIPPED CANCELLED }
type Query {
user(id: ID!): User
}
6.4 Protobuf
message User {
string id = 1;
string email = 2;
repeated Order orders = 3;
}
message Order {
string id = 1;
double total = 2;
OrderStatus status = 3;
}
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_PAID = 2;
ORDER_STATUS_SHIPPED = 3;
ORDER_STATUS_CANCELLED = 4;
}
Same model, four expressions. The information content is roughly equal, but the audience differs. JSON Schema is the shared grammar, OpenAPI is the HTTP surface, GraphQL is client-friendly, Protobuf is wire-efficient. Picking a tool is not about the shape of the data, it is about who you are talking to.
7. Smithy — AWS's API IDL
Smithy is the API definition language built by AWS. It started as an internal tool for generating AWS's own SDKs, but since the late 2020s it has been published outward and is actively evolving.
Smithy's philosophy is "protocol-agnostic model." You define a model once in .smithy files, then project it onto several protocols — REST (JSON over HTTP), AWS-specific protocols, gRPC. OpenAPI is an output.
$version: "2.0"
namespace shop
service Orders {
version: "2026-05-14"
operations: [GetUser]
}
@http(method: "GET", uri: "/users/{userId}")
operation GetUser {
input := {
@httpLabel
@required
userId: String
}
output := {
@required
id: String
@required
email: String
@required
orders: OrderList
}
}
list OrderList { member: Order }
structure Order {
@required
id: String
@required
total: Double
@required
status: OrderStatus
}
enum OrderStatus {
PENDING
PAID
SHIPPED
CANCELLED
}
Strengths of Smithy:
- A trait system more expressive than OpenAPI — auth, pagination, idempotency and other cross-cutting concerns expressed cleanly.
- All AWS SDKs (JavaScript, Python, Go, Rust and more) are generated from Smithy — industrial validation.
- One model, many outputs — OpenAPI, client SDKs, server stubs all out of one source.
Weakness: the ecosystem is narrower than OpenAPI's. Adoption outside AWS is growing, but it does not reach OpenAPI 3.1's universality. Unless your story really is "we are publishing SDKs across many languages," OpenAPI is often enough.
8. TypeSpec — Microsoft's authoring layer over OpenAPI
Writing OpenAPI 3.1 YAML by hand is — honestly — long, repetitive and easy to typo. Microsoft's answer to that pain is TypeSpec (formerly Cadl).
TypeSpec is a compact DSL (TypeScript-flavored) for authoring APIs, from which you generate OpenAPI (or JSON Schema, or Protobuf). It is a level above raw OpenAPI YAML on authorability, reuse and tooling.
import "@typespec/http";
using TypeSpec.Http;
@service({ title: "Orders API" })
namespace ShopApi;
model Order {
id: string;
total: float64;
status: OrderStatus;
}
enum OrderStatus { Pending, Paid, Shipped, Cancelled }
model User {
id: string;
email: string;
orders: Order[];
}
@route("/users/{userId}")
interface Users {
@get op getUser(@path userId: string): User;
}
Far shorter than the equivalent OpenAPI YAML. And it compiles — the TypeSpec compiler emits OpenAPI 3.1, JSON Schema 2020-12, Protobuf and client SDKs in one shot. Most new Azure services are authored in TypeSpec.
Use TypeSpec when:
- The API is big and complex — dozens of endpoints, many shared models and response patterns.
- You need multiple outputs from one definition — OpenAPI plus Protobuf plus clients.
- The team is fluent in TypeScript and the DSL feels native.
Avoid TypeSpec when:
- The API is small — a single OpenAPI YAML may simply be simpler.
- You want the toolchain to stay tiny — TypeSpec is another build step.
9. Pact — contract testing, the second use of schemas
Everything so far is documentation plus code generation. There is another strong use of schemas — consumer-driven contract testing. The flagship is Pact.
Setup: service A calls service B. If B reshapes its responses, A breaks. "Integration tests" mean spinning both up — slow and flaky. Pact takes a different route.
Consumer-driven contracts. A (the consumer) writes a contract file that says "I send B this request, I expect this shape back." The contract is uploaded to a Pact Broker. B (the provider) verifies in CI that its real code satisfies that contract. If B reshapes its response — and A depends on it — B's CI goes red immediately.
// consumer-side test
provider
.uponReceiving('a request for a user')
.withRequest({ method: 'GET', path: '/users/123' })
.willRespondWith({
status: 200,
body: { id: '123', email: 'a@b.com', orders: eachLike({ id: like('o1'), total: like(99.0) }) },
})
Pact does not compete with OpenAPI/AsyncAPI — it complements them. OpenAPI is the definition of what the API should be. Pact is the evidence of what a consumer actually depends on. More and more teams ship both.
10. AI tool calling — a second spring for JSON Schema
In 2024 and 2025, as LLMs started calling tools, JSON Schema had a second spring. The tool definition formats of OpenAI, Anthropic and Gemini are all JSON Schema subsets. The model returns arguments filled in as JSON, and the host validates.
Same User-with-orders domain, expressed as a tool that "fetches a user's orders":
{
"name": "get_user_orders",
"description": "Return all orders for the given user",
"input_schema": {
"type": "object",
"required": ["user_id"],
"properties": {
"user_id": { "type": "string", "format": "uuid" },
"status": {
"type": "string",
"enum": ["pending","paid","shipped","cancelled"]
},
"limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
},
"additionalProperties": false
}
}
Notice this is almost identical to a components.schemas entry inside OpenAPI. The clean 2026 workflow looks like this:
- Keep an OpenAPI 3.1 file as the source of truth.
- Generate server stubs and client SDKs from the same spec.
- Map the spec's
components.schemasto AI tool definitions — one handler exposed to both humans and models. - Validate the model's tool-call arguments against the same schema — wrong shape, immediate rejection.
"API" and "AI agent tool" sharing the same schema is the common reality of 2026. That is exactly why a solid grip on JSON Schema 2020-12 became a base skill for backend engineers.
11. Spec-first vs code-first — the debate that did not end
The OpenAPI/AsyncAPI era split into two camps.
Code-first. Write code (decorators, types) first, generate the spec from it. FastAPI, NestJS, tRPC are the flagships. Upside: a single source of truth — the code is the spec. Downside: the spec becomes a shadow of the code, slower for non-technical stakeholder review, and constrained by what the language expresses well.
Spec-first. Write the spec first, generate server stubs and clients out of it. OpenAPI YAML, TypeSpec, Smithy are the flagships. Upside: the protocol specification exists before the code — designers, QA and external clients see it together. Downside: truth can slip between two places — spec and handler drift.
The 2026 balance:
- External-facing APIs, APIs with many clients (languages, teams) → spec-first is almost always better.
- Internal single service, rapid prototyping, one fullstack team → code-first is faster.
- Hybrid is fine: start code-first, then freeze into spec-first once the spec stabilizes.
Tooling has narrowed the gap. High-level spec-first tools like TypeSpec and Smithy cut the authoring friction; flagship code-first tools like FastAPI tightened spec-conformance checks. Camp warfare is less interesting than asking "how long-lived is this API and how many clients will it have."
12. Toolchain — the 2026 standard stack
| Area | Near-standard tooling |
|---|---|
| OpenAPI doc UI | Scalar, Stoplight Elements (Swagger UI is legacy) |
| OpenAPI editor | Stoplight Studio, Insomnia, Postman |
| Client codegen | openapi-typescript, oapi-codegen, openapi-generator |
| Mock servers | Prism (Stoplight), Mockoon |
| Contract testing | Pact (Pact Broker, PactFlow) |
| Protobuf toolchain | Buf (lint, breaking-change, BSR), protoc (low level) |
| Protobuf to REST | Connect (Buf), gRPC-Gateway |
| AsyncAPI editor | AsyncAPI Studio |
| AsyncAPI codegen | AsyncAPI Generator |
| GraphQL stack | Apollo Studio, GraphiQL, GraphQL Code Generator, Hasura, Relay |
| TypeSpec | tsp CLI, VS Code extension |
| Smithy | smithy build, IntelliJ plugin |
The era of Swagger UI as the default is over. Stoplight Elements and Scalar both handle OpenAPI 3.1 cleanly, with much better dark mode, search, code samples and theming. On a new project, a single Scalar embed snippet is usually the entire docs page.
13. Decision frame — what should your team use
Same word "API," different audience, different answer.
Q1: Who is the audience?
├─ External developers (public API) → OpenAPI 3.1 + Scalar/Stoplight
├─ Internal frontend (many shapes needed) → GraphQL (Apollo Federation)
├─ Internal service-to-service → gRPC + Protobuf (Buf)
├─ Events (Kafka, MQTT, WebSocket) → AsyncAPI 3.0
└─ AI agent's tools → JSON Schema 2020-12 (reuse from OpenAPI)
Q2: How long-lived is the API?
├─ Short prototype → code-first (FastAPI etc.) for speed
└─ Long-lived, external → spec-first (OpenAPI/TypeSpec/Smithy) for solidity
Q3: Do you need multiple outputs from one definition?
├─ Yes (REST + gRPC + client SDKs) → TypeSpec or Smithy
└─ No → plain OpenAPI is enough
Q4: How afraid are you of breaking clients?
├─ Very → add Pact contract tests
└─ Modestly → spec plus codegen is enough
A few combinations to avoid in 2026:
- Starting a new project on OpenAPI 2.0 (Swagger 2) — it is a decade-old spec. Use 3.1.
- Exposing gRPC directly to the browser — go through gRPC-Web/Connect or a BFF.
- Calling
protocdirectly — Buf wins on almost every axis. - Treating Swagger UI as the new default — in 2026 it is Scalar or Stoplight Elements.
- Allowing arbitrary GraphQL queries from external clients — whitelist via persisted queries.
14. Migration — three common paths
A. OpenAPI 3.0 → 3.1
The most common migration. Compatibility is high, so most of it is mechanical:
nullable: true→"type": ["string", "null"](oranyOfwith null).example→examples(a single scalar can sometimes stay).- Audit tool-specific vendor extensions (
x-keys). - Add an OpenAPI 3.1 validator in CI.
B. Single OpenAPI → OpenAPI plus AsyncAPI split
In a repo where REST and events were tangled in one file, split events into AsyncAPI:
- Extract Kafka topics and messages into an AsyncAPI 3.0 file.
- Pull message payload schemas into shared JSON Schema files that both sides
$ref. - Keep
openapi.yaml,asyncapi.yamlandschemas/side by side in one repo.
C. Hand-written OpenAPI → TypeSpec/Smithy
When the API grows large and repetitive:
- Import the existing OpenAPI into TypeSpec/Smithy (official converters exist).
- Lift cross-cutting models and traits (auth, pagination, idempotency).
- Promote TypeSpec/Smithy to the source of truth, demote OpenAPI to a generated artifact.
All three are incremental, not big-bang. A common 2026 mistake is "let's redo all APIs at once" — it almost always fails. Move one area (one service, one topic), solidify the pattern, then expand.
15. Trust — making the spec actually hold
A schema is only truth if it is enforced. Common 2026 guardrails:
- CI lint for OpenAPI/AsyncAPI — Spectral and Redocly are standard. Style rules, banned patterns, required fields.
- Breaking-change detection — Buf's
buf breakingfor protobuf,oasdifffor OpenAPI. A spec-breaking PR turns red. - Validate real responses against the spec — pipe a slice of live traffic into a validator (Optic, prism --validate).
- Contract testing (Pact) — captures only the shapes a consumer actually depends on.
- Argument validation in AI tool calls — reject malformed model output and ask the model to retry.
You do not need all five, but you need one. "Having a spec" and "the spec being honored" are not the same job.
Epilogue — the schema is the truth
A one-line summary of 2026 backend engineering: the unit of an API is the schema, not the endpoint.
Out of one good schema file flow all of the following: docs, clients, server stubs, test fixtures, AI tool definitions. A good schema tells external developers "what exists right now," internal teams "what cannot break," and AI agents "how to call you."
There are many tools — JSON Schema, OpenAPI 3.1, AsyncAPI 3.0, GraphQL, Protobuf, Smithy, TypeSpec, Pact. It is not that you have more names to memorize; it is that the roles split apart. No single tool wins every situation. A good backend engineer picks the tool by the audience.
14-item checklist
- New projects start on OpenAPI 3.1 (not 2.0, not 3.0).
- REST (OpenAPI) and events (AsyncAPI) live side by side in the same repo.
- Message payload schemas live as shared JSON Schema files that both sides
$ref. - Docs are Scalar or Stoplight Elements, not Swagger UI.
- Spec lint (Spectral and friends) runs in CI for both OpenAPI and AsyncAPI.
- Breaking-change detection runs in CI (
buf breaking,oasdiff). - Client SDKs are generated from the spec.
- Server stubs and types are generated from the spec (no hand-coding).
- The Protobuf toolchain is Buf —
protocis not called by hand. - gRPC is not exposed directly to the browser (Connect/gRPC-Web/BFF instead).
- AI tool definitions reuse the same JSON Schema components as the HTTP API.
- AI tool-call arguments are validated against the schema before execution.
- Critical integrations have Pact (or equivalent contract testing).
- GraphQL exposes persisted queries, not arbitrary user queries.
Ten anti-patterns
- Maintaining code and spec both by hand — they always drift.
- GraphQL everywhere — for plain CRUD, OpenAPI is simpler.
- gRPC everywhere — friction is high for browsers and outside developers.
- Having an OpenAPI that is never validated — that is a wiki page, not a spec.
- Forcing events into OpenAPI — AsyncAPI 3.0 is the right answer.
- Starting fresh on Swagger 2 — it is a decade-old spec.
- Calling
protocdirectly — Buf wins everywhere. - Forcing Smithy when you are not shipping multi-language SDKs — ecosystem cost is too high.
- Keeping AI tool definitions separate from the OpenAPI components — reuse the same shapes.
- Big-bang migration of every API to spec-first at once — incremental or fail.
Next-post candidates
Candidates: Connect protocol deep dive — Buf's fusion of gRPC and REST, Building an internal event catalog with AsyncAPI 3.0, Designing AI tool-call schemas — making JSON Schema model-friendly.
"Not the endpoint, the schema. A good 2026 API is not hand-coded — it falls out of the schema."
— API schema atlas 2026, end.
참고 / References
- JSON Schema — draft 2020-12 specification
- JSON Schema — Getting Started
- OpenAPI Specification 3.1.0
- OpenAPI Initiative
- OpenAPI 3.2 work-in-progress
- AsyncAPI 3.0 specification
- AsyncAPI Initiative
- AsyncAPI 3.0 release notes
- GraphQL spec — October 2021
- GraphQL Foundation
- Apollo Federation v2 docs
- Protocol Buffers — google/protobuf
- gRPC project home
- Buf — Protobuf toolchain
- Buf Schema Registry (BSR)
- Connect protocol — Buf
- Smithy — AWS API IDL
- Smithy GitHub — smithy-lang/smithy
- TypeSpec — Microsoft
- TypeSpec GitHub — microsoft/typespec
- Pact docs
- PactFlow
- Stoplight Elements
- Scalar — API docs
- Redocly
- Spectral — OpenAPI/AsyncAPI linter
- oasdiff — OpenAPI diff
- OpenAI — function calling
- Anthropic — tool use
- Gemini — function calling
- openapi-typescript
- openapi-generator
현재 단락 (1/498)
A 2026 backend meeting usually opens with one sentence: "Don't sketch the endpoint yet — show me the...