DEV Community

Cover image for FastAPI + Uvicorn = Blazing Speed: The Tech Behind the Hype
Leapcell
Leapcell

Posted on

FastAPI + Uvicorn = Blazing Speed: The Tech Behind the Hype

Image description

What is Uvicorn?

Image description

Answer: Uvicorn is a very fast ASGI (Asynchronous Server Gateway Interface) server built on uvloop and httptools. It is a lightweight and efficient web server framework developed based on asyncio.
Uvicorn was initially designed to achieve two goals:

  • To implement an extremely fast asyncio server using uvloop and httptools.
  • To implement a minimal application interface based on ASGI. It currently supports http, websockets, Pub/Sub broadcasts, and can be extended to other protocols and message types. Official website: uvicorn (https://uvicorn.org/)

What are uvloop and httptools?

Answer: uvloop is used to replace the event loop in the standard library asyncio. It is implemented with Cython and is very fast, which can increase the speed of asyncio by 2 - 4 times. I assume you are familiar with asyncio, as it is essential for writing asynchronous code.
httptools is a Python implementation of the Node.js HTTP parser.

What is an ASGI server?

Answer: ASGI, the Asynchronous Server Gateway Interface, is a standard interface between network protocol services and Python applications. It can handle multiple common protocol types, including HTTP, HTTP2, and WebSocket.
ASGI protocol: https://asgi.readthedocs.io/en/latest/specs/main.html

Briefly introduce Uvicorn

Answer: Currently, Python lacks an asynchronous gateway protocol interface. The emergence of ASGI fills this gap. From now on, we can use a common standard to implement tools for all asynchronous frameworks. ASGI helps Python compete with Node.JS and Golang in web frameworks, aiming to achieve high-performance IO-intensive tasks. ASGI supports HTTP2 and WebSockets, while WSGI does not.
Uvicorn currently supports HTTP1.1 and WebSocket, and plans to support HTTP2.

Uvicorn Usage

  • Installation
    Run pip install uvicorn

  • Create a new example.py file

async def app(scope, receive, send):
    assert scope['type'] == 'http'
    await send({
        'type': 'http.response.start',
       'status': 200,
        'headers': [
            [b'content-type', b'text/plain'],
        ]
    })
    await send({
        'type': 'http.response.body',
        'body': b'Hello, world!',
    })
Enter fullscreen mode Exit fullscreen mode
  • Start uvicorn from the command line
    Run uvicorn example:app

  • Start in script form

import uvicorn

async def app(scope, receive, send):
   ...

if __name__ == "__main__":
    uvicorn.run("example:app", host="127.0.0.1", port=8000, log_level="info")
Enter fullscreen mode Exit fullscreen mode

Uvicorn supports multiple commands, which can be viewed with uvicorn --help.

➜  ~ uvicorn --help
Usage: uvicorn [OPTIONS] APP

Options:
  --host TEXT                     Bind socket to this host.  [default:
                                  127.0.0.1]
  --port INTEGER                  Bind socket to this port. If 0, an available
                                  port will be picked.  [default: 8000]
  --uds TEXT                      Bind to a UNIX domain socket.
  --fd INTEGER                    Bind to socket from this file descriptor.
  --reload                        Enable auto-reload.
  --reload-dir PATH               Set reload directories explicitly, instead
                                  of using the current working directory.
  --reload-include TEXT           Set glob patterns to include while watching
                                  for files. Includes '*.py' by default; these
                                  defaults can be overridden with `--reload-
                                  exclude`. This option has no effect unless
                                  watchfiles is installed.
  --reload-exclude TEXT           Set glob patterns to exclude while watching
                                  for files. Includes '.*,.py[cod],.sw.*,
                                  ~*' by default; these defaults can be
                                  overridden with `--reload-include`. This
                                  option has no effect unless watchfiles is
                                  installed.
  --reload-delay FLOAT            Delay between previous and next check if
                                  application needs to be. Defaults to 0.25s.
                                  [default: 0.25]
  --workers INTEGER               Number of worker processes. Defaults to the
                                  $WEB_CONCURRENCY environment variable if
                                  available, or 1. Not valid with --reload.
  --loop [auto|asyncio|uvloop]    Event loop implementation.  [default: auto]
  --http [auto|h11|httptools]     HTTP protocol implementation.  [default:
                                  auto]
  --ws [auto|none|websockets|wsproto]
                                  WebSocket protocol implementation.
                                  [default: auto]
  --ws-max-size INTEGER           WebSocket max size message in bytes
                                  [default: 16777216]
  --ws-max-queue INTEGER          The maximum length of the WebSocket message
                                  queue.  [default: 32]
  --ws-ping-interval FLOAT        WebSocket ping interval in seconds.
                                  [default: 20.0]
  --ws-ping-timeout FLOAT         WebSocket ping timeout in seconds.
                                  [default: 20.0]
  --ws-per-message-deflate BOOLEAN
                                  WebSocket per-message-deflate compression
                                  [default: True]
  --lifespan [auto|on|off]        Lifespan implementation.  [default: auto]
  --interface [auto|asgi3|asgi2|wsgi]
                                  Select ASGI3, ASGI2, or WSGI as the
                                  application interface.  [default: auto]
  --env-file PATH                 Environment configuration file.
  --log-config PATH               Logging configuration file. Supported
                                  formats:.ini,.json,.yaml.
  --log-level [critical|error|warning|info|debug|trace]
                                  Log level. [default: info]
  --access-log / --no-access-log  Enable/Disable access log.
  --use-colors / --no-use-colors  Enable/Disable colorized logging.
  --proxy-headers / --no-proxy-headers
                                  Enable/Disable X-Forwarded-Proto,
                                  X-Forwarded-For, X-Forwarded-Port to
                                  populate remote address info.
  --server-header / --no-server-header
                                  Enable/Disable default Server header.
  --date-header / --no-date-header
                                  Enable/Disable default Date header.
  --forwarded-allow-ips TEXT      Comma separated list of IPs to trust with
                                  proxy headers. Defaults to the
                                  $FORWARDED_ALLOW_IPS environment variable if
                                  available, or '127.0.0.1'.
  --root-path TEXT                Set the ASGI 'root_path' for applications
                                  submounted below a given URL path.
  --limit-concurrency INTEGER     Maximum number of concurrent connections or
                                  tasks to allow, before issuing HTTP 503
                                  responses.
  --backlog INTEGER               Maximum number of connections to hold in
                                  backlog
  --limit-max-requests INTEGER    Maximum number of requests to service before
                                  terminating the process.
  --timeout-keep-alive INTEGER    Close Keep-Alive connections if no new data
                                  is received within this timeout.  [default:
                                  5]
  --timeout-graceful-shutdown INTEGER
                                  Maximum number of seconds to wait for
                                  graceful shutdown.
  --ssl-keyfile TEXT              SSL key file
  --ssl-certfile TEXT             SSL certificate file
  --ssl-keyfile-password TEXT     SSL keyfile password
  --ssl-version INTEGER           SSL version to use (see stdlib ssl module's)
                                  [default: 17]
  --ssl-cert-reqs INTEGER         Whether client certificate is required (see
                                  stdlib ssl module's)  [default: 0]
  --ssl-ca-certs TEXT             CA certificates file
  --ssl-ciphers TEXT              Ciphers to use (see stdlib ssl module's)
                                  [default: TLSv1]
  --header TEXT                   Specify custom default HTTP response headers
                                  as a Name:Value pair
  --version                       Display the uvicorn version and exit.
  --app-dir TEXT                  Look for APP in the specified directory, by
                                  adding this to the PYTHONPATH. Defaults to
                                  the current working directory.
  --h11-max-incomplete-event-size INTEGER
                                  For h11, the maximum number of bytes to
                                  buffer of an incomplete event.
  --factory                       Treat APP as an application factory, i.e. a
                                  () -> <ASGI app> callable.
  --help                          Show this message and exit.
➜  ~
Enter fullscreen mode Exit fullscreen mode

Config and Server Instances

To have better control over the configuration and server lifecycle, use uvicorn.Config and uvicorn.Server:

import uvicorn

async def app(scope, receive, send):
   ...

if __name__ == "__main__":
    config = uvicorn.Config("main:app", port=5000, log_level="info")
    server = uvicorn.Server(config)
    server.run()
Enter fullscreen mode Exit fullscreen mode

If you want to run Uvicorn from an already running asynchronous environment, use uvicorn.Server.serve() instead:

import asyncio
import uvicorn

async def app(scope, receive, send):
   ...

async def main():
    config = uvicorn.Config("main:app", port=5000, log_level="info")
    server = uvicorn.Server(config)
    await server.serve()

if __name__ == "__main__":
    asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

Starting a FastAPI Project with Uvicorn

import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

if __name__ == '__main__':
    uvicorn.run(app=app)
Enter fullscreen mode Exit fullscreen mode

Why does FastAPI use Uvicorn?

FastAPI is a modern, high-performance web framework. It uses the asynchronous programming features of Python to improve the performance of web applications. Uvicorn, on the other hand, is a high-performance ASGI server implemented with uvloop and httptools, which can handle HTTP requests asynchronously. FastAPI uses Uvicorn as its default web server because Uvicorn is very fast, reliable, and easy to use. It can remain stable and efficient when handling a large number of concurrent connections. In addition, Uvicorn supports new features such as WebSocket and HTTP/2, which align with the modern web development philosophy advocated by FastAPI. Therefore, using Uvicorn as the web server for FastAPI is an excellent choice.

Leapcell: The Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis

Image description

Finally, let me introduce the platform that is most suitable for deploying FastAPI services: Leapcell.

Leapcell has the following features:

  • 1. Multi-Language Support
    Develop with JavaScript, Python, Go, or Rust.

  • 2. Deploy unlimited projects for free
    Pay only for usage — no requests, no charges.

  • 3. Unbeatable Cost Efficiency
    Pay-as-you-go with no idle charges.

    Example: $25 supports 6.94M requests at a 60ms average response time.

  • 4. Streamlined Developer Experience
    Intuitive UI for effortless setup.

    Fully automated CI/CD pipelines and GitOps integration.

    Real-time metrics and logging for actionable insights.

  • 5. Effortless Scalability and High Performance
    Auto-scaling to handle high concurrency with ease.

    Zero operational overhead — just focus on building.

Image description

Explore more in the documentation!

Leapcell Twitter: https://x.com/LeapcellHQ

Top comments (0)