Skip to main content
FastQL resolves type annotations lazily at Schema(...) build time, not at decoration time. This means you can use string annotations and Python ForwardRefs to define types that refer to each other — enabling circular and mutually-recursive type graphs without import cycles.

String annotations (lazy by default with from __future__ import annotations)

Adding from __future__ import annotations at the top of a module makes all annotations strings automatically. FastQL reads these strings and resolves them against the module’s globals at schema build time.

Explicit string annotations

Without from __future__ import annotations, wrap any forward-referenced type in a string:
FastQL’s annotation resolver handles Optional, Union (X | None), and list[...] inside string annotations, matching the same rules as real type hints.

How resolution works

FastQL’s annotation engine (in fastql.decorators.annotations) performs the following steps at Schema(...) build time:
  1. String / ForwardRef: creates a TypeReference(name, module) placeholder.
  2. Schema builder indexes all decorated types by their Python class name.
  3. Resolution pass walks the reachable type graph, replacing each TypeReference with its concrete GraphQL type.
If a referenced name cannot be resolved, Schema(...) raises a descriptive LookupError naming the unresolved type.

Circular types

Circular references — where A references B and B references A — are fully supported. The schema builder tracks types it has already started building and returns a placeholder that is filled in once both sides complete.
Circular input types are valid in FastQL’s type system but are rejected by the GraphQL specification for inputs — attempting to build a schema with a circular Input graph will raise a validation error at Schema(...) build time.

Troubleshooting