ЁЯПл The SchoolтА║ЁЯПв APIsтА║ЁЯФм рдзрдбрд╛ 05 тАФ API рдмрд╛рдВрдзрд╛: ~235 рдкреНрд░рд╛рдорд╛рдгрд┐рдХ рдУрд│реАрдВрддрд▓реЗ рдХрд╛рдЙрдВрдЯрд░
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯФм рдзрдбрд╛ 05 тАФ API рдмрд╛рдВрдзрд╛: ~235 рдкреНрд░рд╛рдорд╛рдгрд┐рдХ рдУрд│реАрдВрддрд▓реЗ рдХрд╛рдЙрдВрдЯрд░

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 05 ┬╖ рдорд╛рдЧреЗ: lesson-04-json-and-schemas ┬╖ рдкреБрдвреЗ: lesson-06-auth


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ04, рдЖрдгрд┐ рддреНрдпрд╛рд╕реЛрдмрдд рдЦрд▒реНрдпрд╛ counter рдЪреЗ рдорд╛рд░реНрдЧрджрд░реНрд╢рд┐рдд рд╡рд╛рдЪрди тАФ api/school_api.py тАФ рдЖрдгрд┐ рддреНрдпрд╛рдд рддреБрдордЪреА рдкрд╣рд┐рд▓реА рднрд░.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

Counter рдмрд╛рдВрдзрдгреЗ рд╣реЗ framework рдЪреЗ рдХрд╛рдо рд╡рд╛рдЯрддреЗ, рдЬреЛрдкрд░реНрдпрдВрдд рддреБрдореНрд╣реА рдПрдХ рдЙрдШрдбреВрди рдкрд╛рд╣рдд рдирд╛рд╣реА. рдЖрдордЪреНрдпрд╛ counter рдЪреЗ рдиреЗрдордХреЗ рдЪрд╛рд░ рднрд╛рдЧ рдЖрд╣реЗрдд, file рдордзреНрдпреЗ рд╡рд░реВрди рдЦрд╛рд▓реА:

  1. рдЯреЗрдмрд▓рд╛рдВрдЪрд╛ рдирдХрд╛рд╢рд╛ ЁЯзн тАФ routes: route() /v1/students/2 рдЪреЗ ['students', '2'] рдХрд░рддреЗ, рдЖрдгрд┐ do_GET / do_POST / do_PUT / do_DELETE slip рдХреЛрдгрддреНрдпрд╛ drawer рдмрджреНрджрд▓ рдЖрд╣реЗ рддреЗ рдард░рд╡рддрд╛рдд.
  2. рддрдкрд╛рд╕рдгреНрдпрд╛ тЬЕ тАФ validate_student() (form, рдзрдбрд╛ 04) рдЖрдгрд┐ authorized() (pass, рдзрдбрд╛ 06). рдХреЛрдгрддреЗрд╣реА рдХрд╛рдо рдХрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рдЖрдзреА рдирдореНрд░рдкрдгреЗ рдирдХрд╛рд░ рджреНрдпрд╛.
  3. Archive ЁЯЧДя╕П тАФ STUDENTS, рдПрдХ dict. рдЖрдЬ рддреЗ memory рдордзреНрдпреЗ рдЖрд╣реЗ; Database school рддреЗ рдЯрд┐рдХрд╛рдК рдмрдирд╡рддреЗ. Counter рд▓рд╛ рддреНрдпрд╛рдЪреА рдкрд░реНрд╡рд╛ рдирд╕рддреЗ.
  4. Plumbing ЁЯФз тАФ send() рдкреНрд░рддреНрдпреЗрдХ рд╢рд┐рдХреНрдХрд╛ рддреНрдпрд╛рдЪ headers рд╕рд╣ рд▓рд┐рд╣рд┐рддреЗ (X-API-Version, X-Request-Id, Content-Type); error() рдкреНрд░рддреНрдпреЗрдХ рдирдХрд╛рд░ рдПрдХрд╛рдЪ рдЖрдХрд╛рд░рд╛рдд рд▓рд┐рд╣рд┐рддреЗ; log_line() рдкреНрд░рддреНрдпреЗрдХ slip рд╕рд╛рдареА рдПрдХ рдУрд│ рдЫрд╛рдкрддреЗ; rate_limited() рдореНрд╣рдгрдЬреЗ рд░рд╛рдВрдЧ (рдзрдбрд╛ 10).

рд╣рд╛рдЪ server рдЖрд╣реЗ. Frameworks рддреБрдореНрд╣рд╛рд▓рд╛ decorators, typed models, docs generators рдЖрдгрд┐ async рджреЗрддрд╛рдд тАФ рдЦрд▒реНрдпрд╛ рдХрд╛рдорд╛рд╕рд╛рдареА рддреЗ рд╡рд╛рдкрд░рд╛ тАФ рдкрдг рддреЗ рд╣реЗрдЪ рдЪрд╛рд░ рднрд╛рдЧ automate рдХрд░рдд рдЕрд╕рддрд╛рдд, рдЖрдгрд┐ рдЖрддрд╛ рддреБрдореНрд╣реА рддреЗ рднрд╛рдЧ рдкрд╛рд╣рд┐рд▓реЗ рдЖрд╣реЗрдд.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart TB
    subgraph file["api/school_api.py тАФ the four parts"]
        r["1 ЁЯзн routes: route() + do_GET/POST/PUT/DELETE"]
        c["2 тЬЕ checks: validate_student() ┬╖ authorized()"]
        s["3 ЁЯЧДя╕П archive: STUDENTS (a dict тАФ for now)"]
        p["4 ЁЯФз plumbing: send() ┬╖ error() ┬╖ log_line() ┬╖ rate_limited()"]
    end
    client["ЁЯзС any HTTP client"]
    client <-->|"HTTP"| p
    p --> r --> c --> s

ЁЯУЦ рдпрд╛ рдУрд│реА рд╡рд╛рдЪрд╛ (рдореНрд╣рдгрдЬреЗ рддреБрдореНрд╣реА рд╣рд░рд╡рдгрд╛рд░ рдирд╛рд╣реА)

тЭУ рдХрд╛рдп (рдЪреЛрд░рдгреНрдпрд╛рд╕рд╛рд░рдЦреЗ рддрдкрд╢реАрд▓)

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг "рдЖрдореНрд╣рд╛рд▓рд╛ X рд╕рд╛рдареА API рд╣рд╡реЗ" рд╣реЗ рдкреНрд░рддреНрдпреЗрдХ team рдордзрд▓реЗ sprint ticket рдЕрд╕рддреЗ, рдЖрдгрд┐ рдЖрддрд╛ рддреБрдореНрд╣реА рддреНрдпрд╛рдЪрд╛ рдкреНрд░рд╛рдорд╛рдгрд┐рдХ рдЕрдВрджрд╛рдЬ рд▓рд╛рд╡реВ рд╢рдХрддрд╛: plumbing рдПрдХрд╛ рджрд┐рд╡рд╕рд╛рдЪреЗ рдХрд╛рдо (framework рдиреЗ рддреЛ рдПрдХ рддрд╛рд╕ рд╣реЛрддреЛ); рдЦрд░реЗ рдХрд╛рдо рдореНрд╣рдгрдЬреЗ contract тАФ рдирд╛рдореЗ, forms, error рдЪрд╛ рдЖрдХрд╛рд░, auth model. рдЬреНрдпрд╛ teams рд╣реЗ рдмрд░реЛрдмрд░ рдХрд░рддрд╛рдд рддреНрдпрд╛ рдЕрд╕реЗ counters ship рдХрд░рддрд╛рдд рдЬреНрдпрд╛рдВрдирд╛ call рдХрд░рд╛рдпрд▓рд╛ рдЗрддрд░ teams рдирд╛ рдЖрд╡рдбрддреЗ.

ЁЯФз рдХрд╕реЗ + ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛ тАФ рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдврд╡рд╛

# 1) prove the baseline:
python3 api/school_api.py & sleep 1; bash api/smoke_test.sh | head -20
# 2) YOUR first endpoint тАФ the homework cabinet. In school_api.py:
#    HOMEWORK = []                                      (part 3, next to STUDENTS)
#    in do_GET:  if p == ["homework"]: return self.send(200, {"items": HOMEWORK})
#    in do_POST: if p == ["homework"]: (authorized? read_json? require "title") тЖТ HOMEWORK.append(...); 201
# 3) restart and call it:
curl -s http://127.0.0.1:8080/v1/homework
curl -s -X POST http://127.0.0.1:8080/v1/homework -H 'X-API-Key: hall-pass-123' -H 'Content-Type: application/json' -d '{"title":"read lesson 06"}'
# 4) add the two operations to api/openapi.yaml тАФ the catalogue must not lie

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

GET /v1/homework {"items": []} рдкрд░рдд рджреЗрддреЗ, POST рддреБрдордЪреНрдпрд╛ item рд╕рд╣ 201 рджреЗрддреЗ, рдЖрдгрд┐ рджреБрд╕рд░рд╛ GET рддреНрдпрд╛рдЪреА рдпрд╛рджреА рджрд╛рдЦрд╡рддреЛ. Key рд╢рд┐рд╡рд╛рдпрдЪрд╛ POST 401 рдкрд░рдд рджреЗрддреЛ тАФ рдХрд╛рд░рдг рддреБрдореНрд╣реА authorized() рдкреБрдиреНрд╣рд╛ рд╡рд╛рдкрд░рд▓реЗ. Smoke test рдЕрдЬреВрдирд╣реА pass рд╣реЛрддреЛ: рддреБрдореНрд╣реА рдмрд╛рдХреАрдЪреНрдпрд╛рдВрдирд╛ рд╣рд╛рдд рди рд▓рд╛рд╡рддрд╛ рдПрдХ рдХрдкрд╛рдЯ рдЬреЛрдбрд▓реЗ.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреБрдореНрд╣реА рдЦрд░реЗ API рдЪрд╛рд░рд╣реА рднрд╛рдЧрд╛рдВрддреВрди рд╡рд╛рдврд╡рд▓реЗ тАФ route, check, store, plumbing тАФ рдЖрдгрд┐ рдЖрдзреАрдЪреЗ contract рдЪрд╛рд▓реВрдЪ рд░рд╛рд╣рд┐рд▓реЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: frameworks рд╣реЗ рднрд╛рдЧ decorators рдорд╛рдЧреЗ рд▓рдкрд╡рддрд╛рдд, рдЬреЗ рдкрд╣рд╛рдЯреЗ 3 рд╡рд╛рдЬрддрд╛ рдХрд╛рд╣реАрддрд░реА рдмрд┐рдШрдбреЗрдкрд░реНрдпрдВрдд рдЫрд╛рди рдЕрд╕рддреЗ. рдЪрд╛рд░ рднрд╛рдЧ рдорд╛рд╣реАрдд рдЕрд╕рд▓реЗрд▓реЗ engineers framework рдЪреЗ stack traces рдирдХрд╛рд╢рд╛рд╕рд╛рд░рдЦреЗ рд╡рд╛рдЪрддрд╛рдд; рдмрд╛рдХреАрдЪреНрдпрд╛рдВрдирд╛ рддреЗ рдлрдХреНрдд рдЧреЛрдВрдЧрд╛рдЯ рд╡рд╛рдЯрддреЛ.

тПня╕П рдкреБрдвреЗ

рдХреЛрдгрддреНрдпрд╛ slips рдХреЛрдг рджреЗрдК рд╢рдХрддреЛ? Auth тАФ keys, tokens, OAuth, рдЖрдгрд┐ writes рд╕рд╛рдареА рд╣реЙрд▓ рдкрд╛рд╕.

git checkout lesson-06-auth

ЁЯФм Lesson 05 тАФ Build an API: the counter in ~235 honest lines

ЁЯУН You are here: Lesson 05 of 12 ┬╖ Previous: lesson-04-json-and-schemas ┬╖ Next: lesson-06-auth


ЁЯУж What's in this branch

Lessons 01тАУ04, plus the guided read of the real counter тАФ api/school_api.py тАФ and your first extension to it.

ЁЯзТ Explain like I'm 5

Building a counter sounds like a framework's job, until you see one opened up. Ours has exactly four parts, top to bottom of the file:

  1. The desk map ЁЯзн тАФ routes: route() turns /v1/students/2 into ['students', '2'], and do_GET / do_POST / do_PUT / do_DELETE decide which drawer the slip is about.
  2. The checks тЬЕ тАФ validate_student() (the form, lesson 04) and authorized() (the pass, lesson 06). Refuse politely before doing any work.
  3. The archive ЁЯЧДя╕П тАФ STUDENTS, a dict. Today it is memory; the Database school makes it durable. The counter does not care.
  4. The plumbing ЁЯФз тАФ send() writes every stamp with the same headers (X-API-Version, X-Request-Id, Content-Type); error() writes every refusal in one shape; log_line() prints one line per slip; rate_limited() is the queue (lesson 10).

That is a server. Frameworks give you decorators, typed models, docs generators and async тАФ use them for real work тАФ but they are automating these four parts, and now you have seen the parts.

ЁЯЧ║я╕П Diagram

flowchart TB
    subgraph file["api/school_api.py тАФ the four parts"]
        r["1 ЁЯзн routes: route() + do_GET/POST/PUT/DELETE"]
        c["2 тЬЕ checks: validate_student() ┬╖ authorized()"]
        s["3 ЁЯЧДя╕П archive: STUDENTS (a dict тАФ for now)"]
        p["4 ЁЯФз plumbing: send() ┬╖ error() ┬╖ log_line() ┬╖ rate_limited()"]
    end
    client["ЁЯзС any HTTP client"]
    client <-->|"HTTP"| p
    p --> r --> c --> s

ЁЯУЦ Read these lines (so you don't get lost)

тЭУ What (details worth stealing)

ЁЯдФ Why

Because "we need an API for X" is a sprint ticket in every team, and you can now estimate it honestly: the plumbing is a day (a framework makes it an hour); the real work is the contract тАФ the nouns, the forms, the error shape, the auth model. Teams that get those right ship counters other teams enjoy calling.

ЁЯФз How + ЁЯзк Try it тАФ extend it for real

# 1) prove the baseline:
python3 api/school_api.py & sleep 1; bash api/smoke_test.sh | head -20
# 2) YOUR first endpoint тАФ the homework cabinet. In school_api.py:
#    HOMEWORK = []                                      (part 3, next to STUDENTS)
#    in do_GET:  if p == ["homework"]: return self.send(200, {"items": HOMEWORK})
#    in do_POST: if p == ["homework"]: (authorized? read_json? require "title") тЖТ HOMEWORK.append(...); 201
# 3) restart and call it:
curl -s http://127.0.0.1:8080/v1/homework
curl -s -X POST http://127.0.0.1:8080/v1/homework -H 'X-API-Key: hall-pass-123' -H 'Content-Type: application/json' -d '{"title":"read lesson 06"}'
# 4) add the two operations to api/openapi.yaml тАФ the catalogue must not lie

тЬЕ Verify тАФ what you should see

GET /v1/homework returns {"items": []}, the POST returns 201 with your item, and a second GET lists it. A POST without the key returns 401 тАФ because you reused authorized(). The smoke test still passes: you added a cabinet without touching the others.

ЁЯПБ What you just proved

You extended a real API through all four parts тАФ route, check, store, plumbing тАФ and the existing contract kept working.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: frameworks hide these parts behind decorators, which is wonderful until something is wrong at 3 AM. Engineers who know the four parts read framework stack traces as a map; the others read them as noise.

тПня╕П Next

Who may hand in which slips? Auth тАФ keys, tokens, OAuth, and the hall pass for writes.

git checkout lesson-06-auth
тЖР Previousjson and schemasNext тЖТauth

This page is the lesson's README from the lesson-05-build-an-api branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.