<p dir="auto"><strong>Describe the bug</strong></p>
<p dir="auto">The <code class="notranslate">openapi.json</code> generated from the docs is not valid OpenAPI 3.0 schema and doesn't pass validation. Can be checked against few available validators:</p>
<ul dir="auto">
<li><a href="https://apidevtools.org/swagger-parser/online/" rel="nofollow">https://apidevtools.org/swagger-parser/online/</a></li>
<li><a href="https://editor.swagger.io/" rel="nofollow">https://editor.swagger.io/</a></li>
</ul>
<p dir="auto"><strong>Expected behavior</strong></p>
<p dir="auto">Passes validation.</p>
<p dir="auto"><strong>Environment:</strong></p>
<ul dir="auto">
<li>OS: macOS</li>
<li>FastAPI Version: 0.33.0</li>
<li>Python version: 3.7.3</li>
</ul>
<p dir="auto"><strong>Additional context</strong></p>
<p dir="auto">After looking at the schema I found some noticeable parts:</p>
<ul dir="auto">
<li>Security definitions are duplicated multiple times is there are some dependencies with shared sub dependencies. In the schema it looks like:</li>
</ul>
<div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&quot;security&quot;: [
  {
    &quot;My Auth&quot;: []
  },
  {
    &quot;My Auth&quot;: []
  }
]"><pre class="notranslate"><span class="pl-ent">"security"</span>: [
  {
    <span class="pl-ent">"My Auth"</span>: []
  },
  {
    <span class="pl-ent">"My Auth"</span>: []
  }
]</pre></div>
<ul dir="auto">
<li>For numeric values min/max validation flags should be boolean values instead of integers:</li>
</ul>
<div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&quot;schema&quot;: {
  &quot;title&quot;: &quot;Size&quot;,
  &quot;maximum&quot;: 100,
  &quot;exclusiveMinimum&quot;: 0,
  &quot;type&quot;: &quot;integer&quot;,
  &quot;description&quot;: &quot;Number of records to return&quot;,
  &quot;default&quot;: 10
}"><pre class="notranslate"><span class="pl-ent">"schema"</span>: {
  <span class="pl-ent">"title"</span>: <span class="pl-s"><span class="pl-pds">"</span>Size<span class="pl-pds">"</span></span>,
  <span class="pl-ent">"maximum"</span>: <span class="pl-c1">100</span>,
  <span class="pl-ent">"exclusiveMinimum"</span>: <span class="pl-c1">0</span>,
  <span class="pl-ent">"type"</span>: <span class="pl-s"><span class="pl-pds">"</span>integer<span class="pl-pds">"</span></span>,
  <span class="pl-ent">"description"</span>: <span class="pl-s"><span class="pl-pds">"</span>Number of records to return<span class="pl-pds">"</span></span>,
  <span class="pl-ent">"default"</span>: <span class="pl-c1">10</span>
}</pre></div>
<p dir="auto">The <code class="notranslate">"exclusiveMinimum": 0,</code> should be in fact <code class="notranslate">"exclusiveMinimum": false,</code></p>
<ul dir="auto">
<li>Path parameters is referenced in multiple dependencies for a route get duplicated:</li>
</ul>
<div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&quot;parameters&quot;: [
  {
    &quot;required&quot;: true,
    &quot;schema&quot;: {
      &quot;title&quot;: &quot;User_Id&quot;,
      &quot;type&quot;: &quot;string&quot;,
      &quot;format&quot;: &quot;uuid&quot;
    },
    &quot;name&quot;: &quot;user_id&quot;,
    &quot;in&quot;: &quot;path&quot;
  },
  {
    &quot;required&quot;: true,
    &quot;schema&quot;: {
      &quot;title&quot;: &quot;User_Id&quot;,
      &quot;type&quot;: &quot;string&quot;,
      &quot;format&quot;: &quot;uuid&quot;
    },
    &quot;name&quot;: &quot;user_id&quot;,
    &quot;in&quot;: &quot;path&quot;
  }
]"><pre class="notranslate"><span class="pl-ent">"parameters"</span>: [
  {
    <span class="pl-ent">"required"</span>: <span class="pl-c1">true</span>,
    <span class="pl-ent">"schema"</span>: {
      <span class="pl-ent">"title"</span>: <span class="pl-s"><span class="pl-pds">"</span>User_Id<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"type"</span>: <span class="pl-s"><span class="pl-pds">"</span>string<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"format"</span>: <span class="pl-s"><span class="pl-pds">"</span>uuid<span class="pl-pds">"</span></span>
    },
    <span class="pl-ent">"name"</span>: <span class="pl-s"><span class="pl-pds">"</span>user_id<span class="pl-pds">"</span></span>,
    <span class="pl-ent">"in"</span>: <span class="pl-s"><span class="pl-pds">"</span>path<span class="pl-pds">"</span></span>
  },
  {
    <span class="pl-ent">"required"</span>: <span class="pl-c1">true</span>,
    <span class="pl-ent">"schema"</span>: {
      <span class="pl-ent">"title"</span>: <span class="pl-s"><span class="pl-pds">"</span>User_Id<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"type"</span>: <span class="pl-s"><span class="pl-pds">"</span>string<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"format"</span>: <span class="pl-s"><span class="pl-pds">"</span>uuid<span class="pl-pds">"</span></span>
    },
    <span class="pl-ent">"name"</span>: <span class="pl-s"><span class="pl-pds">"</span>user_id<span class="pl-pds">"</span></span>,
    <span class="pl-ent">"in"</span>: <span class="pl-s"><span class="pl-pds">"</span>path<span class="pl-pds">"</span></span>
  }
]</pre></div>
<ul dir="auto">
<li>References are not properly defined and thus resolved, resulting in multiple errors such as:</li>
</ul>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Missing required property: $ref at #/user/roles"><pre class="notranslate"><code class="notranslate">Missing required property: $ref at #/user/roles
</code></pre></div>
<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 <g-emoji class="g-emoji" alias="point_up_2" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f446.png">👆</g-emoji></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="import uvicorn
import threading

from typing import Optional
from datetime import datetime

from passlib.context import CryptContext
from beanie import Document, init_beanie
from motor.motor_asyncio import AsyncIOMotorClient
from fastapi import FastAPI


lock = threading.Lock()


app = FastAPI()

pwd_context = CryptContext(schemes=[&quot;bcrypt&quot;], deprecated=&quot;auto&quot;)


class User(Document):
    username: str
    full_name: Optional[str] = None
    email: Optional[str] = None

    class Settings:
        name = &quot;users&quot;

    def __repr__(self) -&gt; str:
        return f&quot;&lt;User {self.username}&gt;&quot;

    def __str__(self) -&gt; str:
        return self.username

    def __hash__(self) -&gt; int:
        return hash(self.username)

    def __eq__(self, other: object) -&gt; bool:
        if isinstance(other, User):
            return self.username == other.username
        return False

    @property
    def created(self) -&gt; datetime:
        return self.id.generation_time

    @classmethod
    async def by_username(cls, username: str) -&gt; &quot;User&quot;:
        &quot;&quot;&quot;Get a user by username&quot;&quot;&quot;
        return await cls.find_one(cls.username == username)


class UserPW(User):
    password: str


def hash_password(password: str) -&gt; str:
    return pwd_context.hash(password.encode())


async def create_admin_user(
        username: str = &quot;test&quot;,
        password: str = &quot;test&quot;
):
    # lock not working, still create 4 users
    with lock:
        found_user = await User.by_username(username)
        if found_user is not None:
            print(&quot;admin user already exists, pass!&quot;)
        else:
            user = UserPW(
                username=username,
                password=password
            )
            hashed = hash_password(user.password)
            user.password = hashed
            await user.create()
            return User(**user.dict())


async def init_db(app: FastAPI):
    app.db = AsyncIOMotorClient(&quot;mongodb://test:test@127.0.0.1:27017/pml_dev?authSource=admin&quot;)
    col = getattr(app.db, &quot;pml_dev&quot;)
    await init_beanie(col, document_models=[
        User
    ])

@app.on_event(&quot;startup&quot;)
async def init():
    # init database
    await init_db(app)
    # create admin user if not exists
    await create_admin_user()


# create 4 duplicate users
# uvicorn main:app --host=0.0.0.0 --port 5000 --workers 4

# only create 1 user on single worker
# uvicorn main:app --host=0.0.0.0 --port 5000"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">uvicorn</span>
<span class="pl-k">import</span> <span class="pl-s1">threading</span>

<span class="pl-k">from</span> <span class="pl-s1">typing</span> <span class="pl-k">import</span> <span class="pl-v">Optional</span>
<span class="pl-k">from</span> <span class="pl-s1">datetime</span> <span class="pl-k">import</span> <span class="pl-s1">datetime</span>

<span class="pl-k">from</span> <span class="pl-s1">passlib</span>.<span class="pl-s1">context</span> <span class="pl-k">import</span> <span class="pl-v">CryptContext</span>
<span class="pl-k">from</span> <span class="pl-s1">beanie</span> <span class="pl-k">import</span> <span class="pl-v">Document</span>, <span class="pl-s1">init_beanie</span>
<span class="pl-k">from</span> <span class="pl-s1">motor</span>.<span class="pl-s1">motor_asyncio</span> <span class="pl-k">import</span> <span class="pl-v">AsyncIOMotorClient</span>
<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-s1">lock</span> <span class="pl-c1">=</span> <span class="pl-s1">threading</span>.<span class="pl-v">Lock</span>()


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

<span class="pl-s1">pwd_context</span> <span class="pl-c1">=</span> <span class="pl-v">CryptContext</span>(<span class="pl-s1">schemes</span><span class="pl-c1">=</span>[<span class="pl-s">"bcrypt"</span>], <span class="pl-s1">deprecated</span><span class="pl-c1">=</span><span class="pl-s">"auto"</span>)


<span class="pl-k">class</span> <span class="pl-v">User</span>(<span class="pl-v">Document</span>):
    <span class="pl-s1">username</span>: <span class="pl-s1">str</span>
    <span class="pl-s1">full_name</span>: <span class="pl-v">Optional</span>[<span class="pl-s1">str</span>] <span class="pl-c1">=</span> <span class="pl-c1">None</span>
    <span class="pl-s1">email</span>: <span class="pl-v">Optional</span>[<span class="pl-s1">str</span>] <span class="pl-c1">=</span> <span class="pl-c1">None</span>

    <span class="pl-k">class</span> <span class="pl-v">Settings</span>:
        <span class="pl-s1">name</span> <span class="pl-c1">=</span> <span class="pl-s">"users"</span>

    <span class="pl-k">def</span> <span class="pl-en">__repr__</span>(<span class="pl-s1">self</span>) <span class="pl-c1">-&gt;</span> <span class="pl-s1">str</span>:
        <span class="pl-k">return</span> <span class="pl-s">f"&lt;User <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">self</span>.<span class="pl-s1">username</span><span class="pl-kos">}</span></span>&gt;"</span>

    <span class="pl-k">def</span> <span class="pl-en">__str__</span>(<span class="pl-s1">self</span>) <span class="pl-c1">-&gt;</span> <span class="pl-s1">str</span>:
        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-s1">username</span>

    <span class="pl-k">def</span> <span class="pl-en">__hash__</span>(<span class="pl-s1">self</span>) <span class="pl-c1">-&gt;</span> <span class="pl-s1">int</span>:
        <span class="pl-k">return</span> <span class="pl-en">hash</span>(<span class="pl-s1">self</span>.<span class="pl-s1">username</span>)

    <span class="pl-k">def</span> <span class="pl-en">__eq__</span>(<span class="pl-s1">self</span>, <span class="pl-s1">other</span>: <span class="pl-s1">object</span>) <span class="pl-c1">-&gt;</span> <span class="pl-s1">bool</span>:
        <span class="pl-k">if</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">other</span>, <span class="pl-v">User</span>):
            <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-s1">username</span> <span class="pl-c1">==</span> <span class="pl-s1">other</span>.<span class="pl-s1">username</span>
        <span class="pl-k">return</span> <span class="pl-c1">False</span>

    <span class="pl-en">@<span class="pl-s1">property</span></span>
    <span class="pl-k">def</span> <span class="pl-en">created</span>(<span class="pl-s1">self</span>) <span class="pl-c1">-&gt;</span> <span class="pl-s1">datetime</span>:
        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-s1">id</span>.<span class="pl-s1">generation_time</span>

    <span class="pl-en">@<span class="pl-s1">classmethod</span></span>
    <span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">by_username</span>(<span class="pl-s1">cls</span>, <span class="pl-s1">username</span>: <span class="pl-s1">str</span>) <span class="pl-c1">-&gt;</span> <span class="pl-s">"User"</span>:
        <span class="pl-s">"""Get a user by username"""</span>
        <span class="pl-k">return</span> <span class="pl-k">await</span> <span class="pl-s1">cls</span>.<span class="pl-en">find_one</span>(<span class="pl-s1">cls</span>.<span class="pl-s1">username</span> <span class="pl-c1">==</span> <span class="pl-s1">username</span>)


<span class="pl-k">class</span> <span class="pl-v">UserPW</span>(<span class="pl-v">User</span>):
    <span class="pl-s1">password</span>: <span class="pl-s1">str</span>


<span class="pl-k">def</span> <span class="pl-en">hash_password</span>(<span class="pl-s1">password</span>: <span class="pl-s1">str</span>) <span class="pl-c1">-&gt;</span> <span class="pl-s1">str</span>:
    <span class="pl-k">return</span> <span class="pl-s1">pwd_context</span>.<span class="pl-en">hash</span>(<span class="pl-s1">password</span>.<span class="pl-en">encode</span>())


<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">create_admin_user</span>(
        <span class="pl-s1">username</span>: <span class="pl-s1">str</span> <span class="pl-c1">=</span> <span class="pl-s">"test"</span>,
        <span class="pl-s1">password</span>: <span class="pl-s1">str</span> <span class="pl-c1">=</span> <span class="pl-s">"test"</span>
):
    <span class="pl-c"># lock not working, still create 4 users</span>
    <span class="pl-k">with</span> <span class="pl-s1">lock</span>:
        <span class="pl-s1">found_user</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-v">User</span>.<span class="pl-en">by_username</span>(<span class="pl-s1">username</span>)
        <span class="pl-k">if</span> <span class="pl-s1">found_user</span> <span class="pl-c1">is</span> <span class="pl-c1">not</span> <span class="pl-c1">None</span>:
            <span class="pl-en">print</span>(<span class="pl-s">"admin user already exists, pass!"</span>)
        <span class="pl-k">else</span>:
            <span class="pl-s1">user</span> <span class="pl-c1">=</span> <span class="pl-v">UserPW</span>(
                <span class="pl-s1">username</span><span class="pl-c1">=</span><span class="pl-s1">username</span>,
                <span class="pl-s1">password</span><span class="pl-c1">=</span><span class="pl-s1">password</span>
            )
            <span class="pl-s1">hashed</span> <span class="pl-c1">=</span> <span class="pl-en">hash_password</span>(<span class="pl-s1">user</span>.<span class="pl-s1">password</span>)
            <span class="pl-s1">user</span>.<span class="pl-s1">password</span> <span class="pl-c1">=</span> <span class="pl-s1">hashed</span>
            <span class="pl-k">await</span> <span class="pl-s1">user</span>.<span class="pl-en">create</span>()
            <span class="pl-k">return</span> <span class="pl-v">User</span>(<span class="pl-c1">**</span><span class="pl-s1">user</span>.<span class="pl-en">dict</span>())


<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">init_db</span>(<span class="pl-s1">app</span>: <span class="pl-v">FastAPI</span>):
    <span class="pl-s1">app</span>.<span class="pl-s1">db</span> <span class="pl-c1">=</span> <span class="pl-v">AsyncIOMotorClient</span>(<span class="pl-s">"mongodb://test:test@127.0.0.1:27017/pml_dev?authSource=admin"</span>)
    <span class="pl-s1">col</span> <span class="pl-c1">=</span> <span class="pl-en">getattr</span>(<span class="pl-s1">app</span>.<span class="pl-s1">db</span>, <span class="pl-s">"pml_dev"</span>)
    <span class="pl-k">await</span> <span class="pl-en">init_beanie</span>(<span class="pl-s1">col</span>, <span class="pl-s1">document_models</span><span class="pl-c1">=</span>[
        <span class="pl-v">User</span>
    ])

<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">on_event</span>(<span class="pl-s">"startup"</span>)</span>
<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">init</span>():
    <span class="pl-c"># init database</span>
    <span class="pl-k">await</span> <span class="pl-en">init_db</span>(<span class="pl-s1">app</span>)
    <span class="pl-c"># create admin user if not exists</span>
    <span class="pl-k">await</span> <span class="pl-en">create_admin_user</span>()


<span class="pl-c"># create 4 duplicate users</span>
<span class="pl-c"># uvicorn main:app --host=0.0.0.0 --port 5000 --workers 4</span>

<span class="pl-c"># only create 1 user on single worker</span>
<span class="pl-c"># uvicorn main:app --host=0.0.0.0 --port 5000</span></pre></div>
<h3 dir="auto">Description</h3>
<ul dir="auto">
<li>I am current immigrate <code class="notranslate">flask</code> project to <code class="notranslate">fastapi</code>.</li>
<li>on <code class="notranslate">startup</code> it will to <code class="notranslate">setup mongodb database</code> and <code class="notranslate">create 1 admin user</code> to provide <code class="notranslate">security</code> feature.</li>
<li>some <code class="notranslate">api router</code> is sync due to <code class="notranslate">compatibility</code>, to improve performance so i run it on <code class="notranslate">4 workers</code>.</li>
<li>but due to multiple workers threads, it create <code class="notranslate">4 users</code> instead of 1.</li>
<li>I tried add <code class="notranslate">threading lock</code> or <code class="notranslate">async lock</code> and it's not working, how can we add lock on <code class="notranslate">startup event</code> to make sure only 1 event execute in multiple threading?</li>
</ul>
<h3 dir="auto">Operating System</h3>
<p dir="auto">macOS</p>
<h3 dir="auto">Operating System Details</h3>
<p dir="auto"><em>No response</em></p>
<h3 dir="auto">FastAPI Version</h3>
<p dir="auto">0.86.0</p>
<h3 dir="auto">Python Version</h3>
<p dir="auto">3.10</p>
<h3 dir="auto">Additional Context</h3>
<p dir="auto"><em>No response</em></p>