Master

Build the small server

I only ever called APIs, never made one. A server is a function from a request to a response. Build one and the whole contract stops being mysterious.

What you will see

404

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Build one endpoint end to end and read its status map.

1

Define a GET endpoint that returns a record. The framework turns the dict into a 200 JSON response.

@app.get('/users/1') def one(): return {'id': 1}

Before you run it, say out loud what it will print. Then open the answer.

What it prints
{"id": 1}
2

Call it. The status is 200 and the body is the data.

curl -s -o /dev/null -w "%{http_code}" https://my.site/users/1

Before you run it, say out loud what it will print. Then open the answer.

What it prints
200
3

A path with no route returns 404. That is the framework's verdict, not your handler.

curl -s -o /dev/null -w "%{http_code}" https://my.site/nope

Before you run it, say out loud what it will print. Then open the answer.

What it prints
404
A 404 means no route matched, a 500 means a route matched and crashed. Those two numbers point at different fixes: a missing route versus a broken handler. Read the number before you read the code.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A route matches a path and runs a handler; the handler verifies the input, performs one action, and returns data or aborts with a status the caller reads.
1 Route matches path
↓
2 Handler verifies input
↓
3 Handler does one action
↓
4 Return data or abort with status
Why this works
Every endpoint is check-act-answer

An endpoint verifies its input (auth, signature, schema), performs exactly one action, and answers with a status and body. Keep that shape and an API is predictable. Skip the check and you have an endpoint that acts on untrusted input.

Design the status first

Before coding, decide what each failure returns: 400 for a bad body, 404 for a missing path, 409 for a conflict, 422 for a well-formed but wrong one. The status map is the contract; the code just fills it.

Examples

Route to handler

@app.get('/users/<int:i>')
def one(i):
    return {'id': i}

A route matches a path and runs a function. The function returns data; the framework turns it into a response. That is the whole server.

Answer with a status

if not valid:
    abort(400, 'bad body')

Return data for 200, abort with a code for the rest. The status you choose is the verdict the caller reads, so choose it deliberately.

The whole loop

@app.post('/charge')
def charge(req):
    if not verify(req.raw, sig): abort(403)
    return make(req.json)

Verify, act, return. Every endpoint is that shape: check the input, do one thing, answer with a status.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Reading it as it arrives
Previous Lesson
Reading it as it arrives