Combining FastAPI and discord.py

Maku
4 min readJan 16, 2021

--

How to add a super simple webserver to your discord bot.

Ever thought about receiving HTTP requests with your discord bot but you weren’t sure how do go about it? I faced this same issue a few days ago and figured I’d give it a try with FastAPI!

You can chose any web framework, but it has to be async. There are a lot of alternatives to FastAPI: Quart, Sanic, Starlette and so on. You can probably find more in the awesome-asyncio list.
(If you unsure what async means you should probably visit a tutorial like this before continuing).

FastAPI

Let’s start by looking at a basic example for FastAPI
Install fastapi and uvicorn with pip install fastapi uvicorn

uvicorn is the ASGI server we’ll use together with FastAPI.

Then create a python file. You can call it whatever you want but I’ll call it bot.py.

That’s it. Now simply run it with uvicorn bot:app and you should be able to visit http://127.0.0.1:8000/ in your browser and see {"hello": "world"}.

discord.py bot

Next step, a basic discord.py bot!
Install discord.py with pip install discord.py

You will need to create an application on discord.com/developers and add a bot to it, from there copy the token and replace it in bot.run("token"). You can find more details on this in the “Creating a Bot Account” page of the discord.py docs.

Once you replaced your token you can run your bot with python bot.py

Two in one?

Now on to the exciting part. How do we combine these two examples 🤔

That was the easy part, but how do we run either of the two now?
Let’s say we add bot.run("token") at the end and run the bot with uvicorn bot:app

This event loop is already running… From the traceback we see that bot.run tries to loop.run_forever(). Which then yells at us that the event loop is already running since.. it was started by uvicorn. So what now?

Looking at the discord.py docs we see that bot.run initialises our event loop by roughly doing something like this:

Since our loop is already running we can just use bot.start directly I suppose?

Let’s try adding the following to our code from earlier:

Okay… but how do we run this in our code?

The easiest way to run async functions (or rather coroutines) from sync context is by creating a task. This can be done with
asyncio.create_task(run()) (you will need to import asyncio at the top, it’s a standard library).

Let’s recap what we have so far

That’s it! 🎉 Both FastAPI and discord.py should now be running at the same time. You can access http://127.0.0.1:8000/ and also run !welcome @maku

Additionally…

One amazing feature of uvicorn while developing is the --reload flag which gives you hot-reloading whenever a file change is detected.
If you wish to use a specific port you can do so through --port 8080
To access FastAPI from another computer you will have to host it on 0.0.0.0 instead of 127.0.0.1, this can be done through --host 0.0.0.0
My typical run command for uvicorn is:
uvicorn bot:app --reload --port 8080 --host 0.0.0.0

A feature I love from FastAPI is the automatic generation of documentation. If you access http://127.0.0.1:8000/docs you will find an easy to use, interactive OpenAPI page

There are tons more, so go and check out the FastAPI documentation! ⚡

--

--

Maku

Software Engineer who enjoys writing discord bots.