Skip to content

Examples

Adding POST Functionality

If we wanted to add a second handler to enable users to post data to our simple server, you might implement something similar to this:

Simple Web App with POST

from sanic import Sanic
from sanic.response import text

app = Sanic("hello-world-app")

name = "Dave"


@app.get("/")
async def hello(request):
    return text(f"Hello, {name}!\n")


@app.route("/", methods=["POST"])
async def hello_post(request):
    name = request.json['name']
    return text("Success\n")

Let's then check what happens:

% curl http://localhost:8000
Hello, Dave!

% curl http://localhost:8000/ -d '{"name": "Tim"}'
Success

% curl http://localhost:8000
Hello, Dave!

You might be confused to see that the name that is greeted hasn't changed! This is because all of the handlers are initiased with variables when the server is first launched, and so our GET at http://localhost:8000/ never looks outside its handler to see if anything has changed.

The solution to this problem is to store a variable that could change in our app's context: app.ctx.name = "...". If you make the changes below and then re-run the curl statements, you should see that the app now changes the name it greets.

Simple Web App with POST...Fixed!

from sanic import Sanic
from sanic.response import text, HTTPResponse
from sanic.request import Request

app = Sanic("hello-world-app")

app.ctx.name = "Dave"


@app.get("/")
async def hello(request: Request) -> HTTPResponse:
    return text(f"Hello, {app.ctx.name}!\n")


@app.route("/", methods=["POST"])
async def hello_post(request: Request) -> HTTPResponse:
    app.ctx.name = request.json['name']
    return text("Success\n")

Annotating Your Code

It is important to type-annotate your code to catch type bugs before release, and for better legibility. Here is a copy of the simple web app with type-hinting:

Simple Web App with Type Hinting

from sanic import Sanic
from sanic.response import text, HTTPResponse
from sanic.request import Request

app = Sanic("hello-world-app")

@app.get("/")
async def hello(request: Request) -> HTTPResponse:
    return text(f"Hello, world!")

Testing Sanic Applications

Sanic Testing is a testing client for Sanic. It primarily powers the tests of the Sanic project itself. It can be installed running:

pip install sanic-testing

Once sanic-testing is in the environment, there is nothing you need to do to use it.

Sync Test

Simply access the test_client property on your application instance:

import pytest
from sanic import Sanic, response

@pytest.fixture
def app():
    sanic_app = Sanic("TestSanic")

    @sanic_app.get("/")
    def basic(request):
        return response.text("foo")

    return sanic_app

def test_basic_test_client(app):
    request, response = app.test_client.get("/")

    assert request.method.lower() == "get"
    assert response.body == b"foo"
    assert response.status == 200

Async Test

The async test client in pytest needs to be installed:

pip install pytest-asyncio

Async tests require accessing the asgi_client property on your application instance.

import pytest
from sanic import Sanic, response

@pytest.fixture
def app():
    sanic_app = Sanic(__name__)

    @sanic_app.get("/")
    def basic(request):
        return response.text("foo")

    return sanic_app

@pytest.mark.asyncio
async def test_basic_asgi_client(app):
    request, response = await app.asgi_client.get("/")

    assert request.method.lower() == "get"
    assert response.body == b"foo"
    assert response.status == 200

Further Reading

For further information on Sanic, please refer to their documentation.