Attaches a developer-supplied predicate to a field, an argument, or an input field. The reflected static method takes the surrounding table and the relevant scalar values, and returns a jOOQ Condition. The rewrite folds that Condition into the generated WHERE clause: by default in addition to the implicit column = ? predicates the rewrite would otherwise emit, or replacing them when override: true.
|
Names from inside the generator. This page names types from graphitron’s own classification model. They are accurate today, and worth knowing when a rejection message quotes one back at you, but they are not part of the authoring contract: the classification walk that produces them is being drained, and these names retire with it. What stays stable is the schema you write, the directive reference that describes it, and the diagnostics glossary. |
@condition is the escape hatch for predicates Graphitron can’t infer from the catalog: regex matches, range filters, date arithmetic, joins through correlated subqueries, vendor-specific predicates, multi-column boolean shapes. It also drives the dynamic enclosingOverride cascade at field level: an outer field-level @condition(override: true) suppresses the implicit-column predicates from an input-typed argument, but inner argument-level @condition annotations still fire. (The rewrite preserves inner explicit conditions, the legacy generator dropped them, see filmsOuterOverrideFilterInput in the example schema for the test that pins this divergence.)
SDL signature
directive @condition(
condition: ExternalCodeReference
override: Boolean = false
contextArguments: [String!]
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
|
|
(required in practice) |
Points at the static Java method that returns the |
|
|
|
When |
|
|
|
Names of values to pull from the GraphQL |
Canonical examples
Argument-level, no override (the explicit predicate is AND-ed with the implicit column IN (…)):
type Query {
citiesByName(
cityNames: [String!] @field(name: "CITY")
@condition(condition: {className: "com.example.CityConditions", method: "cityMethod"})
): [City]
}
Field-level, no override (the explicit predicate is AND-ed with the implicit predicates for every argument):
type Query {
cities(
countryId: String! @field(name: "COUNTRY_ID"),
cityNames: [String!] @field(name: "CITY")
): [City] @condition(condition: {className: "com.example.CityConditions", method: "cityMethod"})
}
Field-level, override: true on a filter input (its fields resolve against film, the query’s return-type table). The outer @condition runs, the implicit column-equality predicates from FilmConditionInput are suppressed, but an inner @condition on FilmConditionInput.filmId still fires:
type Query {
filmsOuterOverrideFilterInput(filter: FilmConditionInput): [Film!]!
@condition(
condition: {
className: "com.example.conditions.InputFieldConditionFixtures",
method: "outerOverrideMethod"
},
override: true
)
}
input FilmConditionInput {
filmId: Int @field(name: "FILM_ID")
@condition(condition: {
className: "com.example.conditions.InputFieldConditionFixtures",
method: "filmIdCondition"
})
}
Net effect: (film_id >= 2) AND (film_id = 1) — both the outer override and the inner explicit @condition are honoured, even though the outer override is true.
Multi-segment input path with a nested input. The two-level nesting lets a leaf-level @condition reference its enclosing-input field by path:
input NestedFilmInput {
inner: InnerFilmInput
}
input InnerFilmInput {
filmId: Int @condition(condition: {
className: "com.example.conditions.PlainFilmIdConditions",
method: "filmIdCondition"
})
}
The rewrite extracts inner.filmId from the outer argument map and passes it to filmIdCondition as the bound parameter.
Context-argument flow. The reflected method receives the table, any explicit input parameters, and the named context values:
type Query {
citiesForCurrentUser: [City]
@condition(
condition: {className: "com.example.CityConditions", method: "tenantScoped"},
contextArguments: ["tenantId"]
)
}
tenantScoped(City table, UUID tenantId) runs at request time with tenantId pulled from GraphQLContext.
FK-target @nodeId filter. When an input field carries both @nodeId(typeName: "X") and @condition, the field decodes an opaque id for a foreign-key-linked table X (reached from the consuming field’s table through a catalog foreign key). The reflected method’s table parameter is then X, not the consumer’s table (soknadsmangeltype here, the return-type table of the query consuming this filter):
input SoknadsmangeltypeFilterInput {
regelverksamlingId: ID! @nodeId(typeName: "Regelverksamling")
@condition(condition: {
className: "com.example.conditions.SoknadsmangeltypeConditions",
method: "iRegelverksamling"
}, override: true)
}
The method signature is iRegelverksamling(Regelverksamling rs, String regelverksamligId): it receives an alias for the FK-target Regelverksamling, and the rewrite wraps the returned Condition in a correlated subquery, EXISTS (SELECT 1 FROM regelverksamling rs WHERE soknadsmangeltype.<fk> = rs.<pk> AND <your predicate>), so the predicate runs against X while the outer row set is preserved. The developer method is identical to any other @condition method; it neither knows nor cares that its Condition lands inside an EXISTS rather than the top-level WHERE. Composite-key NodeType targets are supported: the correlation ANDs every foreign-key column pair (EXISTS (SELECT 1 FROM project p WHERE p.org_id = project_note.org_id AND p.project_id = project_note.project_id AND <your predicate>)). The same EXISTS is emitted wherever the predicate lands, including inline child-field fetchers and @splitQuery batched fetchers, not only top-level query conditions.
Constraints
-
Applies on
FIELD_DEFINITION,ARGUMENT_DEFINITION, andINPUT_FIELD_DEFINITION. The element type fixes the predicate’s scope: argument-level conditions can only see their own argument and any context arguments, field-level conditions see all of the field’s arguments, input-field-level conditions see the input’s enclosing argument map (the rewrite extracts the leaf value via the path from the outer argument). -
condition.className:andcondition.method:are both required for a meaningful directive. A@conditionwhose reference resolves to no method (missingclassName:ormethod:) surfaces asfield '<name>' @condition: …orargument '<name>' @condition: …. -
argMapping:is supported on@condition(unlike on@externalField,@enum,@sourceRow). Use it to rebind a GraphQL argument name to a differently-named Java parameter, the rewrite carries the mapping into the reflected method’s parameter list. The right-hand side may also walk into a nested input field; the form is the same at every directive and is described once in Binding a parameter to a nested input field. -
contextArguments:values must already be wired into the runtimeGraphQLContextmap, the rewrite passes the listed keys through unmodified. A typo here surfaces as anullparameter at request time, not as a build error. -
Field-level
override: truesuppresses the implicit predicates from the field’s direct arguments and from any input-typed argument nested under them. Inner explicit@conditionannotations on the input’s fields are preserved, override does not cascade through them. (Divergence from the legacy generator: legacy semantics dropped them. The rewrite’s behaviour is the intended one andfilmsOuterOverrideFilterInputpins it as a regression-fence.) -
Argument-level
override: trueis local: it suppresses the implicitcolumn = ?for that one argument, nothing else. -
An input field carrying both
@nodeId(typeName: "X")and@conditionbinds the condition method’s table parameter to the FK-target tableX(the table the decoded id identifies), reached through the catalog foreign key from the consuming field’s table. The predicate is emitted as a correlatedEXISTSover that foreign key, so the method receives an alias forXrather than the consumer’s table. Single-column and composite-key NodeType targets are both supported (the composite correlation ANDs every foreign-key column pair). This holds at every emission site: top-level query conditions, inline child-field fetchers, and@splitQuerybatched fetchers. -
On a
@nodeIdleaf whose route to the target table does not resolve,override: truetakes the wholeWHEREcontribution: the method receives the resolving table (each participant’s own table on a multi-table consumer) plus the raw wire id, and decodes it with the generatedNodeIdEncoderhelpers. This is the ladder’s second rung: where the route does resolve the FK-target contract above applies unchanged, andoverride: falseplus an unresolvable route still fails the build. A leaf on which some participants route and others do not is rejected, one method cannot mean both tables. See Multitable filter inputs. -
Nested input types must be carrier-mapped to flow through
@condition. A class-backed input maps to its Java record (and any input above it must also be a Java record); a jOOQ-record-backed input works only at the leaf (jOOQ records can not contain other records). An input is class-backed when the Java parameter it flows into is that record type; leaving it without a carrier routes through thePojoInputTypepath, which works for scalar paths at any depth. -
Listed nested input types (
[InnerInput]inside an outer record) require a Java record carrier on the outer input, the rewrite needs a stable Java type for the list element.
See also
-
@referencesupplies catalog-driven joins that@conditioncan layer additional filtering on. -
@field(name: …)is the column-binding axis,@conditionand@fieldcoexist on the same slot,@fielddrives the implicit predicate,@conditionadds (or replaces) the explicit one. -
An input type’s backing class determines the carrier the condition method reads from: the carrier is the Java class the method parameter is typed as (inferred, not declared); a carrier-free input arrives as the runtime
Maptraversal’s leaf values. -
@serviceis the alternative when a whole fetcher is custom code,@conditionis for adjusting a single predicate inside an otherwise generated query. -
How-to: Stacking and overriding conditions covers the override cascade rules, nested-input record conventions, and the divergence from legacy semantics.