The dependency graph: where policy and resources enter
FastAPI dependencies answer a problem that ordinary request parsing cannot: how can several endpoints share authentication, database access, or common query logic without copying the same work into every function? This chapter treats a dependency as a declared input in a request-scoped graph. That model explains recursive solving, per-request caching, security integration, resource cleanup, and test substitution, while keeping identity policy and data ownership where they belong: in the application.
A dependency is a declared input
A FastAPI dependency is usually a function that looks much like a path-operation function. It can declare query parameters, headers, models, or its own dependencies. The difference is that it is not registered as an endpoint for a URL. Instead, an endpoint declares that it depends on the callable, commonly through Depends.
That declaration changes who calls the function. In ordinary Python, the endpoint would call a helper, pass arguments to it, and decide what to do with the return value. With Depends, FastAPI inspects the helper's signature, determines what the helper requires, obtains those values from the request, calls the helper at the appropriate point, and supplies its result to the endpoint. The endpoint receives a ready dependency value rather than a recipe for producing it.
Imagine that several operations need the same optional search terms, offset, and limit. A shared dependency can declare those values once and return a small object containing them. The item and user operations can each declare that dependency. FastAPI then validates the query parameters for both operations and calls the shared function when each request arrives. The code that owns the shared rule is separated from the endpoints that use its result, but the request contract remains visible.
The dependency itself is still part of the endpoint's contract. If it requires a header, query parameter, or body field, that requirement affects the operation that depends on it. FastAPI includes the dependency's request parameters and validation rules when it builds the operation's OpenAPI description. A dependency is therefore not an invisible helper hidden from clients; it is a reusable source of request behavior and metadata.
The same mechanism supports callable instances. A class can keep configuration in its instance and expose a __call__ method whose signature FastAPI inspects. This is useful when the behavior needs a parameter chosen while the application is assembled, such as a checker with a fixed condition. FastAPI cares about the callable interface used at request time; the application can construct and configure the instance beforehand.
This is also why Annotated aliases can make a large application easier to read. An application can give a reusable name to a type plus its dependency declaration, then use that name in multiple endpoint signatures. The type information remains available to editors and type checkers while the dependency relationship remains explicit to FastAPI. The gain is not just fewer characters. It keeps the shared boundary recognizable when the same policy appears in many operations.
Solving the graph before the endpoint
When FastAPI registers an operation, it analyzes the endpoint and follows its dependency declarations recursively. The result can be pictured as a graph whose final node is the endpoint and whose earlier nodes provide values or enforce conditions. A dependency can itself depend on another dependency, so the graph is deeper than a flat list of callbacks.
Suppose an endpoint depends on a query_or_cookie function. That function depends on a query_extractor, and also reads a cookie when the query is absent. The endpoint declares only the outer dependency. FastAPI sees the inner edge, solves the extractor first, passes its result to query_or_cookie, and then passes the outer result to the endpoint. The order follows the relationships between nodes rather than the order in which the developer manually wrote helper calls.
At request time, the solver works from the leaves upward. It first resolves a sub-dependency's own inputs, then calls that sub-dependency, records its result under the parameter name that requested it, and uses the result while resolving the parent. Only after the required child values are available can the parent run. If a field at any level is invalid, the error is carried upward and the endpoint is not called.
This recursive structure makes shared policy possible. A current-user dependency might depend on a token extractor. An active-user dependency might depend on current-user. An administrator-only operation might depend on active-user and then an administrator check. Each endpoint declares the policy level it needs, while the framework handles the path from request data to the final decision. The graph does not make the policy correct by itself; it gives the policy a consistent place to run.
FastAPI normally caches a dependency result within one request. If two branches of the graph need the same sub-dependency, the framework can reuse the value rather than calling the sub-dependency twice for that request. This is useful for work such as reading a common user record or constructing a shared request context. The cache is request-scoped: it does not turn the result into a process-wide singleton, and it does not promise that the underlying function is safe to share across requests.
There are cases where repeated evaluation is intentional. A dependency can opt out of the per-request cache with use_cache=False. That distinction matters for a value that must be refreshed at each use, but it should be made consciously. Disabling caching can change both cost and behavior; enabling it can reuse a value that the application assumed would be recomputed. The declaration is expressing a lifetime decision, not only a convenience switch.
Dependency overrides expose another edge of the graph. During testing, an application can map an original dependency to a replacement. FastAPI uses the replacement when solving the graph, so an external authentication service or a paid provider does not have to run in every test. The override can return a fixed test user or test settings while the endpoint remains exercised through the same HTTP boundary. Once the tests finish, the override should be reset so it does not leak into other cases.
The graph shows several different kinds of edges. Request data feeds a dependency, a security dependency can feed a policy check, and a yielded resource feeds the endpoint while also creating a cleanup obligation. A test override replaces one external edge; it does not change the endpoint's business code. The OpenAPI edge is descriptive rather than a second request call: it records the security scheme associated with the operation.
Security is a dependency with a public description
FastAPI's OAuth2 helpers make the graph concrete. OAuth2PasswordBearer is a callable object that can be used with Depends. At request time it looks for an Authorization header with the Bearer scheme and returns the token string. If the header is missing or does not have the expected scheme, the default behavior raises a 401 error before the endpoint runs.
That is already an enforcement point, but it is not the entire identity system. The helper does not decide which user the token represents, whether the token has been revoked, or whether that user may modify a particular object. An application commonly adds another dependency that verifies the token, loads a user, and applies a policy. The graph lets those steps be shared and ordered; it does not hide their choices.
The helper also carries an OAuth2 flow model. FastAPI can recognize security helpers because they inherit from its security base, then use that metadata while generating OpenAPI. The operation can therefore show a security scheme and the interactive documentation can expose an authorization interface. One declaration is doing two related jobs: it places a check in the runtime path and tells consumers what kind of credential the operation expects.
The two jobs can still diverge if the application configures them carelessly. A documented Bearer scheme does not prove that token issuance is secure. A JWT is signed, not automatically encrypted, so its contents should not be treated as secret. Passwords need secure hashing rather than plaintext storage. Key management, expiration, revocation, HTTPS, and authorization rules remain application and operations responsibilities. FastAPI makes standard mechanisms easier to connect, but it does not turn an example security flow into a complete threat model.
This distinction between authentication and authorization is worth preserving. Authentication asks who or what presented a credential. Authorization asks what that identity may do in this context. A dependency can participate in both questions, but the questions are not interchangeable. Returning a current user from a token dependency is not the same as proving that the user owns the item identified by a path parameter. The request graph can make both checks visible without pretending they are one rule.
A dependency can own a resource lifetime
Dependencies are also useful when the value has to be acquired before the endpoint and released afterward. A dependency that uses yield runs its setup code, yields a value for the endpoint and other dependencies, and then runs cleanup code after the request reaches the relevant lifetime boundary. A database session is the familiar example: open or obtain the session, use it through the request, then close it.
Nested yielded dependencies need an orderly teardown. If one dependency uses a resource supplied by another, the outer cleanup must not close its prerequisite too early. FastAPI uses context-manager behavior and exit stacks to preserve the relationship. This is not merely syntactic neatness. Closing a session, file, or network resource at the wrong point can produce failures after an endpoint appears to have succeeded.
The default scope for a yielded dependency keeps it alive through the response lifecycle described by the current FastAPI behavior. With scope="function", cleanup can happen after the endpoint function returns but before the response is sent. The choice depends on whether response generation or streaming still needs the resource. A session used only to authorize a request may not need to remain open while a slow stream travels to the client; a session needed while producing the stream does.
This is a place where a declaration expresses ownership but not every policy. FastAPI can arrange when the dependency's exit code runs. The application still decides transaction boundaries, rollback behavior, pool configuration, and what data may be used after the session closes. For resources shared across many requests, application lifespan is a different scope again: startup and shutdown logic can load a model or create a pool once for the application rather than once per request.
The dependency graph therefore has three practical meanings at once. It is a data-flow graph because values move from request inputs to endpoint parameters. It is a policy graph because security and permission checks can be placed before an operation. It is a lifetime graph because yielded resources must remain available until their consumers finish. Treating all three meanings as “just helper functions” hides the very relationships FastAPI is designed to make explicit.
The next boundary is the value that leaves the endpoint. Dependencies can establish who is calling and which resources are available, but the application still needs to decide what internal object may become public. Response models, router composition, and OpenAPI carry the declaration-centered model outward to clients and documentation.