Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Describe Your API with OpenAPI (PHP Tek 2026)

Describe Your API with OpenAPI (PHP Tek 2026)

No matter how big or small your API, no matter the audience, internal or external, you need to provide clear and precise documentation for your API. Since its inception as Swagger, OpenAPI has seen wide adoption as the de facto specification for describing APIs. Whether your API is yet to be built or has been around for a decade, you can use OpenAPI to document all the parts of your APIs.

In this talk, I’ll introduce you to the basic concepts of OpenAPI, we’ll use it to create documentation for a simple API, and I’ll leave you with a few tips, tricks, and gotchas I’ve learned along the way. You should be able to take what you’ve learned and immediately begin describing your APIs with OpenAPI.

Avatar for Ben Ramsey

Ben Ramsey PRO

May 20, 2026

More Decks by Ben Ramsey

Other Decks in Programming

Transcript

  1. Describe Your API with OpenAPI Ben Ramsey • PHP Tek

    • May 20, 2026 Austin Burleson / Unsplash
  2. You shipped an API. Someone needs to use it. “Where’s

    the documentation?” Josh Marty / Unsplash
  3. Well, there’s a Con fl uence page. It’s from two

    years ago. Half of it is wrong. Hans Vivek / Unsplash
  4. Or you’re the consumer. Guessing at fi eld names. Hoping

    the 400 response tells you something useful. Lee Cartledge / Unsplash
  5. What if your API had a contract? • Machine-readable •

    Human-readable • Single source of truth for docs, code generation, testing, and validation
  6. A brief history Year Event 2011 Swagger 1.0 (SmartBear Software)

    2014 Swagger 1.2 published; Swagger 2.0 follows 2015 SmartBear donates Swagger to the OpenAPI Initiative 2017 OpenAPI Speci fi cation 3.0.0 2021 OpenAPI Speci fi cation 3.1.0 2025 OpenAPI 3.1.2 and 3.2.0
  7. “Swagger” is not the spec. Swagger is a set of

    tools from SmartBear: • Swagger UI • Swagger Editor • Swagger Codegen The spec is OpenAPI. Heidi Kaden / Unsplash
  8. An OpenAPI Description (or OAD) is a text fi le.

    YAML or JSON. That’s it. Connor Scott McManus / Pexels
  9. YAML vs. JSON YAML JSON Hand- authoring Preferred Verbose Generated

    output Fine Fine Recommend fi lename openapi.yaml openapi.json 정규송 Nui MALAMA / Pexels
  10. An OpenAPI Description can live in one fi le or

    be split across many. References ($ref) connect them. Either way, it describes the same API. One File or Many Alexis Ricardo Alaurin / Pexels
  11. openapi: "3.1.2" info: # required .. . servers: - ...

    tags: - ... paths: # required (or webhooks/components) /sessions: ... components: .. .
  12. info: title: PHP Tek Conference API version: "2026-05" summary: Sessions,

    speakers, and schedule description: | Provides access to **session** schedules, **speaker** profiles, and **room** assignments. contact: name: PHP Tek Support url: https: // phptek.io email: [email protected]
  13. info: title: PHP Tek Conference API version: "2026-05" summary: Sessions,

    speakers, and schedule description: | Provides access to **session** schedules, **speaker** profiles, and **room** assignments. contact: name: PHP Tek Support url: https: // phptek.io email: [email protected]
  14. info: title: PHP Tek Conference API version: "2026-05" summary: Sessions,

    speakers, and schedule description: | Provides access to **session** schedules, **speaker** profiles, and **room** assignments. contact: name: PHP Tek Support url: https: // phptek.io email: [email protected]
  15. info: title: PHP Tek Conference API version: "2026-05" summary: Sessions,

    speakers, and schedule description: | Provides access to **session** schedules, **speaker** profiles, and **room** assignments. contact: name: PHP Tek Support url: https: // phptek.io email: [email protected]
  16. servers: - url: https: // api.tek.phparch.com/v1 description: Production - url:

    https: // staging.api.tek.phparch.com/v1 description: Staging - url: http: // localhost:8080/v1 description: Local development
  17. tags: - name: Sessions description: Conference sessions & schedule -

    name: Speakers description: Speaker profiles and bios
  18. paths: /sessions: get: summary: List all sessions operationId: listSessions tags:

    [Sessions] parameters: - name: day in: query description: Filter by day (1, 2, or 3) schema: type: integer maximum: 3 minimum: 1 responses: "200": description: A list of conference sessions content: application/json: schema: type: array items: $ref: "#/components/schemas/Session"
  19. parameters: - name: day in: query description: Filter by day

    (1, 2, or 3) schema: type: integer maximum: 3 minimum: 1
  20. responses: "200": description: A list of conference sessions content: application/json:

    schema: type: array items: $ref: "#/components/schemas/Session"
  21. Components are the reuse engine of OpenAPI. De fi ne

    once. Reference anywhere. $ref: "#/components/schemas/Session" components: schemas: Session: .. . Speaker: .. . responses: NotFound: . .. parameters: SessionId: ... securitySchemes: BearerAuth: ...
  22. In OpenAPI 3.1, the Schema Object is JSON Schema JSON

    Schema Draft 2020-12 Yu Ma / Pexels
  23. In 3.1, type can also be an array: type: [string,

    "null"] type: string type: integer type: number type: boolean type: array type: object type: "null" # quoted string — not YAML null
  24. type: integer format: int32 # signed 32-bit format: int64 #

    signed 64-bit type: string format: date # 2026-05-20 format: date-time # 2026-05-20T21 : 00 : 00Z format: uuid format: email format: uri format: password # hints to UIs to obscure the value
  25. components: schemas: Session: type: object required: [id, title, startsAt] properties:

    id: type: integer format: int64 title: type: string minLength: 1 maxLength: 200 abstract: type: string startsAt: type: string format: date-time room: type: string speaker: $ref: "#/components/schemas/Speaker"
  26. # 3.0 — nullable: true (DO NOT use in 3.1)

    type: string nullable: true # 3.1 — type is a JSON Schema keyword; use a type array type: [string, "null"] # Also valid in 3.1 oneOf: - type: string - type: "null"
  27. # 3.0 — nullable: true (DO NOT use in 3.1)

    type: string nullable: true # 3.1 — type is a JSON Schema keyword; use a type array type: [string, "null"] # Also valid in 3.1 oneOf: - type: string - type: "null"
  28. # 3.0 — nullable: true (DO NOT use in 3.1)

    type: string nullable: true # 3.1 — type is a JSON Schema keyword; use a type array type: [string, "null"] # Also valid in 3.1 oneOf: - type: string - type: "null"
  29. Composition Keyword Meaning allOf Valid against all listed schemas (intersection

    / extends) anyOf Valid against one or more listed schemas oneOf Valid against exactly one listed schema
  30. Talk: allOf: - $ref: "#/components/schemas/BaseSession" - type: object properties: type:

    type: string enum: [talk] slides: type: string format: uri
  31. Session: oneOf: - $ref: "#/components/schemas/Talk" - $ref: "#/components/schemas/Workshop" discriminator: propertyName:

    type mapping: talk: "#/components/schemas/Talk" workshop: "#/components/schemas/Workshop"
  32. Reuse # Reference within the same document $ref: "#/components/schemas/Session" #

    Reference an external file $ref: "./schemas/speaker.yaml" # External file with a specific fragment $ref: "./common.yaml#/components/schemas/Error"
  33. Reuse # Reference within the same document $ref: "#/components/schemas/Session" #

    Reference an external file $ref: "./schemas/speaker.yaml" # External file with a specific fragment $ref: "./common.yaml#/components/schemas/Error"
  34. Reuse # Reference within the same document $ref: "#/components/schemas/Session" #

    Reference an external file $ref: "./schemas/speaker.yaml" # External file with a specific fragment $ref: "./common.yaml#/components/schemas/Error"
  35. Reuse # Reference within the same document $ref: "#/components/schemas/Session" #

    Reference an external file $ref: "./schemas/speaker.yaml" # External file with a specific fragment $ref: "./common.yaml#/components/schemas/Error"
  36. $ref siblings # 3.0 — sibling fields were silently ignored

    $ref: "#/components/schemas/Session" description: "This was ignored in 3.0" # 3.1 — sibling fields are meaningful $ref: "#/components/schemas/Session" description: The session being registered for
  37. $ref siblings # 3.0 — sibling fields were silently ignored

    $ref: "#/components/schemas/Session" description: "This was ignored in 3.0" # 3.1 — sibling fields are meaningful $ref: "#/components/schemas/Session" description: The session being registered for
  38. $ref siblings # 3.0 — sibling fields were silently ignored

    $ref: "#/components/schemas/Session" description: "This was ignored in 3.0" # 3.1 — sibling fields are meaningful $ref: "#/components/schemas/Session" description: The session being registered for
  39. Conference API GET /sessions list all sessions GET /sessions/{id} get

    a single session GET /speakers list speakers PHP Tek Tanya Barrow / Unsplash
  40. openapi: "3.1.2" info: title: PHP Tek Conference API version: "2026-05"

    servers: - url: https: // api.tek.phparch.com/ description: Production - url: http: // localhost:8080/ description: Local development Step 1 Getting started
  41. paths: /sessions: get: summary: List all sessions operationId: listSessions tags:

    [Sessions] parameters: - name: day in: query description: Filter by day (1, 2, or 3) schema: type: integer maximum: 3 minimum: 1 responses: "200": description: A list of sessions content: application/json: schema: type: array items: $ref: "#/components/schemas/Session" Step 2 Add the first path
  42. components: schemas: Session: type: object required: [id, title, startsAt, endsAt]

    properties: id: type: integer format: int64 title: type: string abstract: type: string startsAt: type: string format: date-time endsAt: type: string format: date-time room: type: string speaker: $ref: "#/components/schemas/Speaker" Speaker: type: object required: [id, name] properties: id: type: integer format: int64 name: type: string bio: type: [string, "null"] Step 3 Define schemas
  43. Session: type: object required: [id, title, startsAt, endsAt] properties: id:

    type: integer format: int64 title: type: string abstract: type: string startsAt: type: string format: date-time endsAt: type: string format: date-time room: type: string speaker: $ref: "#/components/schemas/Speaker"
  44. properties: id: type: integer format: int64 .. . startsAt: type:

    string format: date-time endsAt: type: string format: date-time .. .
  45. Speaker: type: object required: [id, name] properties: id: type: integer

    format: int64 name: type: string bio: type: [string, "null"]
  46. paths: /sessions/{id}: get: summary: Get a session by ID operationId:

    getSession tags: [Sessions] parameters: - name: id in: path required: true schema: type: integer format: int64 responses: "200": description: The session content: application/json: schema: $ref: "#/components/schemas/Session" "404": $ref: "#/components/responses/NotFound" Step 4 Second path with params
  47. components: responses: NotFound: description: Resource not found content: application/problem+json: schema:

    $ref: "#/components/schemas/Problem" schemas: Problem: type: object properties: type: type: string format: uri title: type: string status: type: integer detail: type: string Step 5 Reusable errors
  48. schemas: Problem: type: object properties: type: type: string format: uri

    title: type: string status: type: integer detail: type: string
  49. components: securitySchemes: ApiKeyAuth: type: apiKey in: header name: Api-Key BearerAuth:

    type: http scheme: bearer bearerFormat: JWT OAuth2: type: oauth2 flows: authorizationCode: authorizationUrl: https: // example.com/oauth/authorize tokenUrl: https: // example.com/oauth/token scopes: read:sessions: Read session data write:sessions: Register for sessions OpenIDConnect: type: openIdConnect openIdConnectUrl: https: // example.com/.well-known/openid-configuration Security Schemes
  50. security: - BearerAuth: [] Global default Per-operation override paths: /sessions:

    get: security: [] # no auth, public /sessions/{id}/register: post: security: - BearerAuth: [] - OAuth2: - write:sessions
  51. webhooks: sessionRegistered: post: summary: Fired when an attendee registers for

    a session requestBody: content: application/json: schema: $ref: "#/components/schemas/RegistrationEvent" responses: "202": description: Acknowledge receipt Webhooks New in 3.1
  52. # WRONG — YAML parses 3.1.2 as a float openapi:

    3.1.2 # Correct openapi: "3.1.2" 1. openapi must be a quoted string
  53. 2. nullable: true is not in 3.1 # 3.0 —

    nullable: true (DO NOT use in 3.1) type: string nullable: true # 3.1 — type is an array type: [string, "null"] # Also valid in 3.1 oneOf: - type: string - type: "null"
  54. responses: 200: # WRONG — integer key description: OK "200":

    # Correct description: OK "2XX": # Also valid — range wildcard description: Any 2xx success 3. Status codes must be strings
  55. /sessions/{id}: get: parameters: - name: id in: path required: true

    # mandatory for path params schema: type: integer format: int64 5. Path parameters must be declared Every {variable} in the path must have a matching parameter entry: Missing the entry or missing required: true is a validation error.
  56. 6. $ref siblings now work 3.0: siblings next to $ref

    were silently ignored. 3.1: siblings are meaningful — they override or extend the referenced schema. $ref: "#/components/schemas/Session" description: The session being registered for
  57. 7. additionalProperties: false and allOf # Problematic — blocks sub-schemas

    from adding properties BaseSession: type: object properties: id: type: integer additionalProperties: false # rejects Talk's extra properties # Better — JSON Schema 2020-12 aware BaseSession: type: object properties: id: type: integer unevaluatedProperties: false # respects allOf sub-schemas
  58. e.g., format: email does not guarantee email validation type: string

    format: email # annotation, documents intent # validation depends on tool 8. format is an annotation, not validation
  59. # 3.0 type: string format: binary # raw binary file

    upload/download type: string format: byte # base64-encoded binary # 3.1 replacements contentMediaType: image/png # raw binary type: string contentMediaType: image/png contentEncoding: base64 # base64-encoded 9. binary and byte formats are gone
  60. components: schemas: Session: type: object properties: id: type: integer relatedSessions:

    type: array items: $ref: "#/components/schemas/Session" 10. Recursion works
  61. • Swagger Editor • Stoplight Platform • OpenAPI Editor by

    42Crunch
 for VS Code • PhpStorm / IntelliJ Editors & design tools Albert Stoynov / Unsplash
  62. • zircote/swagger-php • darkaonline/l5-swagger • dedoc/scramble • API Platform Code-first

    (generate spec from code) PHP Libraries Tanya Barrow / Unsplash
  63. Description-first vs. code-first Description- fi rst Code- fi rst Work

    fl ow Write spec then implement Implement then generate spec Best for New APIs, contract-driven dev Existing APIs, keeping spec in sync Strength API can be reviewed & mocked before any code Spec stays close to implementation Risk Spec and code can diverge without discipline Spec re fl ects code, not design intent
  64. OpenAPI 3.2.0 was published on September 19, 2025 • the

    same day as 3.1.2 3.2.0 is the recommended version going forward But tooling hasn’t fully caught up 3.1.2 is what works reliably today Why 3.1.2? wr heustis / Pexels
  65. IETF standards process May 12, 2026: IETF published draft-ietf-jsonschema-json- schema-00

    Merges core and validation specs into a single document OpenAPI is built directly on JSON Schema Strong foundation for the long term JSON Schema Tolga Ahmetler / Pexels
  66. • spec.openapis.org
 the speci fi cation • learn.openapis.org
 tutorials and

    guides • openapi.tools
 community tool directory • json-schema.org
 JSON Schema documentation Where to go from here Tolga Ahmetler / Pexels
  67. This work is licensed under Creative Commons Attribution-ShareAlike 4.0 International.

    For uses not covered under this license, please contact the author. Describe Your API With OpenAPI Copyright © 2026 Ben Ramsey Thank you! Keep in touch      ben.ramsey.dev phpc.social/@ramsey github.com/ramsey www.linkedin.com/in/benramsey [email protected] bram.se/phptek-openapi      Ramsey, Ben. “Describe Your API With OpenAPI.” PHP Tek Conference, 20 May 2026, Sheraton Suites Chicago O’Hare, Rosemont, IL.