<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="class ControlValue(BaseModel):
    &quot;&quot;&quot;V4l2 control value model&quot;&quot;&quot;

    value: Union[int, str, List[int], TriggerSequenceValue, Any]
    &quot;&quot;&quot;: value of v4l2 control&quot;&quot;&quot;

class FrameSize(BaseModel):
    &quot;&quot;&quot;Frame size model&quot;&quot;&quot;

    width: int
    height: int

    class Config:
        &quot;&quot;&quot;Config class&quot;&quot;&quot;

        fields = {&quot;width&quot;: {&quot;example&quot;: 1024}, &quot;height&quot;: {&quot;example&quot;: 1024}}


class FrameResolution(FrameSize):
    &quot;&quot;&quot;Video device frame resolution model&quot;&quot;&quot;

    width: Optional[int] = None
    height: Optional[int] = None

class CropRect(FrameSize):
    &quot;&quot;&quot;Video device frame crop rectangle model&quot;&quot;&quot;

    left: int = Field(default=..., example=0)
    top: int = Field(default=..., example=0)

class ControlExamples:
    &quot;&quot;&quot;Examples for controls API&quot;&quot;&quot;

    name = {
        &quot;exposure&quot;: {
            &quot;summary&quot;: &quot;Exposure Time&quot;,
            &quot;value&quot;: &quot;Exposure Time, Absolute&quot;,
        },
        &quot;gain&quot;: {&quot;summary&quot;: &quot;Gain&quot;, &quot;value&quot;: &quot;Gain&quot;},
        &quot;red&quot;: {&quot;summary&quot;: &quot;Red Gain&quot;, &quot;value&quot;: &quot;Red Balance&quot;},
        &quot;green&quot;: {&quot;summary&quot;: &quot;Green Gain&quot;, &quot;value&quot;: &quot;Green Balance&quot;},
        &quot;blue&quot;: {&quot;summary&quot;: &quot;Blue Gain&quot;, &quot;value&quot;: &quot;Blue Balance&quot;},
        &quot;fpn&quot;: {
            &quot;summary&quot;: &quot;Fixed Pattern Noise Correction&quot;,
            &quot;value&quot;: &quot;Fixed Pattern Noise Correction&quot;,
        },
    }

    value = {
        &quot;exposure&quot;: {
            &quot;summary&quot;: &quot;Exposure Time&quot;,
            &quot;value&quot;: {&quot;value&quot;: 5000},
        },
        &quot;gain&quot;: {&quot;summary&quot;: &quot;Gain&quot;, &quot;value&quot;: {&quot;value&quot;: 0}},
        &quot;red&quot;: {&quot;summary&quot;: &quot;Red Gain&quot;, &quot;value&quot;: {&quot;value&quot;: 16384}},
        &quot;green&quot;: {&quot;summary&quot;: &quot;Green Gain&quot;, &quot;value&quot;: {&quot;value&quot;: 16384}},
        &quot;blue&quot;: {&quot;summary&quot;: &quot;Blue Gain&quot;, &quot;value&quot;: {&quot;value&quot;: 16384}},
        &quot;array&quot;: {&quot;summary&quot;: &quot;Array Control&quot;, &quot;value&quot;: {&quot;value&quot;: [1, 2, 3]}},
    }


get_control_responses = {
    200: {
        &quot;description&quot;: &quot;Success&quot;,
        &quot;content&quot;: {
            &quot;application/json&quot;: {
                &quot;examples&quot;: {
                    **ControlExamples.value,
                    &quot;sensor type&quot;: {
                        &quot;summary&quot;: &quot;Sensor Type&quot;,
                        &quot;value&quot;: {&quot;value&quot;: &quot;CMV4000v3 Bayer&quot;},
                    },
                }
            }
        },
    },
}


@app.get(
    V4L2_ROUTE + V4L2Endpoints.control + &quot;/{ctrl_id_or_name}&quot;,
    response_model=ControlValue,
    responses=get_control_responses,
    tags=[Tags.controls],
)
def get_control(
    ctrl_id_or_name: Union[int, str] = Path(  # noqa: B008
        description=&quot;Id or Name of v4l2 control&quot;,
        examples={
            **ControlExamples.name,
            &quot;sensor type&quot;: {
                &quot;summary&quot;: &quot;Sensor Type&quot;,
                &quot;value&quot;: &quot;Sensor Type&quot;,
            },
        },
    ),
    params: CommonParams = Depends(),  # noqa: B008
) -&gt; JSONResponse:
    &quot;&quot;&quot;
    Get value of single v4l2 control

    Supports both normal and extended controls
    &quot;&quot;&quot;
    return check_exceptions(Camera.get_control, ctrl_id_or_name, params.dev)


set_control_responses = {
    200: {
        &quot;description&quot;: &quot;Success&quot;,
        &quot;content&quot;: {&quot;application/json&quot;: {&quot;examples&quot;: ControlExamples.value}},
    },
}


@app.post(
    V4L2_ROUTE + V4L2Endpoints.control + &quot;/{ctrl_id_or_name}&quot;,
    response_model=ControlValue,
    responses=set_control_responses,
    tags=[Tags.controls],
)
def set_control(
    ctrl_id_or_name: Union[int, str] = Path(  # noqa: B008
        description=&quot;Id or Name of v4l2 control&quot;,
        examples=ControlExamples.name,
    ),
    value: ControlValue = Body(  # noqa: B008
        examples=ControlExamples.value, descripton=&quot;v4l2 control value&quot;
    ),
    params: CommonParams = Depends(),  # noqa: B008
) -&gt; JSONResponse:
    &quot;&quot;&quot;
    Set value of single v4l2 control

    Supports both normal and extended controls
    &quot;&quot;&quot;
    return check_exceptions(Camera.set_control, ctrl_id_or_name, value, params.dev)

...

client = TestClient(app)

class TestControl:
    &quot;&quot;&quot;Tests for 'control' endpoint&quot;&quot;&quot;

    base_route = V4L2_ROUTE + V4L2Endpoints.control

    def test_get_control(self):
        control_name = &quot;exposure time, absolute&quot;
        response = client.get(self.base_route + &quot;/&quot; + control_name)
        assert response.status_code == 200
        parse_obj_as(ControlValue, response.json())

    def test_get_control_bad(self):
        response = client.get(self.base_route + &quot;/bla&quot;)
        assert response.status_code == 422

    def test_set_control(self):
        control_name = &quot;exposure time, absolute&quot;
        control_value = 5000
        print(ControlValue.schema_json())
        json_value = jsonable_encoder(ControlValue(value=control_value))
        response = client.post(
            self.base_route + &quot;/&quot; + control_name,
            json=json_value,
        )
        assert response.status_code == 200
        assert response.json() == json_value"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-v">ControlValue</span>(<span class="pl-v">BaseModel</span>):
    <span class="pl-s">"""V4l2 control value model"""</span>

    <span class="pl-s1">value</span>: <span class="pl-v">Union</span>[<span class="pl-s1">int</span>, <span class="pl-s1">str</span>, <span class="pl-v">List</span>[<span class="pl-s1">int</span>], <span class="pl-v">TriggerSequenceValue</span>, <span class="pl-v">Any</span>]
    <span class="pl-s">""": value of v4l2 control"""</span>

<span class="pl-k">class</span> <span class="pl-v">FrameSize</span>(<span class="pl-v">BaseModel</span>):
    <span class="pl-s">"""Frame size model"""</span>

    <span class="pl-s1">width</span>: <span class="pl-s1">int</span>
    <span class="pl-s1">height</span>: <span class="pl-s1">int</span>

    <span class="pl-k">class</span> <span class="pl-v">Config</span>:
        <span class="pl-s">"""Config class"""</span>

        <span class="pl-s1">fields</span> <span class="pl-c1">=</span> {<span class="pl-s">"width"</span>: {<span class="pl-s">"example"</span>: <span class="pl-c1">1024</span>}, <span class="pl-s">"height"</span>: {<span class="pl-s">"example"</span>: <span class="pl-c1">1024</span>}}


<span class="pl-k">class</span> <span class="pl-v">FrameResolution</span>(<span class="pl-v">FrameSize</span>):
    <span class="pl-s">"""Video device frame resolution model"""</span>

    <span class="pl-s1">width</span>: <span class="pl-v">Optional</span>[<span class="pl-s1">int</span>] <span class="pl-c1">=</span> <span class="pl-c1">None</span>
    <span class="pl-s1">height</span>: <span class="pl-v">Optional</span>[<span class="pl-s1">int</span>] <span class="pl-c1">=</span> <span class="pl-c1">None</span>

<span class="pl-k">class</span> <span class="pl-v">CropRect</span>(<span class="pl-v">FrameSize</span>):
    <span class="pl-s">"""Video device frame crop rectangle model"""</span>

    <span class="pl-s1">left</span>: <span class="pl-s1">int</span> <span class="pl-c1">=</span> <span class="pl-v">Field</span>(<span class="pl-s1">default</span><span class="pl-c1">=</span>..., <span class="pl-s1">example</span><span class="pl-c1">=</span><span class="pl-c1">0</span>)
    <span class="pl-s1">top</span>: <span class="pl-s1">int</span> <span class="pl-c1">=</span> <span class="pl-v">Field</span>(<span class="pl-s1">default</span><span class="pl-c1">=</span>..., <span class="pl-s1">example</span><span class="pl-c1">=</span><span class="pl-c1">0</span>)

<span class="pl-k">class</span> <span class="pl-v">ControlExamples</span>:
    <span class="pl-s">"""Examples for controls API"""</span>

    <span class="pl-s1">name</span> <span class="pl-c1">=</span> {
        <span class="pl-s">"exposure"</span>: {
            <span class="pl-s">"summary"</span>: <span class="pl-s">"Exposure Time"</span>,
            <span class="pl-s">"value"</span>: <span class="pl-s">"Exposure Time, Absolute"</span>,
        },
        <span class="pl-s">"gain"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Gain"</span>, <span class="pl-s">"value"</span>: <span class="pl-s">"Gain"</span>},
        <span class="pl-s">"red"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Red Gain"</span>, <span class="pl-s">"value"</span>: <span class="pl-s">"Red Balance"</span>},
        <span class="pl-s">"green"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Green Gain"</span>, <span class="pl-s">"value"</span>: <span class="pl-s">"Green Balance"</span>},
        <span class="pl-s">"blue"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Blue Gain"</span>, <span class="pl-s">"value"</span>: <span class="pl-s">"Blue Balance"</span>},
        <span class="pl-s">"fpn"</span>: {
            <span class="pl-s">"summary"</span>: <span class="pl-s">"Fixed Pattern Noise Correction"</span>,
            <span class="pl-s">"value"</span>: <span class="pl-s">"Fixed Pattern Noise Correction"</span>,
        },
    }

    <span class="pl-s1">value</span> <span class="pl-c1">=</span> {
        <span class="pl-s">"exposure"</span>: {
            <span class="pl-s">"summary"</span>: <span class="pl-s">"Exposure Time"</span>,
            <span class="pl-s">"value"</span>: {<span class="pl-s">"value"</span>: <span class="pl-c1">5000</span>},
        },
        <span class="pl-s">"gain"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Gain"</span>, <span class="pl-s">"value"</span>: {<span class="pl-s">"value"</span>: <span class="pl-c1">0</span>}},
        <span class="pl-s">"red"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Red Gain"</span>, <span class="pl-s">"value"</span>: {<span class="pl-s">"value"</span>: <span class="pl-c1">16384</span>}},
        <span class="pl-s">"green"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Green Gain"</span>, <span class="pl-s">"value"</span>: {<span class="pl-s">"value"</span>: <span class="pl-c1">16384</span>}},
        <span class="pl-s">"blue"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Blue Gain"</span>, <span class="pl-s">"value"</span>: {<span class="pl-s">"value"</span>: <span class="pl-c1">16384</span>}},
        <span class="pl-s">"array"</span>: {<span class="pl-s">"summary"</span>: <span class="pl-s">"Array Control"</span>, <span class="pl-s">"value"</span>: {<span class="pl-s">"value"</span>: [<span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">3</span>]}},
    }


<span class="pl-s1">get_control_responses</span> <span class="pl-c1">=</span> {
    <span class="pl-c1">200</span>: {
        <span class="pl-s">"description"</span>: <span class="pl-s">"Success"</span>,
        <span class="pl-s">"content"</span>: {
            <span class="pl-s">"application/json"</span>: {
                <span class="pl-s">"examples"</span>: {
                    <span class="pl-c1">**</span><span class="pl-v">ControlExamples</span>.<span class="pl-s1">value</span>,
                    <span class="pl-s">"sensor type"</span>: {
                        <span class="pl-s">"summary"</span>: <span class="pl-s">"Sensor Type"</span>,
                        <span class="pl-s">"value"</span>: {<span class="pl-s">"value"</span>: <span class="pl-s">"CMV4000v3 Bayer"</span>},
                    },
                }
            }
        },
    },
}


<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">get</span>(</span>
<span class="pl-en">    <span class="pl-v">V4L2_ROUTE</span> <span class="pl-c1">+</span> <span class="pl-v">V4L2Endpoints</span>.<span class="pl-s1">control</span> <span class="pl-c1">+</span> <span class="pl-s">"/{ctrl_id_or_name}"</span>,</span>
<span class="pl-en">    <span class="pl-s1">response_model</span><span class="pl-c1">=</span><span class="pl-v">ControlValue</span>,</span>
<span class="pl-en">    <span class="pl-s1">responses</span><span class="pl-c1">=</span><span class="pl-s1">get_control_responses</span>,</span>
<span class="pl-en">    <span class="pl-s1">tags</span><span class="pl-c1">=</span>[<span class="pl-v">Tags</span>.<span class="pl-s1">controls</span>],</span>
<span class="pl-en">)</span>
<span class="pl-k">def</span> <span class="pl-en">get_control</span>(
    <span class="pl-s1">ctrl_id_or_name</span>: <span class="pl-v">Union</span>[<span class="pl-s1">int</span>, <span class="pl-s1">str</span>] <span class="pl-c1">=</span> <span class="pl-v">Path</span>(  <span class="pl-c"># noqa: B008</span>
        <span class="pl-s1">description</span><span class="pl-c1">=</span><span class="pl-s">"Id or Name of v4l2 control"</span>,
        <span class="pl-s1">examples</span><span class="pl-c1">=</span>{
            <span class="pl-c1">**</span><span class="pl-v">ControlExamples</span>.<span class="pl-s1">name</span>,
            <span class="pl-s">"sensor type"</span>: {
                <span class="pl-s">"summary"</span>: <span class="pl-s">"Sensor Type"</span>,
                <span class="pl-s">"value"</span>: <span class="pl-s">"Sensor Type"</span>,
            },
        },
    ),
    <span class="pl-s1">params</span>: <span class="pl-v">CommonParams</span> <span class="pl-c1">=</span> <span class="pl-v">Depends</span>(),  <span class="pl-c"># noqa: B008</span>
) <span class="pl-c1">-&gt;</span> <span class="pl-v">JSONResponse</span>:
    <span class="pl-s">"""</span>
<span class="pl-s">    Get value of single v4l2 control</span>
<span class="pl-s"></span>
<span class="pl-s">    Supports both normal and extended controls</span>
<span class="pl-s">    """</span>
    <span class="pl-k">return</span> <span class="pl-en">check_exceptions</span>(<span class="pl-v">Camera</span>.<span class="pl-s1">get_control</span>, <span class="pl-s1">ctrl_id_or_name</span>, <span class="pl-s1">params</span>.<span class="pl-s1">dev</span>)


<span class="pl-s1">set_control_responses</span> <span class="pl-c1">=</span> {
    <span class="pl-c1">200</span>: {
        <span class="pl-s">"description"</span>: <span class="pl-s">"Success"</span>,
        <span class="pl-s">"content"</span>: {<span class="pl-s">"application/json"</span>: {<span class="pl-s">"examples"</span>: <span class="pl-v">ControlExamples</span>.<span class="pl-s1">value</span>}},
    },
}


<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">post</span>(</span>
<span class="pl-en">    <span class="pl-v">V4L2_ROUTE</span> <span class="pl-c1">+</span> <span class="pl-v">V4L2Endpoints</span>.<span class="pl-s1">control</span> <span class="pl-c1">+</span> <span class="pl-s">"/{ctrl_id_or_name}"</span>,</span>
<span class="pl-en">    <span class="pl-s1">response_model</span><span class="pl-c1">=</span><span class="pl-v">ControlValue</span>,</span>
<span class="pl-en">    <span class="pl-s1">responses</span><span class="pl-c1">=</span><span class="pl-s1">set_control_responses</span>,</span>
<span class="pl-en">    <span class="pl-s1">tags</span><span class="pl-c1">=</span>[<span class="pl-v">Tags</span>.<span class="pl-s1">controls</span>],</span>
<span class="pl-en">)</span>
<span class="pl-k">def</span> <span class="pl-en">set_control</span>(
    <span class="pl-s1">ctrl_id_or_name</span>: <span class="pl-v">Union</span>[<span class="pl-s1">int</span>, <span class="pl-s1">str</span>] <span class="pl-c1">=</span> <span class="pl-v">Path</span>(  <span class="pl-c"># noqa: B008</span>
        <span class="pl-s1">description</span><span class="pl-c1">=</span><span class="pl-s">"Id or Name of v4l2 control"</span>,
        <span class="pl-s1">examples</span><span class="pl-c1">=</span><span class="pl-v">ControlExamples</span>.<span class="pl-s1">name</span>,
    ),
    <span class="pl-s1">value</span>: <span class="pl-v">ControlValue</span> <span class="pl-c1">=</span> <span class="pl-v">Body</span>(  <span class="pl-c"># noqa: B008</span>
        <span class="pl-s1">examples</span><span class="pl-c1">=</span><span class="pl-v">ControlExamples</span>.<span class="pl-s1">value</span>, <span class="pl-s1">descripton</span><span class="pl-c1">=</span><span class="pl-s">"v4l2 control value"</span>
    ),
    <span class="pl-s1">params</span>: <span class="pl-v">CommonParams</span> <span class="pl-c1">=</span> <span class="pl-v">Depends</span>(),  <span class="pl-c"># noqa: B008</span>
) <span class="pl-c1">-&gt;</span> <span class="pl-v">JSONResponse</span>:
    <span class="pl-s">"""</span>
<span class="pl-s">    Set value of single v4l2 control</span>
<span class="pl-s"></span>
<span class="pl-s">    Supports both normal and extended controls</span>
<span class="pl-s">    """</span>
    <span class="pl-k">return</span> <span class="pl-en">check_exceptions</span>(<span class="pl-v">Camera</span>.<span class="pl-s1">set_control</span>, <span class="pl-s1">ctrl_id_or_name</span>, <span class="pl-s1">value</span>, <span class="pl-s1">params</span>.<span class="pl-s1">dev</span>)

...

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

<span class="pl-k">class</span> <span class="pl-v">TestControl</span>:
    <span class="pl-s">"""Tests for 'control' endpoint"""</span>

    <span class="pl-s1">base_route</span> <span class="pl-c1">=</span> <span class="pl-v">V4L2_ROUTE</span> <span class="pl-c1">+</span> <span class="pl-v">V4L2Endpoints</span>.<span class="pl-s1">control</span>

    <span class="pl-k">def</span> <span class="pl-en">test_get_control</span>(<span class="pl-s1">self</span>):
        <span class="pl-s1">control_name</span> <span class="pl-c1">=</span> <span class="pl-s">"exposure time, absolute"</span>
        <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">client</span>.<span class="pl-en">get</span>(<span class="pl-s1">self</span>.<span class="pl-s1">base_route</span> <span class="pl-c1">+</span> <span class="pl-s">"/"</span> <span class="pl-c1">+</span> <span class="pl-s1">control_name</span>)
        <span class="pl-k">assert</span> <span class="pl-s1">response</span>.<span class="pl-s1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">200</span>
        <span class="pl-en">parse_obj_as</span>(<span class="pl-v">ControlValue</span>, <span class="pl-s1">response</span>.<span class="pl-en">json</span>())

    <span class="pl-k">def</span> <span class="pl-en">test_get_control_bad</span>(<span class="pl-s1">self</span>):
        <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">client</span>.<span class="pl-en">get</span>(<span class="pl-s1">self</span>.<span class="pl-s1">base_route</span> <span class="pl-c1">+</span> <span class="pl-s">"/bla"</span>)
        <span class="pl-k">assert</span> <span class="pl-s1">response</span>.<span class="pl-s1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">422</span>

    <span class="pl-k">def</span> <span class="pl-en">test_set_control</span>(<span class="pl-s1">self</span>):
        <span class="pl-s1">control_name</span> <span class="pl-c1">=</span> <span class="pl-s">"exposure time, absolute"</span>
        <span class="pl-s1">control_value</span> <span class="pl-c1">=</span> <span class="pl-c1">5000</span>
        <span class="pl-en">print</span>(<span class="pl-v">ControlValue</span>.<span class="pl-en">schema_json</span>())
        <span class="pl-s1">json_value</span> <span class="pl-c1">=</span> <span class="pl-en">jsonable_encoder</span>(<span class="pl-v">ControlValue</span>(<span class="pl-s1">value</span><span class="pl-c1">=</span><span class="pl-s1">control_value</span>))
        <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">client</span>.<span class="pl-en">post</span>(
            <span class="pl-s1">self</span>.<span class="pl-s1">base_route</span> <span class="pl-c1">+</span> <span class="pl-s">"/"</span> <span class="pl-c1">+</span> <span class="pl-s1">control_name</span>,
            <span class="pl-s1">json</span><span class="pl-c1">=</span><span class="pl-s1">json_value</span>,
        )
        <span class="pl-k">assert</span> <span class="pl-s1">response</span>.<span class="pl-s1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">200</span>
        <span class="pl-k">assert</span> <span class="pl-s1">response</span>.<span class="pl-en">json</span>() <span class="pl-c1">==</span> <span class="pl-s1">json_value</span></pre></div>
<h3 dir="auto">Description</h3>
<p dir="auto">I have made an API in which I defined examples for pretty much everything: in the BaseModels, responses, Query, ...</p>
<p dir="auto">This has made interactive testing with swagger <code class="notranslate">/docs</code> pretty nice.</p>
<p dir="auto">However now I wan't to move to proper testing with <code class="notranslate">pytest</code>.</p>
<p dir="auto">I have found this: <a href="https://fastapi.tiangolo.com/tutorial/testing/" rel="nofollow">https://fastapi.tiangolo.com/tutorial/testing/</a> but I can't find information on how to make the testing more "automatic".</p>
<p dir="auto">For example, I would like to be able to re-use the examples defined in the API instead of duplicating them in the test files.</p>
<p dir="auto">And as my API interacts with HW (a v4l2 camera) I can't necessary check that the response content is right because I don't know it before hand.<br>
But it could be relevant to check that the type is right (BaseModel)?<br>
Or is that overkill because of all the internal validation?<br>
So far I came up with: <code class="notranslate">parse_obj_as(ControlValue, response.json())</code></p>
<h3 dir="auto">Operating System</h3>
<p dir="auto">Linux</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.85.0</p>
<h3 dir="auto">Python Version</h3>
<p dir="auto">Python 3.10.5</p>
<h3 dir="auto">Additional Context</h3>
<p dir="auto"><em>No response</em></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 <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 json
from fastapi import FastAPI, Header, Request
from fastapi.openapi.utils import get_openapi

app = FastAPI()


@app.get(&quot;/items/&quot;)
async def read_items(x_tenant_id: str = Header()):
    return [{&quot;hello&quot;: &quot;world&quot;}]


@app.get(&quot;/users&quot;)
async def read_users(request: Request):
    return [{&quot;tag&quot;: &quot;@jayashankarjayan&quot;}]


def custom_openapi():
    header = {
            &quot;required&quot;: True,
            &quot;schema&quot;: {
              &quot;title&quot;: &quot;X-Custom-Id&quot;,
              &quot;type&quot;: &quot;string&quot;
            },
            &quot;name&quot;: &quot;x-custom-id&quot;,
            &quot;in&quot;: &quot;header&quot;
          }
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title=&quot;Custom title&quot;,
        version=&quot;2.5.0&quot;,
        description=&quot;This is a very custom OpenAPI schema&quot;,
        routes=app.routes,
    )
    openapi_schema[&quot;info&quot;][&quot;x-logo&quot;] = {
        &quot;url&quot;: &quot;https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png&quot;
    }
    app.openapi_schema = openapi_schema

    paths = openapi_schema[&quot;paths&quot;]
    for url, methods in paths.items():
        for method in methods:
            if methods[method].get(&quot;parameters&quot;):
                methods[method][&quot;parameters&quot;].append(header)
            else:
                methods[method][&quot;parameters&quot;] = [header]

    return app.openapi_schema


app.openapi = custom_openapi"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">json</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-v">Header</span>, <span class="pl-v">Request</span>
<span class="pl-k">from</span> <span class="pl-s1">fastapi</span>.<span class="pl-s1">openapi</span>.<span class="pl-s1">utils</span> <span class="pl-k">import</span> <span class="pl-s1">get_openapi</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">"/items/"</span>)</span>
<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">read_items</span>(<span class="pl-s1">x_tenant_id</span>: <span class="pl-s1">str</span> <span class="pl-c1">=</span> <span class="pl-v">Header</span>()):
    <span class="pl-k">return</span> [{<span class="pl-s">"hello"</span>: <span class="pl-s">"world"</span>}]


<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">get</span>(<span class="pl-s">"/users"</span>)</span>
<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">read_users</span>(<span class="pl-s1">request</span>: <span class="pl-v">Request</span>):
    <span class="pl-k">return</span> [{<span class="pl-s">"tag"</span>: <span class="pl-s">"@jayashankarjayan"</span>}]


<span class="pl-k">def</span> <span class="pl-en">custom_openapi</span>():
    <span class="pl-s1">header</span> <span class="pl-c1">=</span> {
            <span class="pl-s">"required"</span>: <span class="pl-c1">True</span>,
            <span class="pl-s">"schema"</span>: {
              <span class="pl-s">"title"</span>: <span class="pl-s">"X-Custom-Id"</span>,
              <span class="pl-s">"type"</span>: <span class="pl-s">"string"</span>
            },
            <span class="pl-s">"name"</span>: <span class="pl-s">"x-custom-id"</span>,
            <span class="pl-s">"in"</span>: <span class="pl-s">"header"</span>
          }
    <span class="pl-k">if</span> <span class="pl-s1">app</span>.<span class="pl-s1">openapi_schema</span>:
        <span class="pl-k">return</span> <span class="pl-s1">app</span>.<span class="pl-s1">openapi_schema</span>
    <span class="pl-s1">openapi_schema</span> <span class="pl-c1">=</span> <span class="pl-en">get_openapi</span>(
        <span class="pl-s1">title</span><span class="pl-c1">=</span><span class="pl-s">"Custom title"</span>,
        <span class="pl-s1">version</span><span class="pl-c1">=</span><span class="pl-s">"2.5.0"</span>,
        <span class="pl-s1">description</span><span class="pl-c1">=</span><span class="pl-s">"This is a very custom OpenAPI schema"</span>,
        <span class="pl-s1">routes</span><span class="pl-c1">=</span><span class="pl-s1">app</span>.<span class="pl-s1">routes</span>,
    )
    <span class="pl-s1">openapi_schema</span>[<span class="pl-s">"info"</span>][<span class="pl-s">"x-logo"</span>] <span class="pl-c1">=</span> {
        <span class="pl-s">"url"</span>: <span class="pl-s">"https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"</span>
    }
    <span class="pl-s1">app</span>.<span class="pl-s1">openapi_schema</span> <span class="pl-c1">=</span> <span class="pl-s1">openapi_schema</span>

    <span class="pl-s1">paths</span> <span class="pl-c1">=</span> <span class="pl-s1">openapi_schema</span>[<span class="pl-s">"paths"</span>]
    <span class="pl-k">for</span> <span class="pl-s1">url</span>, <span class="pl-s1">methods</span> <span class="pl-c1">in</span> <span class="pl-s1">paths</span>.<span class="pl-en">items</span>():
        <span class="pl-k">for</span> <span class="pl-s1">method</span> <span class="pl-c1">in</span> <span class="pl-s1">methods</span>:
            <span class="pl-k">if</span> <span class="pl-s1">methods</span>[<span class="pl-s1">method</span>].<span class="pl-en">get</span>(<span class="pl-s">"parameters"</span>):
                <span class="pl-s1">methods</span>[<span class="pl-s1">method</span>][<span class="pl-s">"parameters"</span>].<span class="pl-en">append</span>(<span class="pl-s1">header</span>)
            <span class="pl-k">else</span>:
                <span class="pl-s1">methods</span>[<span class="pl-s1">method</span>][<span class="pl-s">"parameters"</span>] <span class="pl-c1">=</span> [<span class="pl-s1">header</span>]

    <span class="pl-k">return</span> <span class="pl-s1">app</span>.<span class="pl-s1">openapi_schema</span>


<span class="pl-s1">app</span>.<span class="pl-s1">openapi</span> <span class="pl-c1">=</span> <span class="pl-s1">custom_openapi</span></pre></div>
<h3 dir="auto">Description</h3>
<p dir="auto">I have a requirement in my application where all my APIs spread across multiple routers are required to have custom headers, eg: <code class="notranslate">x-custom-header</code>. I want these headers to be visible as fields on the Swagger UI as well so that a user who accesses this Swagger UI can use the API endpoints by providing valid values to the headers.</p>
<p dir="auto">I have two options -</p>
<ol dir="auto">
<li>I add the header as an argument to every single route individually. This introduces a lot of duplication as I have to repeat the same arguments over multiple routes.</li>
<li>I modify the OpenAPI Schema (as seen in the above code snippet) and add the header across all routes at one go. While this works, it seems more like a somewhat ugly hack.</li>
</ol>
<p dir="auto">I would like to know that is there a better, elegant option than the aforementioned one which I can use to get header fields in my Swagger UI while avoiding duplication in my code and keeping it elegant.</p>
<p dir="auto">All help is appreciated. Thanks!</p>
<h3 dir="auto">Operating System</h3>
<p dir="auto">Linux, Windows, 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.8.9</p>
<h3 dir="auto">Additional Context</h3>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/20151948/207940178-8cc90f2a-f697-4177-bd5e-d749229acaf3.png"><img width="1509" alt="image" src="https://user-images.githubusercontent.com/20151948/207940178-8cc90f2a-f697-4177-bd5e-d749229acaf3.png" style="max-width: 100%;"></a></p>
<p dir="auto">This is how I would like the custom header to appear without actually mentioning it explicitly on the route itself (the result seen in the screenshot can be replicated using the code snippet shared above)</p>
<p dir="auto">Kindly note that there could be multiple such custom headers that I want to see on my Swagger UI</p>