Почему pymysql выдает Already Closed?
В питоне блок with обычно используется для освобождения некоторого ресурса независимо от исхода выполнения кода в блоке with (нормальное выполнение, исключение, ранний return).
В случае с соединением с СУБД, with self.connection: освобождает ресурс, т.е. закрывает это соединение, по завершению блока with. Если тебе нужно, чтобы соединение не закрывалось, не используй with.

Я подозреваю, что ты написал with "потому что в туториале так написано".
в конструкторе создаются объекты подключения и курсора
self.connection = pymysql.connect(host='localhost', port=3306, user='root', password='',
                                          db='aiodb')
        self.cursor = self.connection.cursor()

а в блоке выполнения кода, ты используешь with, открывая заново соединение но без курсора.
можно попробовать типа такого
class Database:
    def __init__(self, config):
        self.config = config
        self.connection = pymysql.connect(**self.config)

    def execute(self, query: str, params: tuple = None):
        try:
            with self.connection as connect:
                cursor = connect.cursor()
                if params is not None:
                    cursor.execute(query, params)
                else:
                    cursor.execute(query)
                return cursor.fetchall()
        except OperationalError as Error:
            print(Error)

обработку типа есть юзер или нет лучше отдельным методом