Framework versus server
Sanic calls itself both a web framework and a web server. What does this mean? And more importantly, why is this important? Before we can explore this, we must understand what these terms mean, and why they exist.
Web server
A web server is a piece of software that is designed to deliver documents and data via the HTTP protocol. Its function is to accept an incoming HTTP request, decode the message to understand what the request is trying to accomplish, and deliver an appropriate response. The language of web servers is the HTTP protocol.
We will get into the specifics later, but for now, we will set up a simple Sanic server, issue a request from curl
, and look at the message:
- Create a file called
server.py
and run it in your Terminal:from sanic import Sanic, text, Request app = Sanic(__name__) @app.post("/") async def handler(request: Request): message = ( request.head + b"\n\n" + request.body ).decode("utf-8") print(message) return text("Done") app.run(port=9999, debug=True)
- Now, we will send a request to our API:
$ curl localhost:9999 -d '{"foo": "bar"}'
In our console, we should see the following HTTP request message:
POST / HTTP/1.1 Host: localhost:9999 User-Agent: curl/7.76.1 Accept: */* Content-Length: 14 Content-Type: application/x-www-form-urlencoded {"foo": "bar"}
What we can see here is three components:
- The first line contains the HTTP method, the path, and the HTTP protocol that's being used.
- Next is a list of HTTP headers, one per line in
key: value
format. - Last is the HTTP body, preceded by a blank line.
HTTP responses are very similar:
HTTP/1.1 200 OK content-length: 4 connection: keep-alive content-type: text/plain; charset=utf-8 Done
The three components are now as follows:
- The first line contains the HTTP protocol, followed by the HTTP status, and then a status description.
- Next is a list of HTTP headers, one per line in
key: value
format. - Last is the HTTP body (if there is one), preceded by a blank line.
Though this is the language of web servers, it is very cumbersome to write all of that. This is why tools such as web browsers and HTTP client libraries were created—to build and parse these messages for us. Next we will explore how servers deal with this same problem, and how web frameworks solve it.
Web framework
We could, of course, write a program in Python that receives these raw HTTP messages, decodes them, and returns an appropriate HTTP response message. However, this would require a lot of boilerplate, be difficult to scale, and be prone to mistakes.
Certain tools do this for us: web frameworks. The job of a web framework is to build the HTTP message and handle the request appropriately. Many frameworks go further by providing conveniences and utilities to make the process simpler.
There are many web frameworks in the Python ecosystem that do this work to varying degrees. Some provide a huge number of features, while some are very sparse in terms of what they offer. Some are very strict, while some are more open. Sanic tries to fall on the continuum of being feature-rich, but only so far as what's required to not get in the way of the developer.
One of the features that Sanic provides is that it is both a web framework and a web server.
If you survey the web frameworks on PyPI, you will find that most of them require a separate web server to be installed. When it comes to deploying most Python applications, there is a hard line between the persistent operation that runs on the machine and the tooling that's used to develop response handlers. We will not delve too deeply into WSGI since it doesn't apply to Sanic. However, the paradigm that there is a server that calls a single input function, passes information about the request, and then expects a response is important to understand. Everything that happens in-between is the framework.
Narrowing our focus to projects that support async/await style coroutine handlers, the vast majority require you to run an ASGI server. It follows a similar pattern: an ASGI-ready server calls into an ASGI ready framework. These two components operate with one another using a specific protocol. There are currently three popular ASGI servers: uvicorn, hypercorn, and daphne.
Precisely because Sanic was born during the era that predated ASGI, it needed a server. Over time, this has become one of its greatest assets and is in large part why it outperforms most other Python frameworks. Development of the Sanic server is hyper-focused on performance and minimizing the request/response cycle. However, in recent years, Sanic has also adopted an ASGI interface to allow it to be run by an ASGI web server.
However, for the majority of this book, you can assume that when we are talking about running Sanic, we mean using the internal web server. It is production-ready and remains one of the best methods for deploying Sanic. Later, in Chapter 8, Running a Sanic Server, we will discuss all of the potential choices and help you come up with the questions to ask when you're deciding which solution is obvious for your needs. Now that you know the what of Sanic, we turn to the why.