<h3 dir="auto">Front End</h3>
<p dir="auto">From the same page, if Connect button clicked multiple times, multiple WebSocket requests landing in the backend.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Chat&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;WebSocket Chat&lt;/h1&gt;
        &lt;form action=&quot;&quot; onsubmit=&quot;sendMessage(event)&quot;&gt;
            &lt;label&gt;Token: &lt;input type=&quot;text&quot; id=&quot;token&quot; autocomplete=&quot;off&quot; value=&quot;some-key-token&quot;/&gt;&lt;/label&gt;
            &lt;button onclick=&quot;connect(event)&quot;&gt;Connect&lt;/button&gt;
            &lt;hr&gt;
            &lt;!-- &lt;label&gt;Message: &lt;input type=&quot;text&quot; id=&quot;messageText&quot; autocomplete=&quot;off&quot;/&gt;&lt;/label&gt; --&gt;
            &lt;!-- &lt;button&gt;Send&lt;/button&gt; --&gt;
        &lt;/form&gt;
        &lt;ul id='messages'&gt;
        &lt;/ul&gt;
        &lt;script&gt;
        var ws = null;
            function connect(event) {
                var token = document.getElementById(&quot;token&quot;)
                ws = new WebSocket(&quot;ws://localhost:6003/ws/v1/inbox/test?token=&quot; + token.value);
				
				ws.onopen = function () {
				  console.log('socket opened'); 
				};
                ws.onmessage = function(event) {
                    var messages = document.getElementById('messages')
                    var message = document.createElement('li')
					var content = document.createTextNode(event.data)
                    &lt;!-- var data = document.createTextNode(event.data) --&gt;
					&lt;!-- var content = &quot;message:&quot; + data.message --&gt;
                    message.appendChild(content)
                    messages.appendChild(message)
                };
				
				ws.onclose = function(e) {  
				  console.log('socket closed from server'); 
				}

				ws.onerror = function(err) {
				  console.error(err)
				};
				
                event.preventDefault()
            }
            function sendMessage(event) {
                var input = document.getElementById(&quot;messageText&quot;)
                ws.send(input.value)
                input.value = ''
                event.preventDefault()
            }
        &lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;"><pre class="notranslate"><span class="pl-c1">&lt;</span>!<span class="pl-v">DOCTYPE</span> <span class="pl-s1">html</span><span class="pl-c1">&gt;</span>
<span class="pl-c1">&lt;</span><span class="pl-s1">html</span><span class="pl-c1">&gt;</span>
    <span class="pl-c1">&lt;</span><span class="pl-s1">head</span><span class="pl-c1">&gt;</span>
        <span class="pl-c1">&lt;</span><span class="pl-s1">title</span><span class="pl-c1">&gt;</span><span class="pl-v">Chat</span><span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">title</span><span class="pl-c1">&gt;</span>
    <span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">head</span><span class="pl-c1">&gt;</span>
    <span class="pl-c1">&lt;</span><span class="pl-s1">body</span><span class="pl-c1">&gt;</span>
        <span class="pl-c1">&lt;</span><span class="pl-s1">h1</span><span class="pl-c1">&gt;</span><span class="pl-v">WebSocket</span> <span class="pl-v">Chat</span><span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">h1</span><span class="pl-c1">&gt;</span>
        <span class="pl-c1">&lt;</span><span class="pl-s1">form</span> <span class="pl-s1">action</span><span class="pl-c1">=</span><span class="pl-s">""</span> <span class="pl-s1">onsubmit</span><span class="pl-c1">=</span><span class="pl-s">"sendMessage(event)"</span><span class="pl-c1">&gt;</span>
            <span class="pl-c1">&lt;</span><span class="pl-s1">label</span><span class="pl-c1">&gt;</span><span class="pl-v">Token</span>: <span class="pl-c1">&lt;</span><span class="pl-s1">input</span> <span class="pl-s1">type</span><span class="pl-c1">=</span><span class="pl-s">"text"</span> <span class="pl-s1">id</span><span class="pl-c1">=</span><span class="pl-s">"token"</span> <span class="pl-s1">autocomplete</span><span class="pl-c1">=</span><span class="pl-s">"off"</span> <span class="pl-s1">value</span><span class="pl-c1">=</span><span class="pl-s">"some-key-token"</span><span class="pl-c1">/</span><span class="pl-c1">&gt;</span><span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">label</span><span class="pl-c1">&gt;</span>
            <span class="pl-c1">&lt;</span><span class="pl-s1">button</span> <span class="pl-s1">onclick</span><span class="pl-c1">=</span><span class="pl-s">"connect(event)"</span><span class="pl-c1">&gt;</span><span class="pl-v">Connect</span><span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">button</span><span class="pl-c1">&gt;</span>
            <span class="pl-c1">&lt;</span><span class="pl-s1">hr</span><span class="pl-c1">&gt;</span>
            <span class="pl-c1">&lt;</span>!<span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-c1">&lt;</span><span class="pl-s1">label</span><span class="pl-c1">&gt;</span><span class="pl-v">Message</span>: <span class="pl-c1">&lt;</span><span class="pl-s1">input</span> <span class="pl-s1">type</span><span class="pl-c1">=</span><span class="pl-s">"text"</span> <span class="pl-s1">id</span><span class="pl-c1">=</span><span class="pl-s">"messageText"</span> <span class="pl-s1">autocomplete</span><span class="pl-c1">=</span><span class="pl-s">"off"</span><span class="pl-c1">/</span><span class="pl-c1">&gt;</span><span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">label</span><span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">-</span><span class="pl-c1">&gt;</span>
            <span class="pl-c1">&lt;</span>!<span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-c1">&lt;</span><span class="pl-s1">button</span><span class="pl-c1">&gt;</span><span class="pl-v">Send</span><span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">button</span><span class="pl-c1">&gt;</span> <span class="pl-c1">-</span><span class="pl-c1">-</span><span class="pl-c1">&gt;</span>
        <span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">form</span><span class="pl-c1">&gt;</span>
        <span class="pl-c1">&lt;</span><span class="pl-s1">ul</span> <span class="pl-s1">id</span><span class="pl-c1">=</span><span class="pl-s">'messages'</span><span class="pl-c1">&gt;</span>
        <span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">ul</span><span class="pl-c1">&gt;</span>
        <span class="pl-c1">&lt;</span><span class="pl-s1">script</span><span class="pl-c1">&gt;</span>
        <span class="pl-s1">var</span> <span class="pl-s1">ws</span> <span class="pl-c1">=</span> <span class="pl-s1">null</span>;
            <span class="pl-en">function</span> <span class="pl-s1">connect</span>(<span class="pl-s1">event</span>) {
                <span class="pl-s1">var</span> <span class="pl-s1">token</span> <span class="pl-c1">=</span> <span class="pl-s1">document</span>.<span class="pl-en">getElementById</span>(<span class="pl-s">"token"</span>)
                <span class="pl-s1">ws</span> <span class="pl-c1">=</span> <span class="pl-s1">new</span> <span class="pl-v">WebSocket</span>(<span class="pl-s">"ws://localhost:6003/ws/v1/inbox/test?token="</span> <span class="pl-c1">+</span> <span class="pl-s1">token</span>.<span class="pl-s1">value</span>);
				
				<span class="pl-s1">ws</span>.<span class="pl-s1">onopen</span> <span class="pl-c1">=</span> <span class="pl-en">function</span> () {
				  <span class="pl-s1">console</span>.<span class="pl-en">log</span>(<span class="pl-s">'socket opened'</span>); 
				};
                <span class="pl-s1">ws</span>.<span class="pl-s1">onmessage</span> <span class="pl-c1">=</span> <span class="pl-en">function</span>(<span class="pl-s1">event</span>) {
                    <span class="pl-s1">var</span> <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-s1">document</span>.<span class="pl-en">getElementById</span>(<span class="pl-s">'messages'</span>)
                    <span class="pl-s1">var</span> <span class="pl-s1">message</span> <span class="pl-c1">=</span> <span class="pl-s1">document</span>.<span class="pl-en">createElement</span>(<span class="pl-s">'li'</span>)
					<span class="pl-s1">var</span> <span class="pl-s1">content</span> <span class="pl-c1">=</span> <span class="pl-s1">document</span>.<span class="pl-en">createTextNode</span>(<span class="pl-s1">event</span>.<span class="pl-s1">data</span>)
                    <span class="pl-c1">&lt;</span>!<span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-s1">var</span> <span class="pl-s1">data</span> <span class="pl-c1">=</span> <span class="pl-s1">document</span>.<span class="pl-en">createTextNode</span>(<span class="pl-s1">event</span>.<span class="pl-s1">data</span>) <span class="pl-c1">-</span><span class="pl-c1">-</span><span class="pl-c1">&gt;</span>
					<span class="pl-c1">&lt;</span>!<span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-s1">var</span> <span class="pl-s1">content</span> <span class="pl-c1">=</span> <span class="pl-s">"message:"</span> <span class="pl-c1">+</span> <span class="pl-s1">data</span>.<span class="pl-s1">message</span> <span class="pl-c1">-</span><span class="pl-c1">-</span><span class="pl-c1">&gt;</span>
                    <span class="pl-s1">message</span>.<span class="pl-en">appendChild</span>(<span class="pl-s1">content</span>)
                    <span class="pl-s1">messages</span>.<span class="pl-en">appendChild</span>(<span class="pl-s1">message</span>)
                };
				
				<span class="pl-s1">ws</span>.<span class="pl-s1">onclose</span> <span class="pl-c1">=</span> <span class="pl-en">function</span>(<span class="pl-s1">e</span>) {  
				  <span class="pl-s1">console</span>.<span class="pl-en">log</span>(<span class="pl-s">'socket closed from server'</span>); 
				}

				<span class="pl-s1">ws</span>.<span class="pl-s1">onerror</span> <span class="pl-c1">=</span> <span class="pl-en">function</span>(<span class="pl-s1">err</span>) {
				  <span class="pl-s1">console</span>.<span class="pl-en">error</span>(<span class="pl-s1">err</span>)
				};
				
                <span class="pl-s1">event</span>.<span class="pl-en">preventDefault</span>()
            }
            <span class="pl-s1">function</span> <span class="pl-en">sendMessage</span>(<span class="pl-s1">event</span>) {
                <span class="pl-s1">var</span> <span class="pl-s1">input</span> <span class="pl-c1">=</span> <span class="pl-s1">document</span>.<span class="pl-en">getElementById</span>(<span class="pl-s">"messageText"</span>)
                <span class="pl-s1">ws</span>.<span class="pl-en">send</span>(<span class="pl-s1">input</span>.<span class="pl-s1">value</span>)
                <span class="pl-s1">input</span>.<span class="pl-s1">value</span> <span class="pl-c1">=</span> <span class="pl-s">''</span>
                <span class="pl-s1">event</span>.<span class="pl-en">preventDefault</span>()
            }
        <span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">script</span><span class="pl-c1">&gt;</span>
    <span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">body</span><span class="pl-c1">&gt;</span>
<span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-s1">html</span><span class="pl-c1">&gt;</span></pre></div>
<h3 dir="auto">Back End</h3>
<p dir="auto">Here we are adding incoming WebSockets to a list. Here we want to compare incoming WebSockets considering the request source. The important thing a user should be able to connect from different devices, browsers, or browser tabs.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from fastapi import FastAPI, Request, Depends, WebSocket, WebSocketDisconnect, Query

app = FastAPI()

class WebSocketConnectionModel:
    user_id: int
    account_id: int
    connection_date_time_utc: datetime
    socket: WebSocket


class SocketManager:
    def __init__(self):
        self.connections: List[WebSocketConnectionModel] = []

    def len(self):
        return len(self.connections)

    def connect(self, account_id: int, user_id: int, websocket: WebSocket):
        connection = WebSocketConnectionModel()
        connection.account_id = account_id
        connection.user_id = user_id
        connection.socket = websocket
        connection.connection_date_time_utc = datetime.utcnow()
        self.connections.append(connection)
        return connection

    def disconnect(self, item: WebSocketConnectionModel):
        self.connections.remove(item)



manager = SocketManager()

async def websocket_test(websocket: WebSocket, access_token: str = Query(..., alias=&quot;token&quot;)):
    user = get_current_user_from_query_token(access_token)
    await websocket.accept()
   #add connection to a list
    connection = manager.connect(user.account_id, user.id, websocket)
    try:
        while True:
            data = await websocket.receive_json()
    except WebSocketDisconnect:
        manager.disconnect(connection)

app.add_websocket_route(&quot;/ws/v1/inbox/test&quot;, facebook_inbox.websocket_test)
"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">fastapi</span> <span class="pl-k">import</span> <span class="pl-v">FastAPI</span>, <span class="pl-v">Request</span>, <span class="pl-v">Depends</span>, <span class="pl-v">WebSocket</span>, <span class="pl-v">WebSocketDisconnect</span>, <span class="pl-v">Query</span>

<span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-v">FastAPI</span>()

<span class="pl-k">class</span> <span class="pl-v">WebSocketConnectionModel</span>:
    <span class="pl-s1">user_id</span>: <span class="pl-s1">int</span>
    <span class="pl-s1">account_id</span>: <span class="pl-s1">int</span>
    <span class="pl-s1">connection_date_time_utc</span>: <span class="pl-s1">datetime</span>
    <span class="pl-s1">socket</span>: <span class="pl-v">WebSocket</span>


<span class="pl-k">class</span> <span class="pl-v">SocketManager</span>:
    <span class="pl-k">def</span> <span class="pl-en">__init__</span>(<span class="pl-s1">self</span>):
        <span class="pl-s1">self</span>.<span class="pl-s1">connections</span>: <span class="pl-v">List</span>[<span class="pl-v">WebSocketConnectionModel</span>] <span class="pl-c1">=</span> []

    <span class="pl-k">def</span> <span class="pl-en">len</span>(<span class="pl-s1">self</span>):
        <span class="pl-k">return</span> <span class="pl-en">len</span>(<span class="pl-s1">self</span>.<span class="pl-s1">connections</span>)

    <span class="pl-k">def</span> <span class="pl-en">connect</span>(<span class="pl-s1">self</span>, <span class="pl-s1">account_id</span>: <span class="pl-s1">int</span>, <span class="pl-s1">user_id</span>: <span class="pl-s1">int</span>, <span class="pl-s1">websocket</span>: <span class="pl-v">WebSocket</span>):
        <span class="pl-s1">connection</span> <span class="pl-c1">=</span> <span class="pl-v">WebSocketConnectionModel</span>()
        <span class="pl-s1">connection</span>.<span class="pl-s1">account_id</span> <span class="pl-c1">=</span> <span class="pl-s1">account_id</span>
        <span class="pl-s1">connection</span>.<span class="pl-s1">user_id</span> <span class="pl-c1">=</span> <span class="pl-s1">user_id</span>
        <span class="pl-s1">connection</span>.<span class="pl-s1">socket</span> <span class="pl-c1">=</span> <span class="pl-s1">websocket</span>
        <span class="pl-s1">connection</span>.<span class="pl-s1">connection_date_time_utc</span> <span class="pl-c1">=</span> <span class="pl-s1">datetime</span>.<span class="pl-en">utcnow</span>()
        <span class="pl-s1">self</span>.<span class="pl-s1">connections</span>.<span class="pl-en">append</span>(<span class="pl-s1">connection</span>)
        <span class="pl-k">return</span> <span class="pl-s1">connection</span>

    <span class="pl-k">def</span> <span class="pl-en">disconnect</span>(<span class="pl-s1">self</span>, <span class="pl-s1">item</span>: <span class="pl-v">WebSocketConnectionModel</span>):
        <span class="pl-s1">self</span>.<span class="pl-s1">connections</span>.<span class="pl-en">remove</span>(<span class="pl-s1">item</span>)



<span class="pl-s1">manager</span> <span class="pl-c1">=</span> <span class="pl-v">SocketManager</span>()

<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">websocket_test</span>(<span class="pl-s1">websocket</span>: <span class="pl-v">WebSocket</span>, <span class="pl-s1">access_token</span>: <span class="pl-s1">str</span> <span class="pl-c1">=</span> <span class="pl-v">Query</span>(..., <span class="pl-s1">alias</span><span class="pl-c1">=</span><span class="pl-s">"token"</span>)):
    <span class="pl-s1">user</span> <span class="pl-c1">=</span> <span class="pl-en">get_current_user_from_query_token</span>(<span class="pl-s1">access_token</span>)
    <span class="pl-k">await</span> <span class="pl-s1">websocket</span>.<span class="pl-en">accept</span>()
   <span class="pl-c">#add connection to a list</span>
    <span class="pl-s1">connection</span> <span class="pl-c1">=</span> <span class="pl-s1">manager</span>.<span class="pl-en">connect</span>(<span class="pl-s1">user</span>.<span class="pl-s1">account_id</span>, <span class="pl-s1">user</span>.<span class="pl-s1">id</span>, <span class="pl-s1">websocket</span>)
    <span class="pl-k">try</span>:
        <span class="pl-k">while</span> <span class="pl-c1">True</span>:
            <span class="pl-s1">data</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-s1">websocket</span>.<span class="pl-en">receive_json</span>()
    <span class="pl-k">except</span> <span class="pl-v">WebSocketDisconnect</span>:
        <span class="pl-s1">manager</span>.<span class="pl-en">disconnect</span>(<span class="pl-s1">connection</span>)

<span class="pl-s1">app</span>.<span class="pl-en">add_websocket_route</span>(<span class="pl-s">"/ws/v1/inbox/test"</span>, <span class="pl-s1">facebook_inbox</span>.<span class="pl-s1">websocket_test</span>)</pre></div>
<p dir="auto">Is there any way to identify incoming WebSocket object and compare with an existing list, to avoid adding duplicates? Is there any unique identifier in WebSocket object?</p>
<h3 dir="auto">First Check</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I added a very descriptive title to this issue.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I used the GitHub search to find a similar issue and didn't find it.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I searched the FastAPI documentation, with the integrated search.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already searched in Google "How to X in FastAPI" and didn't find any information.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already read and followed all the tutorial in the docs and didn't find an answer.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/samuelcolvin/pydantic">Pydantic</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/swagger-api/swagger-ui">Swagger UI</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/Redocly/redoc">ReDoc</a>.</li>
</ul>
<h3 dir="auto">Commit to Help</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I commit to help with one of those options 👆</li>
</ul>
<h3 dir="auto">Example Code</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()

@app.get(&quot;/&quot;)
async def root():
    return {'message': 'success'}"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">fastapi</span> <span class="pl-k">import</span> <span class="pl-v">FastAPI</span>
<span class="pl-k">from</span> <span class="pl-s1">fastapi</span>.<span class="pl-s1">staticfiles</span> <span class="pl-k">import</span> <span class="pl-v">StaticFiles</span>

<span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-v">FastAPI</span>()

<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">get</span>(<span class="pl-s">"/"</span>)</span>
<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">root</span>():
    <span class="pl-k">return</span> {<span class="pl-s">'message'</span>: <span class="pl-s">'success'</span>}</pre></div>
<h3 dir="auto">Description</h3>
<p dir="auto">Attempt to run the file with uvicorn:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="(.venv) ➜  hmx1 uvicorn app2:app --reload --port 8002
INFO:     Uvicorn running on http://127.0.0.1:8002 (Press CTRL+C to quit)
INFO:     Started reloader process [70344] using statreload
Process SpawnProcess-1:
Traceback (most recent call last):
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/process.py&quot;, line 315, in _bootstrap
    self.run()
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/process.py&quot;, line 108, in run
    self._target(*self._args, **self._kwargs)
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/subprocess.py&quot;, line 61, in subprocess_started
    target(sockets=sockets)
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/server.py&quot;, line 48, in run
    loop.run_until_complete(self.serve(sockets=sockets))
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py&quot;, line 616, in run_until_complete
    return future.result()
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/server.py&quot;, line 55, in serve
    config.load()
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/config.py&quot;, line 308, in load
    self.loaded_app = import_from_string(self.app)
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/importer.py&quot;, line 23, in import_from_string
    raise exc from None
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/importer.py&quot;, line 20, in import_from_string
    module = importlib.import_module(module_str)
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/importlib/__init__.py&quot;, line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 1014, in _gcd_import
  File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 991, in _find_and_load
  File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 975, in _find_and_load_unlocked
  File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 671, in _load_unlocked
  File &quot;&lt;frozen importlib._bootstrap_external&gt;&quot;, line 783, in exec_module
  File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 219, in _call_with_frames_removed
  File &quot;./app2.py&quot;, line 2, in &lt;module&gt;
    from fastapi.staticfiles import StaticFiles
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/fastapi/staticfiles.py&quot;, line 1, in &lt;module&gt;
    from starlette.staticfiles import StaticFiles as StaticFiles  # noqa
  File &quot;/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/starlette/staticfiles.py&quot;, line 7, in &lt;module&gt;
    from aiofiles.os import stat as aio_stat
ModuleNotFoundError: No module named 'aiofiles'"><pre class="notranslate"><code class="notranslate">(.venv) ➜  hmx1 uvicorn app2:app --reload --port 8002
INFO:     Uvicorn running on http://127.0.0.1:8002 (Press CTRL+C to quit)
INFO:     Started reloader process [70344] using statreload
Process SpawnProcess-1:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
    self.run()
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/subprocess.py", line 61, in subprocess_started
    target(sockets=sockets)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/server.py", line 48, in run
    loop.run_until_complete(self.serve(sockets=sockets))
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/server.py", line 55, in serve
    config.load()
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/config.py", line 308, in load
    self.loaded_app = import_from_string(self.app)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/importer.py", line 23, in import_from_string
    raise exc from None
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/uvicorn/importer.py", line 20, in import_from_string
    module = importlib.import_module(module_str)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "&lt;frozen importlib._bootstrap&gt;", line 1014, in _gcd_import
  File "&lt;frozen importlib._bootstrap&gt;", line 991, in _find_and_load
  File "&lt;frozen importlib._bootstrap&gt;", line 975, in _find_and_load_unlocked
  File "&lt;frozen importlib._bootstrap&gt;", line 671, in _load_unlocked
  File "&lt;frozen importlib._bootstrap_external&gt;", line 783, in exec_module
  File "&lt;frozen importlib._bootstrap&gt;", line 219, in _call_with_frames_removed
  File "./app2.py", line 2, in &lt;module&gt;
    from fastapi.staticfiles import StaticFiles
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/fastapi/staticfiles.py", line 1, in &lt;module&gt;
    from starlette.staticfiles import StaticFiles as StaticFiles  # noqa
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/starlette/staticfiles.py", line 7, in &lt;module&gt;
    from aiofiles.os import stat as aio_stat
ModuleNotFoundError: No module named 'aiofiles'
</code></pre></div>
<h3 dir="auto">Operating System</h3>
<p dir="auto">macOS</p>
<h3 dir="auto">Operating System Details</h3>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="(.venv) ➜  hmx1 pip freeze
aiofiles==0.8.0
anyio==3.4.0
asgiref==3.4.1
Chameleon==3.9.1
click==8.0.3
fastapi==0.70.0
fastapi-chameleon==0.1.12
h11==0.12.0
idna==3.3
Jinja2==3.0.3
MarkupSafe==2.0.1
pydantic==1.8.2
sniffio==1.2.0
starlette==0.16.0
typing-extensions==4.0.1
uvicorn==0.15.0"><pre class="notranslate"><code class="notranslate">(.venv) ➜  hmx1 pip freeze
aiofiles==0.8.0
anyio==3.4.0
asgiref==3.4.1
Chameleon==3.9.1
click==8.0.3
fastapi==0.70.0
fastapi-chameleon==0.1.12
h11==0.12.0
idna==3.3
Jinja2==3.0.3
MarkupSafe==2.0.1
pydantic==1.8.2
sniffio==1.2.0
starlette==0.16.0
typing-extensions==4.0.1
uvicorn==0.15.0
</code></pre></div>
<h3 dir="auto">FastAPI Version</h3>
<p dir="auto">0.70.0</p>
<h3 dir="auto">Python Version</h3>
<p dir="auto">Python 3.8.7</p>
<h3 dir="auto">Additional Context</h3>
<p dir="auto">I'm just trying to follow the tutorial at <a href="https://fastapi.tiangolo.com/advanced/templates/" rel="nofollow">https://fastapi.tiangolo.com/advanced/templates/</a>.</p>
<p dir="auto">I can do the import interactively from a python terminal, but it fails when using uvicorn.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Python 3.8.7 (v3.8.7:6503f05dd5, Dec 21 2020, 12:45:15)
[Clang 6.0 (clang-600.0.57)] on darwin
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; from fastapi.staticfiles import StaticFiles
&gt;&gt;&gt;"><pre class="notranslate"><code class="notranslate">Python 3.8.7 (v3.8.7:6503f05dd5, Dec 21 2020, 12:45:15)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; from fastapi.staticfiles import StaticFiles
&gt;&gt;&gt;
</code></pre></div>