OpenAPI 101 — มารู้จัก OpenAPI ผู้ช่วยสร้าง REST API กันเถอะ

Icegotcha's avatar
OpenAPI 101 — มารู้จัก OpenAPI ผู้ช่วยสร้าง REST API กันเถอะ

ตอนนี้ Software ไม่ใช่โปรแกรมที่มีชุดคำสั่งรวมกันเป็นก้อนเดียวก้อนใหญ่อีกต่อไป แต่ประกอบด้วย Service ตัวเล็ก ๆ หลายตัวเชื่อมต่อกันเรียกว่า Microservices ซึ่งอาจจะเป็นของเราเอง หรือเป็นของคนอื่น (Third Party) ก็ได้ การเชื่อมต่อกันของ Service ทุกตัวจะต้องทำผ่าน Protocol สักตัวหนึ่ง ในปัจจุบัน Service จำนวนมากจะเป็น REST API เนื่องจาก REST API นั้นมีวิธีการรับส่งข้อมูลที่ทำได้ง่ายและสะดวกที่สุด

แต่การพัฒนา Service ที่เป็น REST API นั้นกลับเป็นเรื่องน่าปวดหัว ทั้งนี้เนื่องจากการเชื่อมต่อกันไปมาของ Service เป็นการเพิ่มความซับซ้อนให้กับระบบ เราจะต้องมีข้อมูลเกี่ยวกับการเชื่อมต่อของแต่ละ Service ที่ทำให้ทุกคนที่เกี่ยวข้อง เช่น นักออกแบบระบบ นักพัฒนาระบบ นักทดสอบระบบ Stakeholders ลูกค้า คนภายนอกคนอื่น ๆ ที่เข้ามาใช้งานระบบของเรา หรือแม้แต่เราในอนาคตเข้าใจ เพื่อให้การพัฒนาเป็นไปอย่างราบรื่น หากมีการแก้ไข Service เราต้องตามไปแก้ข้อมูลอยู่เสมอ แต่บางครั้งอาจมีเกิดเหตุที่ทำให้เราไม่ได้ทำข้อมูลและเอกสารต่าง ๆ เก็บไว้ หรือทำแล้วแต่ข้อมูลอาจตกหล่นไปไม่ครบถ้วน ทำให้ความยุ่งยากและปัญหาตามมามากมาย

ในบางครั้งเราต้องพัฒนา SDK และ Software Development Kit เพื่อทำให้คนอื่นเข้าใช้งาน REST API ของเราง่ายที่สุด แน่นอนว่าทำส่วนนี้เป็นการเพิ่ม Cost ยิ่งต้องทำ SDK หลายภาษาโปรแกรมก็ยิ่งเพิ่ม Cost เข้าไปอีก อาจจะเพิ่มอย่างไม่จำเป็นด้วย อย่างเช่น นั่งแปลงโค้ดจากภาษาหนึ่งไปเป็นอีกภาษาหนึ่ง เป็นต้น

ในบทความนี้ จะกล่าวถึงตัวที่ช่วยลดปัญหาที่ว่ามานี้ให้กับเรา ตัวนี้มีชื่อว่า OpenAPI

OpenAPI คืออะไร?

OpenAPI ที่พูดถึงกันมักหมายถึง OpenAPI Specification เป็นมาตรฐานในการอธิบาย REST API ตัวหนึ่ง ที่ให้ทั้งคนและเครื่องคอมพิวเตอร์อ่านได้ เมื่อเราเขียน API Specification ตามมาตรฐานนี้ เราสามารถใช้มันเป็นตัวกลางในการสื่อสารกับคนในทีม เช่น Developer, Tester และลูกค้า รวมถึงใช้เป็นตัวช่วยสร้าง พัฒนา ปรับปรุง REST API ให้ตรงกับสิ่งที่เราเขียนไว้เสมอ

ประวัติของ OpenAPI

OpenAPI Specification มีชื่อเดิมคือ Swagger Specification ถูกพัฒนาในปี 2011 โดย Tony Tam เป็น Co-founder ของบริษัททำระบบดิกชันนารีออนไลน์ Wordnik เขาได้พัฒนาพร้อมกับ Swagger UI ซึ่งเป็นโปรแกรมช่วยสร้างเอกสารจาก Specification ตัวนี้ เพื่อลดปัญหาความยุ่งยากในการสร้างเอกสารและ SDK (Software Development Kit) ระหว่างการพัฒนา Wordnik อยู่นั่นเอง

หลังจากทำเสร็จและปล่อยออกมาเป็น Open-source ก็มีการใช้มากขึ้นอย่างกว้างขวาง เพราะในตอนนั้น Swagger เป็นโปรเจคแรกที่ออกแบบกลไกให้เครื่องคอมพิวเตอร์สามารถประมวลผล Specification ของ REST API ได้

ต่อมาในปี 2015 SmartBear Software ก็ได้เข้ามาให้ความสนใจและสนับสนุนโครงการ Swagger ก่อนที่จะร่วมมือกับ Linux Foundation ตั้งบริษัทแยกขึ้นมาเพื่อพัฒนา Swagger Specification โดยเฉพาะ ชื่อว่า OpenAPI Initiative (OAI)

Swagger Specification ก็ได้ถูกตั้งชื่อใหม่เป็น OpenAPI Specification ในปี 2016 และก็มีเวอร์ชัน 3.0.0 ออกมาในปี 2017 ตั้งแต่ตอนนั้น OpenAPI Specification เวอร์ชัน 3 ก็ได้รับพัฒนาเรื่อย ๆ จนถึงปัจจุบัน

โครงสร้างของ OpenAPI Specification

ในปัจจุบัน OpenAPI Specification มี 2 เวอร์ชันใหญ่ ๆ คือ Swagger 2.0 และ OpenAPI 3.x

Swagger 2.0 เป็นเวอร์ชันดั้งเดิมของ OpenAPI ถูกสร้างตั้งแต่ปี 2014 และนิยมใช้กันจนถึงปัจจุบัน เวอร์ชัน 2.0 มีโครงสร้างสรุปได้ดังภาพนี้

ภาพแสดงโครงสร้างของ Swagger 2.0

OpenAPI 3.x เป็นเวอร์ชันที่เกิดขึ้นมาในปี 2017 โครงสร้างในเวอร์ชันนี้จะแตกต่างจาก Swagger 2.0 อย่างสิ้นเชิง ถ้าเรานำ 2 เวอร์ชันนี้เปรียบเทียบกัน จะเห็นว่า OpenAPI 3.x มีโครงสร้างที่เรียบง่ายกว่า เพราะมีการรวมข้อมูลบางส่วนเป็นข้อมูลก้อนใหญ่ก้อนเดียว ทำให้ Specification มีความยืดหยุ่น พัฒนาปรับปรุงได้ง่าย และทำความเข้าใจได้ง่ายกว่า รองรับการทำงานกับ API ทั้งในปัจจุบันและอนาคต

ภาพแสดงโครงสร้างของ OpenAPI 3.0

ในหัวข้อนี้จะเจาะลึกโครงสร้างของ OpenAPI เวอร์ชัน 3 กัน จากภาพโครงสร้างจะเห็นได้ว่า เราสามารถแบ่งเป็น 7 ส่วนดังนี้

1. openapi

ส่วนนี้แสดงเวอร์ชันของ OpenAPI ที่ใช้เขียน Specification ชุดนี้ จะต้องเขียนในรูปแบบ Sematic versioning ที่เป็นเลขเวอร์ชันมีตัวเลขสามส่วน เวอร์ชันของ OpenAPI ที่ใช้เขียนได้นั้นสามารถดูได้ที่หน้า Release ของ OpenAPI

openapi: 3.0.0

2. info

ส่วนนี้แสดงข้อมูลทั่วไปของ REST API

info:
  description: >-
    This is a sample server Petstore server. For this sample, you can use the api key
    `special-key` to test the authorization filters.
  version: 1.0.0
  title: OpenAPI Petstore
  license:
    name: Apache-2.0
    url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
  termsOfService: http://example.com/terms/
  contact:
    name: API Support
    url: http://www.example.com/support
    email: [email protected]
  • title — ชื่อเรียกของ REST API
  • description — รายละเอียดเกี่ยวกับ REST API สามารถเขียนหลายบรรทัดได้
  • version — เวอร์ชันของ REST API ปัจจุบัน คนละอย่างกับส่วน openapi ตรงนี้สามารถเขียนเป็น Sematic versioning, String ลงท้ายด้วย -alpha หรือ -beta (1.0-beta), หรือวันที่ (2020-05-21)
  • license — ชื่อของ License ที่ใช้และลิงก์ที่สามารถเข้าไปดูรายละเอียดของ License นั้น
  • contact — ข้อมูลด้านการความช่วยเหลือหรือ Support ข้อมูลของ REST API
  • termsOfService — ลิงก์ URL ที่ไปยังหน้า Terms of Service

3. servers

ส่วนนี้แสดงข้อมูลเกี่ยวกับ Server ที่ตั้งของ API หรือ Base path

servers:
  - url: http://api.example.com/v1
    description: Main server
  - url: http://staging-api.example.com
    description: Internal staging server for testing

4. paths

ส่วนนี้คือส่วนใหญ่ที่สุด เป็นส่วนที่แสดง Endpoint (หรือ Path) ต่าง ๆ ของ API รวมถึง Operation ที่สามารถใช้กับ Endpoint นั้นได ้(ในที่นี้ก็คือ HTTP verb ต่าง ๆ เช่น Get, Post, Put, Delete)

paths:
  '/pet/{petId}':
    get:
      tags:
        - pet
      summary: Find pet by ID
      description: Returns a single pet
      operationId: getPetById
      parameters:
        - name: petId
          in: path
          description: ID of pet to return
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: successful operation
          content:
            application/xml:
              schema:
                $ref: '#/components/schemas/Pet'
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
        '400':
          description: Invalid ID supplied
        '404':
          description: Pet not found
      security:
        - api_key: []
    post:
      tags:
        - pet
      summary: Updates a pet in the store with form data
      description: ''
      operationId: updatePetWithForm
      parameters:
        - name: petId
          in: path
          description: ID of pet that needs to be updated
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '405':
          description: Invalid input
      security:
        - petstore_auth:
            - 'write:pets'
            - 'read:pets'
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                name:
                  description: Updated name of the pet
                  type: string
                status:
                  description: Updated status of the pet
                  type: string
    delete:
      tags:
        - pet
      summary: Deletes a pet
      description: ''
      operationId: deletePet
      parameters:
        - name: api_key
          in: header
          required: false
          schema:
            type: string
        - name: petId
          in: path
          description: Pet id to delete
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '400':
          description: Invalid pet value
      security:
        - petstore_auth:
            - 'write:pets'
            - 'read:pets'

จากตัวอย่าง เป็นการแสดงการเขียน Path หนึ่งตัวภายในส่วน paths นี้ชื่อ pet/{petId} ซึ่งมี Operation อยู่สามตัวคือ get, post และ delete แต่ละ Operation ก็มีข้อมูลรายละเอียดอยู่ภายในอีก ซึ่งมีรายละเอียดดังนี้

  • tags — รายชื่อกลุ่มที่ Operation จะอยู่ สามารถมีได้หลาย Tag
  • summary — ใช้แนะนำว่า Operation นี้คืออะไร? ทำงานอะไร?
  • description — บอกรายละเอียดเพิ่มเติมเกี่ยวกับ Operation นี้ เช่น การทำงาน การส่งค่าเข้ามา เป็นต้น สามารถพิมพ์ได้หลายบรรทัดได้
  • operationId — Identity ของ Operation นี้ ตรงส่วนนี้ต้องมีค่าไม่ซ้ำกันระหว่าง Operation ด้วยกัน มักจะตั้งชื่อตรงกับชื่อฟังก์ชันที่ใช้ใน Code เพื่อบอกให้รู้ว่า Operation ทำงานตามฟังก์ชันใด
  • security — ลักษณะของ Authentication ที่ต้องใช้ใน Operation นี้
security:
  - petstore_auth:
      - 'write:pets'
      - 'read:pets'

หากเราต้องการใช้ Authentication เช่น OAuth2, Basic Token ใน Operation หนึ่ง เราสามารถเพิ่มส่วน security พร้อมสิทธิ์เข้าถึงที่เราต้องการให้ผู้ใช้ต้องมีก่อนเข้าใช้งาน ดังตัวอย่างด้านบนนี้ แต่ก่อนที่เราจะเพิ่มที่นี่ เราต้องกำหนดชนิดและกระบวนการ (Flow) ของ Authentication ใน securitySchemes ซึ่งอยู่ภายในส่วนที่เรียกว่า components (มีพูดถึงในส่วนสุดท้าย) ดังตัวอย่างด้านล่าง

components:
    securitySchemes:
        petstore_auth:
           type: oauth2
           flows:
             implicit:
                authorizationUrl: 'http://petstore.com/api/oauth'
                scopes:
                   'write:pets': modify pets in your account
                    'read:pets': read your pets
  • requestBody —นี้มีไว้สำหรับกำหนดรายละเอียดของข้อมูลที่ต้องส่งมาให้ Operation ทาง Request Body ซึ่งมักจะใช้ใน Operation ประเภท POST, PUT, PATCH ในส่วนนี้เราต้องกำหนด Media Types และโครงสร้างของข้อมูลที่รับในแต่ละ Media Type ใน content ซึ่ง Media Type กำหนดได้โดยอิงตามรายการนี้ (เช่น application/x-www-form-urlencoded, application/json, multipart/form-data เป็นต้น) ส่วนโครงสร้างข้อมูลหรือ schema นั้น เราสามารถสร้างขึ้นมาโดยตรง หรือจะใช้ $ref อ้างอิงถึง Schema ที่เรากำหนดไว้ในส่วน component

ส่วนของ Schema นั้น เป็นส่วนที่สำคัญมากในการเขียน Specification ใช้กำหนดโครงสร้างข้อมูลทั้งใน Request Body, Parameter, Response, Header และภายในตัว Schema ด้วยกันเอง สามารถดูการเขียนได้ที่ลิงก์นี้

ตัวอย่างการกำหนด Schema โดยตรง

requestBody:
  content:
    application/x-www-form-urlencoded:
      schema:
        type: object
        properties:
          name:
            description: Updated name of the pet
            type: string
          status:
            description: Updated status of the pet
            type: string

ตัวอย่างการกำหนด Schema โดยใช้ $ref

requestBody:
  description: Pet object that needs to be added to the store
  required: true
  content:
    application/json:
      schema:
        $ref: '#/components/schemas/Pet'
    application/xml:
      schema:
        $ref: '#/components/schemas/Pet'
  • parameters — มีไว้สำหรับกำหนดรายละเอียดของข้อมูลที่ต้องส่งมาให้ Operation ทาง Query, Path, Header, และ Cookie
parameters:
  - name: petId
    in: path
    description: ID of pet to return
    required: true
    schema:
      type: integer
      format: int64
  • responses — มีไว้กำหนดรายละเอียดของข้อมูลที่ Operation จะส่งกลับเมื่อทำงานเสร็จแล้ว เริ่มต้นด้วยเขียน HTTP Status Code และลักษณะโครงสร้างข้อมูลหรือ Response Body ให้ในแต่ละ HTTP Status Code ซึ่ง Response Body จะเขียนเป็น Schema เช่นเดียวกับ Request Body แต่จะไม่เขียนเลยก็ได้ หากไม่เขียนจะอ่านได้หลายความหมาย คือ Operation จะไม่ส่ง Response Body มาให้ หรือ Operation ส่งเป็นข้อความตามที่เขียนใน description
responses:
   '200':
     description: successful operation
     content:
       application/xml:
         schema:
           $ref: '#/components/schemas/Pet'
       application/json:
         schema:
           $ref: '#/components/schemas/Pet'
    '400':
      description: Invalid ID supplied
    '404':
      description: Pet not found
    '405':
      description: Validation exception

5. tags

เป็นส่วนที่รวบรวมชื่อที่ใช้จัดกลุ่มของ Operation เครื่องมือบางตัวสามารถจัดการแบ่งกลุ่มให้เราเห็นอย่างชัดเจน หากเรานำ Specification ไปใช้ในเครื่องมือนั้น เช่น Swagger UI

Swagger UI ใช้ส่วนนี้ในการแสดง Operation เป็นกลุ่มอย่างชัดเจน ดังรูปด้านล่าง โดยการจัดกลุ่มมีผลเมื่อมี Operation ที่มีข้อมูล tags อยู่เท่านั้น หาก Operation ใดไม่มี tags ก็จะอยู่กลุ่ม default แทน

tags:
  - name: pet
    description: Everything about your Pets
  - name: store
    description: Access to Petstore orders
  - name: user
    description: Operations about user

รูปแสดงการจัดกลุ่มโดยใช้ Tags ของ Swagger UI

6. externalDocs

ส่วนนี้เป็นส่วนที่แสดง URL ของเว็บไซต์ที่มีข้อมูลนอกเหนือจาก Specification

externalDocs:
  description: Find out more about Swagger
  url: 'http://swagger.io'

7. components

เป็นส่วนที่รวบรวมโครงสร้างต่าง ๆ เพื่อให้เราสามารถนำเฉพาะชื่อไปใช้ในส่วนอื่น ๆ ใน Specification ผ่าน $ref โดยโครงสร้างที่เขียนในส่วนนี้ได้นั้น มีดังต่อไปนี้

  • Schema Object (เก็บผ่าน schemas)
  • Request Body (เก็บผ่าน requestBodies)
  • Response Body (เก็บผ่าน responses)
  • Parameter (เก็บผ่าน parameters)
  • Header Object (เก็บผ่าน headers)
  • Example Object (เก็บผ่าน examples)
  • Authentication (เก็บผ่าน securitySchemes )
components:
  #-------------------------------
  # Reusable schemas (data models)
  #-------------------------------
  schemas:
    User: # Can be referenced as '#/components/schemas/User'
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
    Error: # Can be referenced as '#/components/schemas/Error'
      type: object
      properties:
        code:
          type: integer
        message:
          type: string
  #-------------------------------
  # Reusable operation parameters
  #-------------------------------
  parameters:
    offsetParam: # Can be referenced via '#/components/parameters/offsetParam'
      name: offset
      in: query
      description: Number of items to skip before returning the results.
      required: false
      schema:
        type: integer
        format: int32
        minimum: 0
        default: 0
    limitParam: # Can be referenced as '#/components/parameters/limitParam'
      name: limit
      in: query
      description: Maximum number of items to return.
      required: false
      schema:
        type: integer
        format: int32
        minimum: 1
        maximum: 100
        default: 20
  #-------------------------------
  # Reusable responses
  #-------------------------------
  responses:
    404NotFound: # Can be referenced as '#/components/responses/404NotFound'
      description: The specified resource was not found.
    ImageResponse: # Can be referenced as '#/components/responses/ImageResponse'
      description: An image.
      content:
        image/*:
          schema:
            type: string
            format: binary
    GenericError: # Can be referenced as '#/components/responses/GenericError'
      description: An error occurred.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

จากรายละเอียดข้างต้น สรุปได้ว่า ข้อมูลที่เราสามารถเขียนใน OpenAPI Specification จะมีดังนี้

  • ข้อมูลเบื้องต้นเกี่ยวกับ REST API ของเรา เช่น เวอร์ชันปัจจุบัน ช่องทางการติดต่อ (Support) ลิขสิทธิ์ (License) เงื่อนไขการใช้งาน (Term of use) ข้อมูลของ Server ที่ใช้ในแต่ละ Environment เป็นต้น
  • วิธีการพิสูจน์ตัวตน (Authentication) ในการเข้าใช้งาน API
  • Endpoint ต่าง ๆ และ Operation ของแต่ละ Endpoint (HTTP Verb ต่าง ๆ เช่น GET, POST, PUT, DELETE เป็นต้น)
  • ข้อมูลนำเข้า (Path Parameters, Queries, Request Bodies) และข้อมูลส่งออก (Response) ของแต่ละ Operation

ยังมีรายละเอียดอยู่เยอะมาก หากต้องการรู้วิธีการเขียนเพิ่มเติม ก็สามารถเข้าไปที่ลิงก์เหล่านี้ได้ค่ะ

และสามารถดูตัวอย่าง openAPI Specification แบบเต็ม ๆ ได้ที่ลิงก์นี้

การเขียน OpenAPI Specification

เราสามารถเริ่มเขียน OpenAPI Specification ตั้งแต่ตอนที่ยังไม่สร้าง REST API ซึ่งจะทำให้เราสามารถสร้าง API ตามแนวคิด API Design-First Approach วิธีนี้จะช่วยให้ทั้งคนในทีม และลูกค้าเห็นภาพของ API ที่สมบูรณ์ก่อนลงมือเขียนโค้ด เมื่อเขียนเสร็จแล้ว เราสามารถใช้ Specification สร้าง API ปลอม (Mock API) และเอกสาร รวมไปถึงโค้ดบางส่วนอัตโนมัติ ทำให้คนในทีมทำงานสะดวกขึ้น สามารถทำงานเป็น Parallel ได้ ซึ่งส่งผลให้การสร้าง API เป็นไปอย่างรวดเร็วและมีประสิทธิภาพมากยิ่งขึ้น

ภาพกระบวนการพัฒนาโปรแกรมแบบ API Design-first Approach

เราสามารถเขียน OpenAPI Specification ใน Text Editor ทั่วไปที่เราใช้งานอยู่ หรือจะใช้ Swagger Editor ก็ได้ เป็น Text Editor ตัวหนึ่งที่ถูกสร้างขึ้นเพื่อทำ OpenAPI Specification โดยเฉพาะ โปรแกรมนี้จะแบ่งหน้าต่างออกเป็นสองส่วน ส่วนด้านซ้ายคือส่วนที่ให้เราเขียน OpenAPI Specification กับอีกส่วนหนึ่งคือ ส่วนด้านขวาเป็น Live Preview เอกสารจากที่เราเขียนในขณะนั้น

รูปแสดง Visual Studio Code ที่ลง Extension ชื่อว่า OpenAPI (Swagger) Editor และ Swagger Viewer ไว้ ซึ่งช่วยให้เราสามารถจัดการกับข้อมูลส่วนต่าง ๆ และดูเอกสารที่สร้างจาก Specification ตามลำดับ เมื่อเราเปิดทำงานกับไฟล์ OpenAPI Specification ไฟล์หนึ่ง

รูปแสดงหน้าตาของ OpenAPI Editor ชื่อว่า Swagger Editor

หากไม่ต้องการสร้าง Specification ด้วยการเขียนด้วยมือ เราก็ใช้เครื่องมือพวก GUI Editor สร้างขึ้นได้เช่นกัน เครื่องมือประเภทนี้ทำให้เราสามารถสร้าง Specification โดยไม่จำเป็นต้องเรียนรู้วิธีการเขียนมากนัก บางตัวก็มีเครื่องมือเล็ก ๆ ที่ช่วยอำนวยความสะดวกมาให้อีกด้วย อย่างเช่น ตัวช่วยตรวจความถูกต้องของ Specification, ตัวช่วยสร้าง Mock API, ตัวช่วยสร้างเอกสาร เป็นต้น

เครื่องมือที่นิยมใช้กันมีดังนี้

อีกวิธีหนึ่งคือ เขียนอธิบายเป็นคอมเมนต์หรือฟังก์ชันในโค้ดโปรแกรม และให้เครื่องมือประเภท Automate API documentation ช่วยสแกนและสร้าง Specification จากโค้ดที่เขียนไว้เลย เครื่องมือประเภทนี้ถูกสร้างขึ้นสำหรับใช้กับภาษาโปรแกรมเพียงภาษาเดียว ยกตัวอย่างเช่น

  • tsoa — ใช้ใน Node.js REST API ที่เขียนด้วย TypeScript
  • Springdoc — ใช้ใน Spring Application (REST API ภาษา Java, หากต้องการใช้ สามารถดูวิธีการใช้ที่ลิงก์นี้)

เครื่องมือประเภทนี้เหมาะสำหรับโปรเจคที่มี REST API ตัวจริงมาแล้วแต่ยังไม่มีเอกสารใด ๆ มันช่วยสร้างความสะดวกให้กับเรา เราไม่ต้องเสียเวลาสร้างเอกสารขึ้นเอง ช่วยลดปัญหาเอกสารและ REST API ทำงานไม่ตรงกัน อีกทั้งยังช่วยให้นักพัฒนารู้ข้อมูลเกี่ยวกับ API Endpoint ที่ทำงานอยู่ทันที เนื่องจากข้อมูลเกี่ยวกับ API Endpoint ส่วนใหญ่จะเป็นคอมเมนต์อยู่ใกล้กับโค้ดของ API Endpoint แต่ละตัว แต่ก็มีข้อเสียอยู่เช่นเดียวกัน คือ มันจะเพิ่มขั้นตอนและอาจเพิ่มเวลาการเริ่มทำงานของ REST API ด้วย และทำให้โค้ดดูเยอะและรกหากไม่มีการจัด Format ของโค้ดที่ดีพอ

การใช้ OpenAPI กับ REST API

ในปัจจุบันมีเครื่องมือที่ทำงานกับ API Specification ที่เขียนตามมาตรฐานนี้อยู่มากมาย ส่วนใหญ่จะช่วยสร้างเอกสาร และช่วยเพิ่มความสะดวกในการพัฒนา ปรับปรุง และทดสอบ REST API ทำให้เรามั่นใจได้ว่า REST API ที่สร้างนั้นจะตรงกับ API Specification ที่เราเขียนไว้ ในหัวข้อนี้จะยกตัวอย่างเครื่องมือบางตัวที่ทำงานกับ API Specification ในหมวดหมู่ดังต่อไปนี้

  • เครื่องมือช่วยสร้าง Source code
  • เครื่องมือช่วยสร้างเอกสาร
  • เครื่องมือช่วยสร้าง Mock API Server
  • เครื่องมือช่วย Validate API

เครื่องมือช่วยสร้าง Source code

รูปแสดงหน้าเว็บของ OpenAPI Generator — openapi-generator.tech

เครื่องมือประเภทนี้สามารถสร้าง Source code ของ Application และเอกสารตามภาษาที่เราเลือกให้ได้ ภายในตัวเครื่องมือจะมีฟังก์ชันต่าง ๆ เตรียมไว้ให้เราใช้งาน เรียกว่า Generator มีอยู่ 3 ประเภทใหญ่ ๆ คือ

  • Client Generator — ใช้สร้าง Application ที่มีการติดต่อกับ REST API อยู่บ่อยครั้ง เช่นพวก Frontend, หรือพวก SDK (Software Development Kit), โปรแกรม CLI (Command-line Interface) ภายใน Source code ที่สร้างขึ้นจะมีฟังก์ชันติดต่อกับ REST API ติดมาให้
  • Server (Stub) Generator — ใช้สร้าง Application ที่เป็น REST API เมื่อสร้างออกมา จะมีฟังก์ชันสร้าง Server, ฟังก์ชันที่เรียกใช้ Middleware ที่จำเป็น เช่น ฟังก์ชันกำหนด Header, CORS เป็นต้น และฟังก์ชันที่เป็น Controller ของแต่ละ API Endpoint เตรียมไว้ให้ สามารถใช้เป็น API ปลอม (เรียกว่า Server Stub หรือ Mock API) ให้ Client ลองติดต่อ หรือจะใช้เป็นตัวเริ่มต้นนำไปสู่การสร้าง REST API ที่สมบูรณ์ก็ได้
  • Document Generator — ใช้สร้างเอกสารในรูปแบบต่าง ๆ มักจะมีในรูปแบบ HTML ให้ใช้

เครื่องมือที่นิยมใช้กันจะมีอยู่ 2 ตัว คือ Swagger Codegen กับ OpenAPI Generator โดยทั้ง 2 ตัวเมื่อใช้งานจะได้ผลลัพธ์ที่คล้ายกัน ทั้งนี้ เนื่องจากเครื่องมือที่ชื่อ OpenAPI Generator เป็นโปรเจคที่ Fork มาจาก Swagger Codegen

เครื่องมือประเภทนี้มีข้อดีคือ มันช่วยให้เราสามารถสร้าง Application โดยไม่ต้องเสียเวลาในวางโครงสร้างโปรเจคและเตรียมฟังก์ชันในเบื้องต้น ข้อเสียก็คือ เป็นการทำให้ API Specification ไม่เป็น “Single source of truth” (แหล่งข้อมูลจริงอยู่ที่เดียว) อีกต่อไป เพราะว่าเมื่อใช้เครื่องมือประเภทนี้ช่วยสร้างโปรเจค จะมี API Specification อีกไฟล์ปรากฎอยู่ในโปรเจคด้วย ที่มันเป็นอย่างนั้น เพราะว่าในโปรเจคที่ Generator บางตัวสร้างมีเครื่องมืออีกประเภทที่ต้องใช้กับ API Specification แถมมาให้ใช้งาน เช่นพวก API Tester, API Request Validation (จะพูดถึงในหัวข้อต่อไป) ด้วย ทำให้เราต้องคอยปรับปรุง API Specification ที่อยู่ในโปรเจคให้ตรงกับ API Specification ที่เราใช้สร้าง Application ไปตอนแรก หากมีการเปลี่ยนแปลงในไฟล์ Specification นั้น

ในกรณีที่มีการปรับปรุง API Specification เช่น เพิ่ม/ลด API Endpoint เราสามารถใช้เครื่องมือสร้างทับ Application เดิมได้ แต่ต้องระวังว่า มันจะเขียนทับไฟล์ทั้งหมดที่มีอยู่ตั้งแต่ตอนสร้าง ส่งผลทำให้ Code ใด ๆ ที่เราเขียนเพิ่มเติมทั้งหมดในไฟล์เหล่านี้หายไป แต่หากต้องการใช้เครื่องมือเพื่ออัพเดท API ที่มีอยู่จริง ๆ ในโปรเจคที่สร้างจะมีไฟล์หนึ่งที่ชื่อว่า openapi-generator-ignore (ถ้าสร้างด้วย Swagger Codegen จะมีชื่อไฟล์ว่า .swagger-codegen-ignore) ทำหน้าที่ป้องกันไม่ให้ Generator เขียนทับไฟล์ตามที่ระบุ เราสามารถเขียนชื่อไฟล์ที่ต้องการโดยใช้รูปแบบเดียวกับที่ใช้ใน .gitignore ก่อนที่จะใช้เครื่องมือในครั้งต่อไป

เครื่องมือช่วยสร้างเอกสาร

ในที่นี้จะขอยกตัวอย่างเครื่องมือสร้างเอกสารที่เป็นที่นิยม 4 ตัวด้วยกัน ดังนี้

Swagger UI เป็นเครื่องมือสร้างเอกสารตัวหนึ่งที่เป็น Open Source ถูกพัฒนาโดยบริษัท Smart Bear (เช่นเดียวกับ Swagger Codegen) เอกสารต่าง ๆ ที่ถูกสร้างขึ้นโดยโปรแกรมนี้จะมีลักษณะเป็น Interactive Documentation แสดงข้อมูลครบ เข้าใจง่าย มีช่องทางให้ผู้อ่านเอกสารทุกคน ไม่ว่าจะเป็นผู้พัฒนาและลูกค้า สามารถติดต่อกับ API ผ่านเอกสารนี้ได้อย่างง่ายดาย

ตัวอย่างหน้าเอกสารที่สร้างจาก Swagger UI

Redoc เป็นเครื่องมือสร้างเอกสารประเภท Interactive Document ผู้สร้างเอกสารสามารถตกแต่งเอกสารได้ตามที่ต้องการเหมือนที่ทำกับหน้าเว็บทั่วไป หากใช้ React พัฒนาเว็บไซต์ก็สามารถใช้งาน Redoc เป็น Component ตัวหนึ่งได้ เอกสารที่ถูกสร้างขึ้นจะสามารถอ่านได้ในทั้ง Desktop และ Mobile มีส่วนที่ให้ผู้อ่านสามารถคลิกเพื่อเปิดดู หรือซ่อนรายละเอียดของ Object เช่น Schema, Parameter, Request Body, Response เป็นต้น และสามารถดูการเรียกใช้ API ด้วยภาษาโปรแกรมต่าง ๆ (แต่ไม่มีที่ให้เรียกใช้ API เหมือน Swagger UI)

ตัวอย่างหน้าเอกสารที่สร้างจาก Redoc จาก Github ของ Redoc

OpenAPI Generator นอกจาก Application แล้ว ยังสามารถสร้างเอกสารในรูปแบบต่าง ๆ ได้ ในที่นี้จะแสดงตัวอย่างสร้างเอกสารโดยใช้ Generator ชื่อ html, html2, dynamic-html และ markdown

HTML — Generator ตัวนี้จะสร้างหน้าเว็บง่าย ๆ เป็นไฟล์ HTML เพียงไฟล์เดียว

openapi-generator generate -i petstore.yaml -g html -o petstore-document

ด้านบนเป็นตัวอย่างคำสั่งสร้างเอกสารในรูปแบบนี้ ผลลัพธ์ที่ได้จะมีไฟล์ที่ชื่อว่า index.html อยู่ในโฟลเดอร์ตามที่กำหนดใน -o เมื่อเปิดไฟล์นี้ จะพบกับเอกสารที่มีหน้าตาตามตัวอย่าง

ตัวอย่างหน้าเอกสารที่สร้างจาก Generator “html” ของ OpenAPI Generator

ยังมี Generator ที่สร้าง HTML อีกรูปแบบหนึ่ง คือ HTML2

Generator ตัวนี้จะสร้างเอกสารในรูปแบบ HTML ที่สวยงามกว่ารูปแบบแรก

openapi-generator generate -i petstore.yaml -g html2 -o petstore-document

ตัวอย่างหน้าเอกสารที่สร้างจาก Generator “html2” ของ OpenAPI Generator

Generator ชื่อ dynamic-html สามารถสร้างไฟล์ HTML ได้เช่นกัน แต่จะสร้างเป็นชุดของไฟล์ มีทั้ง HTML แยกตาม Operation และ Model (Schema), ไฟล์ CSS, ไฟล์ Javascript และต้องรันด้วย Node.js ถึงจะแสดงหน้าเว็บสมบูรณ์ รูปแบบนี้จะโหลดเนื้อหาค่อนข้างช้าเพราะมีการรัน JavaScript (JQuery) เพื่อ Render เว็บแต่ละหน้า

ตัวอย่างหน้าเอกสารที่สร้างจาก Generator “dynamic-html” ของ OpenAPI Generator

Markdown — OpenAPI Generator สามารถสร้างเอกสารในรูปแบบ Markdown (.md) ได้เช่นกัน

openapi-generator generate -i petstore.yaml -g markdown -o petstore-document

เมื่อรันคำสั่งนี้ จะได้ชุดไฟล์ Markdown ที่ประกอบด้วยหน้าหลัก (README.md) และหน้าของแต่ละ Operation และ Model (Schema) ซึ่งแต่ละหน้ามีลิงก์เข้าไปดูหน้าของกันและกันได้

ภาพแสดงชุดไฟล์ .md, หน้าตาของไฟล์ README.md และหน้าตาของ README.md เมื่อแสดงผลในเว็บที่ Render Markdown ได้

Slate & Widdershins

Slate เป็นตัวช่วยสร้างเอกสารจาก Markdown ที่มีคุณสมบัติเด่นคือ เป็น Single Page ที่มีความสวยงามและ Responsive รองรับแสดงตัวอย่างโค้ดจากหลากหลายภาษาโปรแกรม โดยไม่ต้องกำหนด Config ใด ๆ

ตัวอย่างหน้าเว็บเอกสารที่สร้างจาก Slate

เนื่องจาก Slate เป็นโปรแกรมพัฒนาด้วย Ruby การสร้างเอกสารด้วย Slate นั้นจึงต้องติดตั้ง Ruby ในเครื่อง รวมถึง Bundler ซึ่งเป็น Package Manager ของ Ruby ด้วย

Markdown ใน Slate จะมีลักษณะเนื้อหาที่ต้องเพิ่มที่เฉพาะตัวอยู่บ้าง บางทีต้องใช้เครื่องมือช่วยในการสร้าง Markdown สำหรับใช้ใน Slate

สำหรับ OpenAPI นั้น มีเครื่องมือที่สามารถเปลี่ยน Specification ให้เป็น Markdown เช่นนั้นได้ นั่นก็คือ Widdershins

Widdershins เป็นโปรแกรมประเภท CLI ที่สามารถติดตั้งผ่าน NPM ได้

npm install -g widdershins

คำสั่งแปลงเอกสารที่เรียบง่ายที่สุดจะเป็นดังนี้

widdershins petstore.yaml -o petstore-markdown.md

เมื่อแปลงเสร็จแล้วจะได้ไฟล์ Markdown (ในคำสั่งตัวอย่างจะเป็นไฟล์ที่มีชื่อว่า petstore-markdown.md) ที่มีลักษณะดังรูป

ลักษณะ Markdown ที่สร้างจาก Widdershins

ถึงตรงนี้ก็จะสามารถนำ Markdown ที่ได้นี้ ไปให้ Slate สร้างเอกสารได้ ซึ่งวิธีการสร้างจะมีอธิบายใน Github ของ Slate

เครื่องมือช่วยสร้าง Mock API Server

OpenAPI Specification สามารถนำไปสร้าง Mock API ที่มีการใช้งานเหมือน REST API จริงได้เพื่อนำไปแสดงให้ลูกค้า หรือให้ Frontend Developer นำไปเรียกใช้ได้โดยไม่ต้องเขียนโค้ดเพิ่มเติม เครื่องมือที่นิยมใช้ก็คือ Prism

รูปแสดงหน้าเว็บของ Prism — stoplight.io/open-source/prism/

Prism เป็นโปรแกรมที่มีความสามารถ 2 อย่าง หนึ่งคือ สามารถสร้าง REST API ปลอมที่มีการตรวจสอบข้อมูลขาเข้า และส่งข้อมูลออก (Response) ใกล้เคียงกับ API จริง สองคือ สามารถสร้าง Proxy Server ที่ Redirect ไปยัง REST API ตัวจริงได้ เพื่อช่วยให้ทางฝั่ง Frontend สามารถเชื่อมต่อง่ายขึ้น ซึ่ง Proxy ตัวนี้เราสามารถตรวจสอบได้ว่าเมื่อเราส่ง Request ไปให้ API Endpoint จะเกิดอะไรขึ้นบ้าง? และได้ Response อย่างที่เราเขียนใน Specification หรือไม่?

ในหัวข้อนี้จะแสดงถึงความสามารถของ Prism โดยสรุป

Validation

Mock API ของ Prism มีการ Validate ข้อมูลที่ส่งมาให้ด้วย ถ้าเราลองส่งค่าผิดไปจาก Specification เช่น ส่ง String ไปเป็นตัวเลข ไม่ได้ส่งค่าที่ Required หรือไม่ได้ส่ง API Key ทาง Header มันก็จะส่ง Response ตอบกลับเป็น Error ดังตัวอย่าง

Response ที่ได้รับเมื่อส่ง Request Body ที่มี category.name เป็นตัวเลข (Specification กำหนดไว้ว่าต้องส่งเป็น String)

Response ที่ได้รับเมื่อเราส่ง Request Body ที่ไม่มี name และ photoUrl (Specification กำหนดว่าต้องมี)

Response

ค่าใน Response โดยปกติ ถ้าไม่ได้กำหนดค่าตัวอย่าง จะแสดงค่าเป็น default ตามชนิดข้อมูล เช่น ตัวเลข จะแสดงเป็น 0 ข้อความจะแสดงเป็น “string” Boolean จะแสดงค่าเป็น true เป็นต้น

รูปแสดง Response เป็นค่า Default

แต่ถ้ากำหนดค่าตัวอย่างใน Schema ตัวหนึ่ง อย่างตัวอย่างนี้

type: object
  properties:
      name:
          type: string
          example: doggie

ผลลัพธ์ที่ได้หากมีการใช ้Response เป็น Schema ที่มี Properties ชื่อ “name” นี้ เมื่อทำ Mock API และเรียก API ที่มีการใช้ Response ดังกล่าว จะเห็นว่ามีคำว่า doggie ตามที่กำหนดปรากฎด้วย

รูปแสดง Response ที่ Property ชื่อว่า “name” ชื่อว่า “doggie” ตามที่กำหนดไว้ใน Specification

ถ้าเรากำหนด Response ตัวหนึ่งมี Example Object อย่างเช่น

/user/{username}:
  get:
    tags:
      - user
    summary: Get user by user name
    description: ''
    operationId: getUserByName
    parameters:
      - name: username
        in: path
        description: The name that needs to be fetched.
        required: true
        schema:
          type: string
    responses:
      '200':
        description: successful operation
        content:
          application/xml:
            schema:
              $ref: '#/components/schemas/User'
            examples:
              user1:
                value:
                  id: 10
                  name: Jessica Smith
      '400':
        description: Invalid username supplied
      '404':
        description: User not found

เมื่อเราใช้ API ที่ส่ง Response ดังกล่าว Mock API จะส่ง Response เป็น Example Object นั้นมาแทน ดังรูป

รูปแสดง Response ที่แสดงเป็นค่าตาม Example ตามที่กำหนดไว้ใน Specification

Dynamic Response

หากไม่มีพวก Example Object เราสามารถให้ Mock API สร้างค่าเองได้ โดยรัน API ด้วยคำสั่งใหม่ ที่มี -d (Dynamic Response)

prism mock -d petstore.yaml

เมื่อลองใช้ API ตัวหนึ่ง ก็จะพบว่า Mock API ก็จะสุ่มค่ามั่ว ๆ ให้ดังตัวอย่าง

รูปแสดง Response เป็นแบบ Dynamic

Mock API จะสุ่มค่ามั่ว ๆ เช่นนี้ให้กับทุก Field ข้อมูลในทุก Schema ถึงแม้ว่า Schema หรือ Response นั้นจะมี Example Value/Object รองรับก็ตาม เมื่อเราต้องการรัน Mock API ในโหมดนี้ เราต้องกำหนดลักษณะเฉพาะให้กับทุก Field นอกจาก type เพื่อให้ได้ค่าที่มีลักษณะใกล้เคียงหรือเดียวกับที่เราต้องการ

/example:
  get:
    responses:
      '200':
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                age:
                  type: integer
                  minimum: 0
                  maximum: 100

จากตัวอย่าง เมื่อเราใช้ API ตัวนี้ เราจะได้ Response ดังตัวอย่างนี้

รูปแสดง Response เป็นแบบ Dynamic ที่มีลักษณะเจาะจงมากขึ้น

จากภาพตัวอย่าง จะเห็นได้ว่า เมื่อเรากำหนดลักษณะเฉพาะของแต่ละ Field ค่าที่อยู่ใน Response จะมีลักษณะตรงกับที่กำหนด แต่ข้อมูลบางส่วนก็จะดูมั่ว ๆ อยู่ดี เพื่อให้ได้ค่าที่ใกล้เคียงกับความเป็นจริงมากขึ้น เราสามารถให้ Prism ทำการสุ่มค่าเช่นนั้น โดยกำหนด Property พิเศษชื่อ x-faker ให้กับ Field ข้อมูลที่ต้องการ ค่าของ x-faker จะต้องเป็นชื่อฟังก์ชันตามที่แสดงในหน้าเว็บนี้ (มาจาก Library ที่ชื่อว่า Faker.js)

/example:
  get:
    responses:
      '200':
        description: successful operation
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  x-faker: internet.email
                age:
                  type: integer
                  minimum: 0
                  maximum: 100

จากตัวอย่าง เมื่อเราใช้ API ตัวนี้ เราจะได้ Response ดังตัวอย่างนี้

รูปแสดง Response เป็นแบบ Dynamic ที่มีลักษณะเจาะจงจากการใช้ x-faker

เครื่องมือช่วย Validate API

หากเราทำตามกระบวนการ API Design-First Appoarch เราจะสามารถทำ Testing และ Validation กับ REST API ได้อย่างง่ายดาย และใช้เวลาไม่นาน เพราะในเมื่อ OpenAPI Specification ถูกสร้างมาตั้งแต่ขั้นตอน Design แล้ว เราก็สามารถนำมันทำงานกับเครื่องมือที่ทำหน้าที่สองอย่างนี้ให้นั่นเอง

เครื่องมือประเภทนี้จะทำหน้าที่เป็นตัวกลาง หรือ Middleware ของ REST API ช่วยกรองข้อมูลขาเข้าให้ โดยเมื่อผู้ใช้ส่ง Request เข้ามายัง REST API ของเราผ่าน API Endpoint ตัวหนึ่ง เครื่องมือตัวนี้ก็จะนำ Request ที่ได้รับไปเทียบกับข้อมูลของ API Endpoint ที่เราเขียนใน Specification ว่าตรงกันหรือไม่ หากตรงกัน ก็จะให้ REST API ทำตาม Logic ปกติ แต่ถ้าไม่ มันก็จะส่ง Response บอกความผิดพลาดออกมา

การทำงานแบบนี้ยังช่วยในเรื่องของ Testing ให้ด้วย กล่าวคือ ทำให้เรารู้ได้ว่า ที่จริงแล้ว REST API สามารถรับข้อมูลได้ตรงตามที่เขียนไว้ใน Specification หรือไม่

ในหัวข้อนี้จะยกตัวอย่างเครื่องมือตัวหนึ่ง คือ

Prism ตัวเดียวกับตัวที่กล่าวถึงในหัวข้อที่แล้วนี่เอง เพราะ Prism นอกจาก Mock API ได้แล้ว ยังสามารถสร้าง Proxy Server ได้ ซึ่ง Proxy ตัวที่สร้างขึ้นจะเป็นด่านหน้าของ REST API ช่วยดักจับ Request มาตรวจสอบ

การสร้าง Proxy Server ด้วย Prism สามารถทำได้ด้วยคำสั่งนี้

prism proxy petstore.yaml localhost:3000

คำสั่งดังกล่าว จะเปิด Proxy ที่ Forward ไปที่ API ตามที่อยู่ localhost:3000 โดยใช้ Specification petstore.yaml ช่วยกรองและตรวจสอบ Request ต่าง ๆ ที่เข้ามา และ Response ที่ API ตัวนี้ส่งออก เสร็จแล้ว เราก็ใช้งาน API ตามที่อยู่ที่ Prism ให้ Response ที่ส่งมาจะเหมือนกับ REST API ที่ Mock โดยใช้ Prism

ภาพแสดงผลลัพธ์จากการเปิดใช้งาน Proxy Server ของ Prism

Proxy Server จะช่วย Debug สิ่งที่เกิดขึ้นเมื่อมีการใช้งาน REST API ผ่าน Proxy ดังรูปตัวอย่างนี้

ทั้งนี้สามารถอ่านข้อมูลเพิ่มเติมได้ที่ Documentation ของ Prism

สรุปและทิ้งท้าย

ในบทความนี้ จะเห็นได้ว่า OpenAPI Specification นั้นช่วยให้เราสามารถเขียนคำอธิบายถึงลักษณะของ REST API ได้ครบถ้วน คำอธิบายนั้นประกอบด้วย ชื่อ, เวอร์ชันปัจจุบัน, ข้อมูลการติดต่อ, ลิขสิทธิ์, Server ที่ตั้ง REST API, Path ต่าง ๆ, รวมไปถึงโครงสร้างข้อมูลที่ใช้ใน REST API การเขียน Specification สามารถทำได้ใน Swagger Editor และ Text Editor ทั่วไป รวมถึงพวก GUI Editor อย่างเช่น Stoplight Studio, Apicurio Studio เป็นต้น เมื่อเขียนเสร็จแล้ว นอกจากจะเป็นเอกสารสำคัญ ยังสามารถเป็นตัวช่วยพัฒนา REST API อีกด้วย ในบทความนี้ได้แสดงให้เห็นถึงความสามารถของ OpenAPI Specification ในการเป็นตัวช่วย 4 อย่าง คือ Generate Applications, Documentation, Mock API, และการ Validate API

ที่จริงเครื่องมือที่ทำงานกับ OpenAPI Specification มีไม่หมดเพียงแค่นี้ ยังมีเครื่องมืออื่น ๆ อีกมากมาย หากเราต้องการเครื่องมือใช้งานเพิ่มเติม ผู้เขียนขอแนะนำเว็บหนึ่งที่รวบรวมเครื่องมือสร้าง Specification และเครื่องมือที่ทำงานร่วมกับ Specification เพื่อสร้างสิ่งต่าง ๆ ตามที่เราต้องการ เว็บนี้มีชื่อว่า OpenAPI.tools สามารถเข้าไปดูได้เลยค่ะ

ขอขอบคุณผู้อ่านทุกคนที่อ่านบล็อกนี้จนจบด้วยนะคะ กด 👏 เพื่อเป็นกำลังใจให้ให้ผู้เขียนได้ ถ้ามีคำแนะนำหรือคำติชมอะไร สามารถ Comment ได้เลยนะคะ

พบกันใหม่ในบล็อกหน้า สวัสดีค่ะ

Credit & References

Icons (Used in image of API Design-Fist Approach)


This article was originally published at Medium.com