การสร้างเอกสาร API ที่ครอบคลุมและถูกต้องเป็นส่วนสำคัญแต่ก็มักจะเป็นส่วนที่น่าเบื่อของการพัฒนาซอฟต์แวร์ OpenAPI Specification (เดิมชื่อ Swagger) ได้กลายเป็นมาตรฐานอุตสาหกรรมสำหรับการกำหนด RESTful API เป็นรูปแบบที่เครื่องอ่านได้ ซึ่งช่วยให้ทั้งมนุษย์และคอมพิวเตอร์สามารถค้นพบและทำความเข้าใจความสามารถของบริการได้ โดยไม่ต้องเข้าถึงซอร์สโค้ด เอกสารประกอบ หรือผ่านการตรวจสอบทราฟฟิกเครือข่าย1
ในขณะที่เฟรมเวิร์กหลายตัวมีปลั๊กอินสำหรับสร้าง OpenAPI spec จากคำอธิบายโค้ด (เช่น docstrings) ก็มีบางสถานการณ์ที่คุณอาจต้องการการควบคุมที่ตรงกว่าและเป็นโปรแกรมในการสร้าง specification นี่อาจเป็นเพราะคุณกำลังทำงานกับระบบเก่า เฟรมเวิร์กที่ไม่ได้มาตรฐาน หรือคุณจำเป็นต้องสร้าง spec สำหรับ API ที่ประกอบด้วยหลาย microservices
นี่คือจุดที่ pyswagger เข้ามามีบทบาท เป็นไลบรารี Python ที่ทรงพลังซึ่งทำหน้าที่เป็นชุดเครื่องมือสำหรับ OpenAPI แม้ว่าจะถูกใช้บ่อยครั้งเป็น API client เพื่อเรียกใช้บริการที่กำหนดโดย OpenAPI spec แต่พลังที่แท้จริงของมันอยู่ที่ object model ซึ่งช่วยให้คุณสามารถสร้าง จัดการ และตรวจสอบ specification ด้วยโปรแกรมได้
ในบทช่วยสอนที่ครอบคลุมนี้ เราจะมาดูขั้นตอนการใช้ pyswagger เพื่อสร้าง OpenAPI 3.0 specification ที่สมบูรณ์สำหรับเว็บแอปพลิเคชัน Python ง่ายๆ ที่สร้างด้วย Flask ด้วยตนเอง แต่ก็เป็นแบบอัตโนมัติ เราจะสร้าง specification ตั้งแต่เริ่มต้น ทีละส่วน โดยแสดงให้เห็นว่า object ของ pyswagger แมปโดยตรงกับส่วนประกอบต่างๆ ของมาตรฐาน OpenAPI อย่างไร เมื่อจบบทเรียนนี้ คุณจะไม่เพียงแค่มีไฟล์ openapi.json ที่สร้างขึ้นเท่านั้น แต่ยังมี UI เอกสารแบบโต้ตอบที่ใช้งานได้จริง ซึ่งให้บริการโดยตรงจากแอปพลิเคชันของคุณ
ต้องการแพลตฟอร์มแบบครบวงจร All-in-One สำหรับทีมพัฒนาของคุณเพื่อทำงานร่วมกันด้วย ประสิทธิภาพสูงสุด ใช่ไหม?
Apidog ตอบสนองทุกความต้องการของคุณ และ แทนที่ Postman ในราคาที่เข้าถึงได้ง่ายกว่ามาก!
ส่วนที่ 1: การตั้งค่าสภาพแวดล้อมของโปรเจกต์
ก่อนที่เราจะเริ่มสร้าง specification เราจำเป็นต้องตั้งค่าสภาพแวดล้อมการพัฒนาที่เหมาะสม ซึ่งรวมถึงการสร้างสภาพแวดล้อม Python ที่แยกออกมาเพื่อจัดการ dependencies และติดตั้งไลบรารีที่จำเป็น
การสร้างพื้นที่ทำงานของคุณ ⚙️
ขั้นแรก มาสร้างไดเรกทอรีสำหรับโปรเจกต์ของเรากัน เปิดเทอร์มินัลหรือ command prompt ของคุณ แล้วรันคำสั่งต่อไปนี้:Bash
# Create a new directory for our project
mkdir pyswagger-tutorial
cd pyswagger-tutorial
# Create a Python virtual environment
# On macOS/Linux
python3 -m venv venv
# On Windows
python -m venv venv
สภาพแวดล้อมเสมือน (virtual environment) คือโครงสร้างไดเรกทอรีแบบ self-contained ซึ่งรวมถึงการติดตั้ง Python และไฟล์สนับสนุนจำนวนหนึ่ง การใช้สภาพแวดล้อมเสมือนช่วยให้แน่ใจว่าแพ็กเกจที่เราติดตั้งสำหรับโปรเจกต์นี้จะไม่ขัดแย้งกับแพ็กเกจที่ติดตั้งสำหรับโปรเจกต์อื่น
ตอนนี้ เปิดใช้งานสภาพแวดล้อมเสมือน:Bash
# On macOS/Linux
source venv/bin/activate
# On Windows
.\venv\Scripts\activate
เมื่อเปิดใช้งานแล้ว พรอมต์ของเทอร์มินัลของคุณควรเปลี่ยนไปแสดงชื่อของสภาพแวดล้อมเสมือน (เช่น (venv)) ซึ่งบ่งชี้ว่าคุณกำลังทำงานอยู่ภายในนั้น
การติดตั้งไลบรารีที่จำเป็น
เมื่อสภาพแวดล้อมของเราเปิดใช้งานอยู่ เราก็สามารถติดตั้งไลบรารี Python ที่เราจะต้องใช้สำหรับบทช่วยสอนนี้ได้ เราต้องการ pyswagger เพื่อสร้าง spec, Flask เพื่อสร้าง web API ง่ายๆ ของเรา และ PyYAML เพราะ pyswagger ใช้สำหรับการดำเนินการเกี่ยวกับ YAML:Bash
pip install "pyswagger[utils]" Flask PyYAML
ส่วน [utils] เมื่อติดตั้ง pyswagger เป็นแนวปฏิบัติที่ดี เนื่องจากมีเครื่องมือที่เป็นประโยชน์ เช่น validator ที่เราจะใช้ในภายหลังเพื่อตรวจสอบความถูกต้องของ specification ที่สร้างขึ้น
เพื่อการจัดการโปรเจกต์ที่ดี ควรระบุเวอร์ชันของ dependencies ของคุณในไฟล์ requirements.txt:Bash
pip freeze > requirements.txt
ไฟล์ requirements.txt ของคุณตอนนี้จะมีไลบรารีและเวอร์ชันเฉพาะของมัน ทำให้โปรเจกต์ของคุณสามารถสร้างซ้ำได้ง่ายโดยผู้อื่น
การสร้างแอปพลิเคชัน Flask พื้นฐาน
ตอนนี้ มาสร้างแอปพลิเคชัน Flask ที่เรียบง่ายกัน นี่จะเป็นรากฐานของ API ที่เราจะจัดทำเอกสาร
ในไดเรกทอรีโปรเจกต์ของคุณ ให้สร้างไฟล์ใหม่ชื่อ app.py แล้วเพิ่มโค้ดต่อไปนี้:Python
# app.py
from flask import Flask, jsonify
# Initialize the Flask application
app = Flask(__name__)
@app.route("/")
def index():
""" A simple endpoint to check if the app is running. """
return jsonify({"message": "API is up and running!"})
if __name__ == "__main__":
# Runs the Flask app on http://127.0.0.1:5000
app.run(debug=True)
โค้ดนี้ตั้งค่าเว็บเซิร์ฟเวอร์ที่เรียบง่ายมากพร้อม endpoint เดียว ในการรัน ให้แน่ใจว่าสภาพแวดล้อมเสมือนของคุณยังคงเปิดใช้งานอยู่ แล้วรันคำสั่งต่อไปนี้ในเทอร์มินัลของคุณ:Bash
python app.py
คุณควรเห็นเอาต์พุตที่บ่งชี้ว่าเซิร์ฟเวอร์กำลังทำงาน คล้ายกับนี้:
* Serving Flask app 'app'
* Debug mode: on
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
ตอนนี้คุณสามารถเปิดเว็บเบราว์เซอร์หรือใช้เครื่องมืออย่าง curl เพื่อเข้าชม http://127.0.0.1:5000 ได้แล้ว คุณควรเห็นการตอบกลับแบบ JSON: {"message": "API is up and running!"}
เมื่อเรามีสภาพแวดล้อมพื้นฐานและโครงสร้างแอปพลิเคชันพร้อมแล้ว ตอนนี้เราก็สามารถเจาะลึกแนวคิดหลักของ pyswagger ได้แล้ว
ส่วนที่ 2: ทำความเข้าใจ pyswagger Object Model
เพื่อใช้ pyswagger สร้าง specification ได้อย่างมีประสิทธิภาพ คุณต้องเข้าใจก่อนว่า object model ของมันสอดคล้องกับโครงสร้างของเอกสาร OpenAPI อย่างไร OpenAPI specification เป็นหลักคือ JSON หรือ YAML object ขนาดใหญ่ที่มี schema เฉพาะ pyswagger มีคลาสและ object ใน Python ที่สะท้อน schema นี้ ทำให้คุณสามารถสร้าง spec ในรูปแบบที่ใช้งานง่ายและเป็น object-oriented มากขึ้น
แนวคิดหลักของ OpenAPI 3.0 Specification 📜
เอกสาร OpenAPI 3.0 มีฟิลด์ระดับบนสุดที่สำคัญบางส่วน:
openapi: สตริงที่ระบุเวอร์ชันของ OpenAPI Specification (เช่น'3.0.0')info: Object ที่ให้ข้อมูลเมตาเกี่ยวกับ API ซึ่งรวมถึงtitle,version,descriptionและข้อมูลติดต่อservers: Array ของ object เซิร์ฟเวอร์ ซึ่งกำหนด base URL สำหรับ APIpaths: ฟิลด์ที่สำคัญที่สุด Object นี้เก็บ API endpoints (paths) ที่มีอยู่ทั้งหมด และการดำเนินการ HTTP (GET, POST, PUT, DELETE, ฯลฯ) ที่สามารถทำได้บน endpoints เหล่านั้นcomponents: Object ที่เก็บชุด object ที่สามารถนำกลับมาใช้ซ้ำได้สำหรับส่วนต่างๆ ของ spec นี่เป็นสิ่งสำคัญในการทำให้ specification ของคุณเป็นแบบ DRY (Don't Repeat Yourself) คุณสามารถกำหนดschemas(โมเดลข้อมูล),responses,parameters,examplesและอื่นๆ ที่นำกลับมาใช้ซ้ำได้
การแมป OpenAPI กับ pyswagger Objects
pyswagger มีการแมปที่ชัดเจนจากแนวคิด OpenAPI เหล่านี้ไปยัง object ใน Python มาสำรวจส่วนหลักที่เราจะใช้กัน
object หลักใน pyswagger คือ App คุณสามารถคิดว่า instance ของ App เป็นรากของเอกสาร OpenAPI ของคุณ:Python
from pyswagger import App
# The root of the specification document
# We initialize it with a version, but it can also load from a URL or file
root_app = App(version='3.0.0')
เมื่อคุณมี object App แล้ว คุณสามารถเริ่มเติม attribute ของมันได้ ซึ่งตรงกับฟิลด์ OpenAPI โดยตรง pyswagger ใช้ builder pattern ซึ่งช่วยให้ syntax ลื่นไหลและอ่านง่าย
Info และ Servers
ส่วน info และ servers นั้นเติมข้อมูลได้ตรงไปตรงมา:Python
# Populating the 'info' object
root_app.info.title = "User API"
root_app.info.version = "1.0.0"
root_app.info.description = "A simple API to manage users, used for the pyswagger tutorial."
# Populating the 'servers' array
# You create a Server object and append it
server = root_app.prepare_obj('Server', {'url': 'http://127.0.0.1:5000', 'description': 'Local development server'})
root_app.servers.append(server)
Paths และ Operations
Paths ถูกกำหนดบน object App คุณเพิ่ม paths ใหม่ แล้วกำหนดการดำเนินการ (HTTP methods) ภายในนั้น แต่ละการดำเนินการจะถูกกำหนดค่าด้วยรายละเอียดต่างๆ เช่น summary, description, parameters, requestBody และ responses:Python
# Defining a path and an operation
# This doesn't execute anything; it just builds the object structure.
path_item = root_app.define_path('/users')
get_op = path_item.define_op('get')
get_op.summary = "Retrieve a list of all users"
Components: Schemas, Parameters และ Responses
พลังที่แท้จริงของ OpenAPI spec ที่มีโครงสร้างดีมาจากส่วนประกอบที่นำกลับมาใช้ซ้ำได้ (reusable components) แทนที่จะกำหนดโครงสร้างของ object "User" ทุกครั้งที่ปรากฏในการตอบกลับ คุณกำหนดมันเพียงครั้งเดียวใน components/schemas แล้วอ้างอิงถึงมันโดยใช้ $ref pointer pyswagger จัดการสิ่งนี้ได้อย่างสวยงาม
Schema: Object Schema กำหนดโมเดลข้อมูล คุณสามารถระบุประเภท (object, string, integer) และคุณสมบัติของมันได้:Python
# Preparing a Schema object for a User
user_schema = root_app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'id': {'type': 'integer', 'format': 'int64'},
'username': {'type': 'string'},
'email': {'type': 'string', 'format': 'email'}
},
'required': ['id', 'username', 'email']
})
# Add it to the reusable components
root_app.components.schemas['User'] = user_schema
Parameter: Object Parameter กำหนด parameter ของการดำเนินการเดียว คุณระบุชื่อ ตำแหน่งที่ตั้ง (in: 'path', 'query', 'header', หรือ 'cookie') และ schema ของมัน:Python
# Preparing a Parameter object for a user ID in the path
user_id_param = root_app.prepare_obj('Parameter', {
'name': 'user_id',
'in': 'path',
'description': 'ID of the user to retrieve',
'required': True,
'schema': {'type': 'integer'}
})
Response: Object Response กำหนดโครงสร้างของการตอบกลับสำหรับรหัสสถานะ HTTP ที่เฉพาะเจาะจง ซึ่งรวมถึง description และ content ซึ่งระบุประเภทสื่อ (เช่น application/json) และ schema ของมัน:Python
# Preparing a Response object for a 200 OK response returning a single user
# Note the use of '$ref' to point to our reusable User schema
ok_user_response = root_app.prepare_obj('Response', {
'description': 'Successful retrieval of a user',
'content': {
'application/json': {
'schema': {'$ref': '#/components/schemas/User'}
}
}
})
การทำความเข้าใจการแมปนี้เป็นกุญแจสำคัญในการสร้าง specification ของคุณ โดยพื้นฐานแล้ว คุณกำลังสร้างกราฟ object ใน Python ซึ่ง pyswagger จะทำการ serialize ในภายหลังให้เป็นไฟล์ OpenAPI JSON หรือ YAML ที่ถูกต้อง
ส่วนที่ 3: การสร้าง API อย่างง่ายด้วย Flask
เพื่อให้การจัดทำเอกสารของเราเป็นไปได้จริง เราต้องการ API จริงๆ ที่จะจัดทำเอกสาร เราจะขยายแอป Flask ง่ายๆ ของเราจากส่วนที่ 1 ให้เป็น REST API ขั้นต่ำสำหรับการจัดการรายการผู้ใช้ API นี้จะทำหน้าที่เป็น "แหล่งข้อมูลที่ถูกต้อง" ที่เราจะอธิบายด้วย pyswagger
การออกแบบ API "ผู้ใช้" อย่างง่าย 📝
เราจะนำสี่ endpoint พื้นฐานมาใช้ ซึ่งแสดงถึงการดำเนินการ CRUD (Create, Read, Update, Delete) ทั่วไป:
GET /users: ดึงรายการผู้ใช้ทั้งหมดPOST /users: สร้างผู้ใช้ใหม่GET /users/{user_id}: รับผู้ใช้คนเดียวตาม ID ของพวกเขาDELETE /users/{user_id}: ลบผู้ใช้ตาม ID ของพวกเขา
เพื่อความเรียบง่าย เราจะใช้ Python dictionary อย่างง่ายเป็น "ฐานข้อมูล" ในหน่วยความจำของเรา ในแอปพลิเคชันจริง นี่จะเป็นการเชื่อมต่อกับฐานข้อมูลเช่น PostgreSQL หรือ MongoDB
การนำ Flask Endpoints มาใช้
มาอัปเดตไฟล์ app.py ของเราเพื่อรวม logic สำหรับ endpoints เหล่านี้กัน แทนที่เนื้อหาของ app.py ด้วยโค้ดต่อไปนี้:Python
# app.py
from flask import Flask, jsonify, request, abort
app = Flask(__name__)
# --- In-Memory Database ---
# A simple dictionary to store our users.
# The key is the user_id (integer), and the value is the user data (dict).
USERS_DB = {
1: {"username": "alice", "email": "alice@example.com"},
2: {"username": "bob", "email": "bob@example.com"},
3: {"username": "charlie", "email": "charlie@example.com"},
}
# A counter to simulate auto-incrementing IDs for new users
LAST_INSERT_ID = 3
# --- API Endpoints ---
@app.route("/users", methods=["GET"])
def get_users():
""" Returns a list of all users. """
# We need to convert the dictionary to a list of user objects, including their IDs.
users_list = []
for user_id, user_data in USERS_DB.items():
user = {'id': user_id}
user.update(user_data)
users_list.append(user)
return jsonify(users_list)
@app.route("/users", methods=["POST"])
def create_user():
""" Creates a new user. """
global LAST_INSERT_ID
if not request.json or 'username' not in request.json or 'email' not in request.json:
abort(400, description="Missing username or email in request body.")
LAST_INSERT_ID += 1
new_user_id = LAST_INSERT_ID
new_user = {
"username": request.json["username"],
"email": request.json["email"],
}
USERS_DB[new_user_id] = new_user
# The response should include the ID of the newly created user
response_user = {'id': new_user_id}
response_user.update(new_user)
return jsonify(response_user), 201
@app.route("/users/<int:user_id>", methods=["GET"])
def get_user(user_id):
""" Returns a single user by their ID. """
if user_id not in USERS_DB:
abort(404, description=f"User with ID {user_id} not found.")
user_data = USERS_DB[user_id]
user = {'id': user_id}
user.update(user_data)
return jsonify(user)
@app.route("/users/<int:user_id>", methods=["DELETE"])
def delete_user(user_id):
""" Deletes a user by their ID. """
if user_id not in USERS_DB:
abort(404, description=f"User with ID {user_id} not found.")
del USERS_DB[user_id]
# A 204 No Content response is standard for successful deletions
return '', 204
if __name__ == "__main__":
app.run(debug=True, port=5000)
ตอนนี้ หากคุณรัน python app.py อีกครั้ง คุณก็จะมี API ที่ใช้งานได้เต็มรูปแบบ (แม้จะเรียบง่าย) คุณสามารถทดสอบได้ด้วย curl หรือเครื่องมือที่คล้ายกัน:
- รับผู้ใช้ทั้งหมด:
curl http://127.0.0.1:5000/users - รับผู้ใช้คนเดียว:
curl http://127.0.0.1:5000/users/1 - สร้างผู้ใช้:
curl -X POST -H "Content-Type: application/json" -d '{"username": "david", "email": "david@example.com"}' http://127.0.0.1:5000/users - ลบผู้ใช้:
curl -X DELETE http://127.0.0.1:5000/users/3
เมื่อเราได้นำ API มาใช้แล้ว เราก็มีเป้าหมายที่ชัดเจนสำหรับการจัดทำเอกสารของเรา ขั้นตอนต่อไปคือการใช้ pyswagger เพื่ออธิบายแต่ละ endpoint เหล่านี้อย่างละเอียด
ส่วนที่ 4: การสร้าง OpenAPI Spec แบบอัตโนมัติด้วย pyswagger
นี่คือหัวใจหลักของบทช่วยสอนของเรา ตอนนี้เราจะสร้างสคริปต์ Python แยกต่างหากที่ import pyswagger กำหนดโครงสร้าง API ของเราโดยใช้ object model ของมัน จากนั้น serialize โครงสร้างนั้นให้เป็นไฟล์ openapi.json ที่สมบูรณ์ แนวทางนี้ช่วยแยกการสร้าง specification ออกจาก logic ของแอปพลิเคชัน ซึ่งเป็นรูปแบบที่สะอาดและบำรุงรักษาได้ง่ายมาก
การสร้าง Specification Generator
ในไดเรกทอรีโปรเจกต์ของคุณ ให้สร้างไฟล์ใหม่ชื่อ generate_spec.py สคริปต์นี้จะรับผิดชอบในการสร้างและบันทึก OpenAPI specification ของเรา
การสร้าง Spec ทีละขั้นตอน
มาสร้างสคริปต์ generate_spec.py ทีละส่วนกัน
1. การ Import และการเริ่มต้น App
ขั้นแรก เราต้อง import object App จาก pyswagger และ PyYAML เพื่อช่วยในการ dump ไฟล์สุดท้าย เราจะสร้าง object App รากของเรา และเติมข้อมูลในส่วน info และ servers พื้นฐาน เหมือนที่เราได้พูดถึงในส่วนที่ 2:Python
# generate_spec.py
import json
from pyswagger import App
from pyswagger.contrib.client.requests import Client
# --- 1. Initialize the root App object ---
app = App(version='3.0.0')
# --- 2. Populating the Info & Servers sections ---
app.info.title = "User API"
app.info.version = "1.0.0"
app.info.description = "A simple API to manage users, for the pyswagger tutorial."
server = app.prepare_obj('Server', {
'url': 'http://127.0.0.1:5000',
'description': 'Local development server'
})
app.servers.append(server)
2. การกำหนดส่วนประกอบที่นำกลับมาใช้ซ้ำได้ (Schemas)
การออกแบบ API ที่ดีจะหลีกเลี่ยงการทำซ้ำ เราจะกำหนดโมเดลข้อมูล User ของเราเพียงครั้งเดียวและนำกลับมาใช้ใหม่ เราจะกำหนด schema Error ทั่วไปสำหรับการตอบกลับข้อผิดพลาดของเราด้วย (เช่น 404 Not Found)
เพิ่มโค้ดต่อไปนี้ลงใน generate_spec.py:Python
# --- 3. Defining Reusable Components (Schemas) ---
# Schema for the Error response
error_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'code': {'type': 'integer', 'format': 'int32'},
'message': {'type': 'string'}
}
})
app.components.schemas['Error'] = error_schema
# Schema for a single User. Note the properties match our USERS_DB structure.
user_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'id': {
'type': 'integer',
'description': 'Unique identifier for the user.',
'readOnly': True # The client cannot set this value
},
'username': {
'type': 'string',
'description': 'The user\'s chosen username.'
},
'email': {
'type': 'string',
'description': 'The user\'s email address.',
'format': 'email'
}
},
'required': ['id', 'username', 'email']
})
app.components.schemas['User'] = user_schema
# Schema for creating a user (doesn't include the 'id' field)
new_user_schema = app.prepare_obj('Schema', {
'type': 'object',
'properties': {
'username': {
'type': 'string',
'description': 'The user\'s chosen username.'
},
'email': {
'type': 'string',
'description': 'The user\'s email address.',
'format': 'email'
}
},
'required': ['username', 'email']
})
app.components.schemas['NewUser'] = new_user_schema
3. การจัดทำเอกสารสำหรับ /users Endpoints
ตอนนี้เราจะกำหนด path /users และการดำเนินการสองอย่างคือ GET และ POST
- สำหรับ
GETการตอบกลับคือ array ของ objectUser - สำหรับ
POSTrequest body จะคาดหวัง objectNewUserและการตอบกลับที่สำเร็จจะเป็น objectUserเพียงรายการเดียว (รวมถึง ID ใหม่)
Python
# --- 4. Documenting the Paths ---
# -- Path: /users --
path_users = app.define_path('/users')
# Operation: GET /users
op_get_users = path_users.define_op('get')
op_get_users.summary = "List all users"
op_get_users.description = "Returns a JSON array of all user objects."
op_get_users.tags.append('Users')
op_get_users.responses.A('200').description = "A list of users."
op_get_users.responses.A('200').content.A('application/json').schema.A(
'array', items={'$ref': '#/components/schemas/User'}
)
# Operation: POST /users
op_post_users = path_users.define_op('post')
op_post_users.summary = "Create a new user"
op_post_users.description = "Adds a new user to the database."
op_post_users.tags.append('Users')
op_post_users.requestBody.description = "User object that needs to be added."
op_post_users.requestBody.required = True
op_post_users.requestBody.content.A('application/json').schema.set_ref('#/components/schemas/NewUser')
op_post