Replaces the generated DB operation for a field with a call to an external Java method. Use it when the operation cannot be expressed by the directive surface — multi-statement transactions, business-logic-heavy reads, third-party calls, or shapes the framework does not (yet) cover.
SDL signature
directive @service(
service: ExternalCodeReference!,
contextArguments: [String!]
) on FIELD_DEFINITION
input ExternalCodeReference {
className: String
method: String
argMapping: String
}
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
|
|
(required) |
The Java target. |
|
|
none |
Names of values pulled from the request |
Canonical example
The example schema’s root services hand off three different return shapes:
type Query {
filmsByService(ids: [Int!]!): [Film!]!
@service(service: {
className: "no.sikt.graphitron.rewrite.test.services.SampleQueryService",
method: "filmsByService"
})
filmsByServiceRenamed(ids: [Int!]!): [Film!]!
@service(service: {
className: "no.sikt.graphitron.rewrite.test.services.SampleQueryService",
method: "filmsByServiceRenamed",
argMapping: "filmIds: ids"
})
filmCount: Int!
@service(service: {
className: "no.sikt.graphitron.rewrite.test.services.SampleQueryService",
method: "filmCount"
})
}
filmsByService returns a Result<FilmRecord> directly; the framework treats jOOQ records as already-populated rows and skips its own projection. filmsByServiceRenamed shows argMapping: the GraphQL arg ids binds to the Java parameter filmIds because the service method’s signature reads more naturally that way. filmCount returns a plain scalar.
The Java surface for the first method:
public class SampleQueryService {
public SampleQueryService(DSLContext context) { … }
public Result<FilmRecord> filmsByService(List<Integer> ids) { … }
}
The framework constructs the service with whatever it can resolve from the request context (a DSLContext for the request’s database session is the most common parameter); the rest of the method signature is the field’s GraphQL arguments, mapped by name (or by argMapping overrides).
Constraints
-
service.classNameis required. graphql-java rejects a no-arg@serviceat parse time. -
service.methoddefaults to the GraphQL field name. Set it only when the Java method’s name diverges. -
argMappingis parsed as"javaParam: graphqlArg"entries, comma-separated. Whitespace around:and,is tolerated; multi-line text-block input is accepted. Empty string is identity. -
@serviceand@mutationare mutually exclusive on the same field. Use@mutationfor fully-generated DB writes,@servicefor custom logic. -
On non-root fields,
@servicerequires@splitQuery: the parent’s selection set is collected first, the service method receives aSetof parent records (only their primary-key columns populated), and returns aMapfrom those keys to results. Single-statement nested resolution is not supported on the service path. -
Conditions and other DB-customisation directives on the same field are ignored: the service is opaque and the generator does not splice generated SQL into a custom method’s body.
See also
-
How-to: Handle services covers nested-input mapping, response shaping, and the
@splitQuerypattern in depth. -
@mutationfor the framework-generated alternative. -
@tableMethodfor the lighter-weight "developer returns aTableand the framework projects" variant. -
How-to: Test your schema for the consumer test pattern with services in play.