ЁЯУЛ рдзрдбрд╛ 04 тАФ JSON рдЖрдгрд┐ schemas: рдкреНрд░рдорд╛рдгрд┐рдд рдлреЙрд░реНрдо
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 04 ┬╖ рдорд╛рдЧреЗ: lesson-03-rest-resources ┬╖ рдкреБрдвреЗ: lesson-05-build-an-api
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ03, рдЖрдгрд┐ рддреНрдпрд╛рд╕реЛрдмрдд рдкреНрд░рддреНрдпрдХреНрд╖ form: JSON bodies, рд╡рд╛рдИрдЯ forms рдХрд╛рд░рдгрд╛рд╕рд╣ рдирд╛рдХрд╛рд░рдгрд╛рд░реЗ validation, рдЖрдгрд┐ OpenAPI тАФ counter рд╕реНрд╡реАрдХрд╛рд░рддреЛ рддреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ form рдЪрд╛ рдЫрд╛рдкрд▓реЗрд▓рд╛ рдХреЕрдЯрд▓реЙрдЧ. рдЦрд░реА file:
- api/openapi.yaml тАФ рдХреЕрдЯрд▓реЙрдЧ
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
Counter рдЦрд░рдбрд▓реЗрд▓реНрдпрд╛ рдЪрд┐рдареНрдареНрдпрд╛ рд╕реНрд╡реАрдХрд╛рд░рдд рдирд╛рд╣реА. рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рдЪреЗ рдирд╛рд╡ рдиреЛрдВрджрд╡рд╛рдпрдЪреЗ рддрд░ рддреБрдореНрд╣реА рдкреНрд░рдорд╛рдгрд┐рдд form ЁЯУЛ рднрд░рддрд╛:
{"name": "Zoya", "class": "3B", "grade": "A"}
Archive рдХрдбреЗ рдЬрд╛рдгреНрдпрд╛рдЪреНрдпрд╛ рдЖрдзреА clerk рддреЛ рддрдкрд╛рд╕рддреЛ: name рднрд░рд▓реЗ
рдЖрд╣реЗ рдХрд╛? class рдЖрдкрд▓реНрдпрд╛рдХрдбреЗ рдЦрд░реЛрдЦрд░ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рд╡рд░реНрдЧрд╛рдВрдкреИрдХреА рдПрдХ рдЖрд╣реЗ рдХрд╛ (3A, 3B)?
grade рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдЕрд╕рд▓реЗрд▓реНрдпрд╛ grades рдкреИрдХреА рдПрдХ рдЖрд╣реЗ рдХрд╛? рдирд╕реЗрд▓ рддрд░ slip рд▓рдЧреЗрдЪ
400 рд╢рд┐рдХреНрдХрд╛ рдЖрдгрд┐ рдиреЗрдордХреЗ рдХрд╛рдп рдЪреБрдХрд▓реЗ рдпрд╛рдЪреА field-рд╡рд╛рд░ рдпрд╛рджреА рдШреЗрдКрди рдкрд░рдд
рдпреЗрддреЗ тАФ рдЦрд╛рдВрджреЗ рдЙрдбрд╡рдгреЗ рдирд╛рд╣реА, 500 рдирд╛рд╣реА, "рдХрд╛рд╣реАрддрд░реА рдЪреБрдХрд▓реЗ" рдирд╛рд╣реА.
рдЖрдгрд┐ forms рдХрд╕реЗ рджрд┐рд╕рддрд╛рдд рдпрд╛рдЪрд╛ рдХреЛрдгрд╛рд▓рд╛рдЪ рдЕрдВрджрд╛рдЬ рд▓рд╛рд╡рд╛рд╡рд╛ рд▓рд╛рдЧреВ рдирдпреЗ рдореНрд╣рдгреВрди office рддреНрдпрд╛рдВрдЪрд╛ рдХреЕрдЯрд▓реЙрдЧ рдЫрд╛рдкрддреЗ тАФ рдкреНрд░рддреНрдпреЗрдХ path, рдкреНрд░рддреНрдпреЗрдХ parameter, рдкреНрд░рддреНрдпреЗрдХ field, рдкреНрд░рддреНрдпреЗрдХ рд╢рд┐рдХреНрдХрд╛ тАФ OpenAPI рдирд╛рд╡рд╛рдЪреНрдпрд╛ рдкреНрд░рдорд╛рдгрд┐рдд рд░рдЪрдиреЗрдд. рдорд╛рдгрд╕реЗ рддреЛ рд╡рд╛рдЪрддрд╛рдд; рд╕рд╛рдзрдиреЗ рддреНрдпрд╛рддреВрди documentation, client libraries рдЖрдгрд┐ tests рддрдпрд╛рд░ рдХрд░рддрд╛рдд.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
form["ЁЯУЛ the form (JSON)<br/>name ┬╖ class тИИ {3A,3B} ┬╖ grade?"]
val["тЬЕ validate()<br/>required? type? allowed value?"]
ok["201 Created<br/>Location: /v1/students/6"]
bad["400 validation_failed<br/>problems: [{field, problem}, тАж]"]
form -->|"1"| val
val -->|"2 fine"| ok
val -.->|"2 not fine"| bad
cat["ЁЯУЪ openapi.yaml тАФ the catalogue<br/>paths ┬╖ params ┬╖ schemas ┬╖ errors"]
cat -.->|"3 describes all of it"| form
тЭУ рдХрд╛рдп
- JSON: objects
{}, arrays[], strings, numbers, booleans,null. Comments рдирд╛рд╣реАрдд, рд╢реЗрд╡рдЯрдЪреЗ рдЬрд╛рджрд╛ рд╕реНрд╡рд▓реНрдкрд╡рд┐рд░рд╛рдо рдирд╛рд╣реАрдд, dates рдирд╛рд╣реАрдд (рддреНрдпрд╛ strings рдЕрд╕рддрд╛рдд тАФ ISO 8601 рд╡рд░ рдПрдХрдордд рдХрд░рд╛). рджреЛрдиреНрд╣реА рдмрд╛рдЬреВрдВрдирд╛Content-Type: application/json. - Validation counter рд╡рд░ рд╣реЛрддреЗ, archive рдордзреНрдпреЗ рдирд╛рд╣реА: рдЖрд╡рд╢реНрдпрдХ
fields, types, рдЪрд╛рд▓рдгрд╛рд▒реНрдпрд╛ рдХрд┐рдорддреА (
enum), рдорд░реНрдпрд╛рджрд╛, formats.400рдЖрдгрд┐problemsрдпрд╛рджреА рджреЗрдКрди рдирд╛рдХрд╛рд░рд╛; рдЕрд░реНрдзрд╡рдЯ-рдмрд░реЛрдмрд░ form рдХрдзреАрдЪ storage рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪреВ рджреЗрдК рдирдХрд╛. - Schema: form рдЪрд╛ рд▓рд┐рдЦрд┐рдд рдЖрдХрд╛рд░. OpenAPI рдордзреНрдпреЗ рддреЛ JSON Schema рдЕрд╕рддреЛ:
type,required,enum,minLength,default. - OpenAPI (
api/openapi.yaml):pathsтЖТ operations тЖТparameters,requestBody,responses; рд╕рд╛рдорд╛рдпрд┐рдХ forms рд╕рд╛рдареАcomponents/schemas(Student,StudentInput,Error). рдПрдХ file рд╕рдВрдкреВрд░реНрдг counter рдЪреЗ рд╡рд░реНрдгрди рдХрд░рддреЗ; рдзрдбрд╛ 12 рддреА docs рдЖрдгрд┐ tests рд╕рд╛рдареА рд╡рд╛рдкрд░рддреЛ. - Output рд╕реБрджреНрдзрд╛ рдПрдХ form рдЖрд╣реЗ: рддреБрдореНрд╣реА рдкрд░рдд рджреЗрддрд╛ рддреНрдпрд╛ field рдирд╛рд╡рд╛рдВрд╡рд░ рдЖрдгрд┐ types рд╡рд░ clients рдЕрд╡рд▓рдВрдмреВрди рдЕрд╕рддрд╛рдд. Field рдЬреЛрдбрдгреЗ рд╕реБрд░рдХреНрд╖рд┐рдд рдЖрд╣реЗ; рддреЗ рдХрд╛рдврдгреЗ рдХрд┐рдВрд╡рд╛ рддреНрдпрд╛рдЪреЗ рдирд╛рд╡ рдмрджрд▓рдгреЗ рдирд╛рд╣реА (рдзрдбрд╛ 09).
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг archive рд╣рд╛ рдорд╣рд╛рдЧрдбрд╛ рднрд╛рдЧ рдЖрд╣реЗ. Counter рд╡рд░ рддрдкрд╛рд╕рд▓реЗрд▓реНрдпрд╛ form рд▓рд╛
microseconds рд▓рд╛рдЧрддрд╛рдд; database рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ рд╡рд╛рдИрдЯ row рдореБрд│реЗ data рдЪреА
рд╕рд╛рдлрд╕рдлрд╛рдИ рдХрд░рд╛рд╡реА рд▓рд╛рдЧрддреЗ, рдЖрдгрд┐ рдЬрд┐рдереЗ 400 рд╣рд╡рд╛ рддрд┐рдереЗ 500 рджрд┐рд▓реНрдпрд╛рдиреЗ on-call engineer рдЪрд╛ рдПрдХ
рддрд╛рд╕ рдЬрд╛рддреЛ. рдХреЕрдЯрд▓реЙрдЧ рд╣рд╛ рджреБрд╕рд░рд╛ рдЕрд░реНрдзрд╛ рднрд╛рдЧ рдЖрд╣реЗ: рд▓рд┐рдЦрд┐рдд contract рдирд╕рд▓реЗрд▓реЗ API
рдореНрд╣рдгрдЬреЗ рдлрдХреНрдд рдЕрдлрд╡рд╛.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
school_api.py рдордзрд▓реЗ validate_student(body) {field, problem} dicts рдЪреА
рдпрд╛рджреА рдкрд░рдд рджреЗрддреЗ; рдпрд╛рджреА рд░рд┐рдХрд╛рдореА рдирд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛ do_POST рдЖрдгрд┐ do_PUT
400 validation_failed рдиреЗ рдирд╛рдХрд╛рд░рддрд╛рдд. рддреНрдпрд╛рдЪреА рдУрд│-рдУрд│ рддреБрд▓рдирд╛
api/openapi.yaml рдордзрд▓реНрдпрд╛ StudentInput рд╢реА рдХрд░рд╛ тАФ рддреЗ рдиреЗрд╣рдореА рдЬреБрд│рд▓реЗрдЪ рдкрд╛рд╣рд┐рдЬреЗрдд.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
K='X-API-Key: hall-pass-123'; B=http://127.0.0.1:8080/v1; J='Content-Type: application/json'
curl -s -X POST $B/students -H "$K" -H "$J" -d '{"name":"","class":"9Z"}' # 400 with two problems
curl -s -X POST $B/students -H "$K" -H "$J" -d '{"name":"Zoya","class":"3B"}' # 201; grade defaults to C
curl -s -X POST $B/students -H "$K" -H "$J" -d '{"name":"Zoya","class":"3B","grade":"Z"}' # 400: grade
# now change the contract: add a required "roll_no" field to validate_student() AND openapi.yaml, restart, retest
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
рдкрд╣рд┐рд▓рд╛ call name рдЖрдгрд┐ class рдпрд╛рдВрдЪреА рдирд╛рд╡реЗ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ problems рд╕рд╣ 400 рдкрд░рдд рджреЗрддреЛ; рджреБрд╕рд░рд╛ 201 рдЖрдгрд┐ "grade": "C" рдЕрд╕рд▓реЗрд▓реА body рджреЗрддреЛ; рддрд┐рд╕рд░рд╛ grade рдЪреЗ рдирд╛рд╡ рд╕рд╛рдВрдЧрддреЛ. рддреБрдордЪрд╛ roll_no рдмрджрд▓ рдХреЗрд▓реНрдпрд╛рдирдВрддрд░, рддреБрдореНрд╣реА рддреЗ field рдЬреЛрдбреЗрдкрд░реНрдпрдВрдд рджреБрд╕рд░рд╛ call рдЕрдпрд╢рд╕реНрд╡реА рд╣реЛрддреЛ тАФ рдЖрдгрд┐ рдХреЕрдЯрд▓реЙрдЧ рдХрд╛ рддреЗ рд╕рд╛рдВрдЧрддреЛ.
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдХреЛрдгрддреЗрд╣реА рдХрд╛рдо рд╣реЛрдгреНрдпрд╛рдЖрдзреАрдЪ counter рд╡рд╛рдИрдЯ forms field-рдкрд╛рддрд│реАрд╡рд░рдЪреНрдпрд╛ рдХрд╛рд░рдгрд╛рд╕рд╣ рдирд╛рдХрд╛рд░рддреЛ, рдЖрдгрд┐ рдХреЕрдЯрд▓реЙрдЧ рд╡ code рдПрдХрд╛рдЪ form рдЪреЗ рд╡рд░реНрдгрди рдХрд░рддрд╛рдд.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- counter рдРрд╡рдЬреА database рдордзреНрдпреЗ validation рдХрд░рдгреЗ тАФ constraint error рдЪрд╛ 500 рд╣реЛрддреЛ
- рдкреНрд░рддреНрдпреЗрдХ рдЪреБрдХреАрд╕рд╛рдареА рдПрдХрдЪ error рд╕рдВрджреЗрд╢ ("invalid input") тАФ рдХреЛрдгрддреЗ field рдЖрдгрд┐ рдХрд╛ рддреЗ рд╕рд╛рдВрдЧрд╛
- code рдкрд╛рд╕реВрди рджреВрд░ рдЬрд╛рдгрд╛рд░рд╛ рдХреЕрдЯрд▓реЙрдЧ тАФ рдПрдХрд╛рддреВрди рджреБрд╕рд░реЗ generate рдХрд░рд╛, рдХрд┐рдВрд╡рд╛ рдПрдХрдореЗрдХрд╛рдВрд╡рд┐рд░реБрджреНрдз test рдХрд░рд╛ (рдзрдбрд╛ 12)
- dates
new Date()strings рдореНрд╣рдгреВрди рдкрд╛рдард╡рдгреЗ тАФ ISO 8601 рдЖрдгрд┐ time zones рд╡рд░ рдПрдХрдордд рдХрд░рд╛
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: contract-first teams OpenAPI file рдордзреВрди client SDKs, mocks рдЖрдгрд┐ tests generate рдХрд░рддрд╛рдд, рддреНрдпрд╛рдореБрд│реЗ frontend рдЖрдгрд┐ backend рдПрдХрд╛рдЪ рд╡реЗрд│реА рдмрд╛рдВрдзрд▓реЗ рдЬрд╛рддрд╛рдд. Contract рдирд╕рд▓реЗрд▓реНрдпрд╛ teams рдирд╛ field рдЪреА рдирд╛рд╡реЗ Slack рдордзреНрдпреЗ рдХрд│рддрд╛рдд.
тПня╕П рдкреБрдвреЗ
рдЖрддрд╛ counter рдЙрдШрдбреВрди рдкреНрд░рддреНрдпреЗрдХ рдУрд│ рд╡рд╛рдЪрд╛рдпрдЪреА рд╡реЗрд│: API рдмрд╛рдВрдзрд╛ тАФ school_api.py рдЪреЗ рдЪрд╛рд░
рднрд╛рдЧ.
git checkout lesson-05-build-an-api