graphitron-maven-plugin exposes three Maven goals: generate (the build-time codegen pipeline), validate (a faster schema-only run that emits no Java), and dev (the editor-loop server). Each goal accepts the same handful of <configuration> parameters; dev adds three of its own. Two of the parameters (<schemaInputs> and <namedReferences>) are list-of-complex-objects with their own POM bindings.

This page is the parameter-by-parameter reference. The tutorial’s first chapter introduces the canonical <configuration> shape with one schema input and the two required packages; the directive reference under reference/directives covers what each schema input declares.

Goals

Goal Default phase Purpose Required parameters

generate

generate-sources

Run the rewrite pipeline (load → attribute → classify → validate → emit) and write Java sources under <outputDirectory>. Adds <outputDirectory> as a compile source root so the consumer’s compile phase picks the generated code up automatically.

<outputPackage>, <jooqPackage>

validate

validate

Run load → attribute → classify → validate only. No Java is emitted; the pipeline stops after validation. Useful as a CI gate when you want a schema check without paying for the full code generation.

(none; both packages are optional, a sentinel is substituted)

dev

(none; CLI-only)

Run the LSP server on 127.0.0.1:8487 and watch <schemaInputs> for writes matching <schemaFileExtensions> (default .graphqls / .graphql) plus the consumer’s compiled jOOQ output for .class changes. Re-runs the generator on every save (debounced) and rebuilds the in-process catalog atomically. Stop with Ctrl+C.

<outputPackage>, <jooqPackage>

The plugin coordinates are no.sikt:graphitron-maven-plugin; bind a goal to a <execution> block under the rewrite plugin’s <executions> to wire it into the standard Maven lifecycle, or invoke from the command line with mvn graphitron:<goal>.

Shared parameters

The following parameters apply to every goal:

Name Type Default Description

outputDirectory

String (path)

${project.build.directory}/generated-sources/graphitron

Directory the generate goal writes Java sources into. Resolved against the project basedir if relative. The generate goal also adds this directory as a compile source root so the consumer’s compile phase picks up the generated code; validate and dev honour the parameter for their own context-construction but do not add the source root.

outputPackage

String (Java package)

(none)

Root Java package the generator writes under. Required for generate and dev; optional for validate (a sentinel package is substituted because the validate goal never emits code). The Graphitron facade lands at <outputPackage>.Graphitron and GraphitronContext at <outputPackage>.schema.GraphitronContext.

jooqPackage

String (Java package)

(none)

Root Java package of the consumer’s jOOQ-generated catalog (the Tables / Keys / Routines classes). Required for generate and dev; optional for validate. The classifier resolves every @table and @field against the catalog rooted at this package.

schemaInputs

List<SchemaInputBinding>

empty

List of <schemaInput> entries pointing at schema files (or globs that expand to schema files) the loader assembles into one schema. By default schema files are those ending in .graphqls or .graphql; the <schemaFileExtensions> parameter narrows or extends that set. Order matters when two files declare the same type with overlapping fields; later entries win. See <schemaInput> binding for the per-entry shape.

namedReferences

List<NamedReferenceBinding>

empty

Reusable nameclassName mappings the SDL refers to via ExternalCodeReference.name: instead of repeating a fully qualified class name at every directive site. Each entry carries <name> and <className> children. Empty by default; populated only when the same external class is referenced from many places.

schemaFileExtensions

List<String>

[".graphqls", ".graphql"]

File-name suffixes that count as GraphQL schema files. Drives three places at once: the <schemaInputs> post-scan filter (matches are kept only when the filename ends in a configured suffix), the graphitron:dev watcher’s trigger filter (only writes matching a configured suffix fire a regenerate), and the orphan-file scanner that runs when schema validation fails (only files with a configured suffix can be reported as orphans). Leading dots are optional (graphqls normalises to .graphqls); case is preserved. A configured but empty list is rejected at Mojo execute; omit the parameter to accept the default.

lint

LintBinding

(none)

Lint suppression. A <lint> block naming rule ids to silence everywhere (<disabledRules>) and type-name globs to skip in the SDL lint engine (<excludedTypes>). Applied build-side, so the build log, the graphitron:dev editor squiggles, and the MCP diagnostics tool suppress identically. A disabled rule id that resolves to no rule fails the build with the list of valid ids. Omit the block to lint every author-owned type with every rule. See Silencing lint warnings.

sessionState

SessionStateBinding

(none)

Session identity (R429). A <sessionState> block naming how per-request identity is mounted on the pinned connection: either consumer-authored database callables (<connect call> / <disconnect call>, with an optional OUT handle, plus the optional <stateSurvivesTransactions> survival declaration) or the Postgres <variables> sugar (<variable name claim>) that generates both hook halves. Graphitron emits a SessionHook from the block that runs at connection acquisition and release; an undeclared function-hook pair is additionally re-fired after each mutation-field transaction settle. An unpaired or handle-inconsistent block fails the build. Omit to mount no identity. See Session identity.

devDatabase

DevDatabaseBinding

(none)

Dev database for the MCP execute tool (dev goal only, R428). A <devDatabase> block carrying plain connection coordinates: <url>, <user>, <password>, <dialect> (explicit and enumerated, POSTGRES or ORACLE, never defaulted), the <claims> payload handed to the <sessionState> connect hook (inline, or @/path/to/file; the file is re-read on every call), and the <allowClaimsOverride> opt-in for a per-call claims argument (default off). Environment variables override the POM on every field (GRAPHITRON_DEV_DB_URL, GRAPHITRON_DEV_DB_USER, GRAPHITRON_DEV_DB_PASSWORD, GRAPHITRON_DEV_DB_DIALECT, GRAPHITRON_DEV_CLAIMS, GRAPHITRON_DEV_DB_ALLOW_CLAIMS_OVERRIDE), so credentials stay out of the checked-in file. With no url from either source the execute tool is simply absent and every other dev tool works without it; a url with a missing or unsupported dialect fails the goal. See Run a query without an app server.

dev-goal parameters

Three watch-loop knobs unique to the dev goal, each driven from a CLI property:

Name Type Default CLI property Description

port

int

8487

graphitron.dev.port

TCP port the LSP server binds on 127.0.0.1. The plugin fails fast with a clear message when the port is in use, naming the override property.

debounceMs

long

300

graphitron.dev.debounceMs

Debounce window for both the schema watcher and the classpath watcher. Multiple .graphqls writes inside the window collapse into one regenerate; the same applies to .class changes for the catalog rebuild. Lower for snappier response, higher for noisy editors.

skipInitial

boolean

false

graphitron.dev.skipInitial

Skip the initial generator pass at startup. Useful when the consumer has just run mvn graphitron:generate and the generated sources are already on disk; the dev loop still re-runs on every subsequent save.

compile

boolean

true

graphitron.dev.compile

Compile the generated sources in-process into target/graphitron-classes/<outputPackage>/ (a Graphitron-exclusive directory), incrementally on every save. This runnable image is what the in-process MCP query tools execute against. Set to false to fall back to generate-only behaviour (giving up the query tools). No fail-fast: because the output directory is Graphitron’s alone, any misconfiguration degrades to generate-only rather than corrupting bytecode. See Compiled generated classes.

The CLI properties take precedence over <configuration> values, so mvn graphitron:dev -Dgraphitron.dev.port=9090 works without editing the POM.

Quarkus dev-mode interaction

Consumers running their app under quarkus:dev alongside graphitron:dev may find that schema edits regenerate sources but the running app keeps serving the old GraphQLSchema until they press s in the Quarkus console. The cause is Quarkus’s bytecode-instrumentation hot-swap: it sees the regenerated .java files, decides the changes are method-body-compatible, and replaces classes in place rather than restarting. The schema bean is built once at startup, so a hot-swap doesn’t rebuild it. The matching log line is Files changed but restart not needed - notified extensions: Quarkus saw the change, asked its extensions, and none claimed it as restart-worthy.

Set quarkus.live-reload.instrumentation=false in application.properties to disable instrumentation hot-swap entirely. Every change triggers a full restart, including the schema rebuild. The cost is losing hot-swap on hand-edited Java; for a schema-driven app that is usually the right trade. Putting the .graphqls files on a watched path does not help on its own — Quarkus already watches src/main/resources/, but a change there is still classified by extension claims, and no shipped Quarkus extension claims .graphqls as restart-worthy.

<schemaInput> binding

Each <schemaInput> carries three optional children. The pattern is the only one most consumers need:

Child Type Description

pattern

String (Ant-style glob)

Path or Ant glob relative to the project basedir, expanding to one or more .graphqls files. A literal path matches one file; a glob like src/main/resources/graphql/*/.graphqls matches every schema file under the directory. Both shapes fan into the same loader.

tag

String

Optional tag applied to every type, field, argument, and enum value defined in matched files. Emitted as a @tag(name: "…​") directive on each element. The plugin treats this as an implicit Federation 2 opt-in: when set without an @link in the SDL, the plugin synthesises one with import: ["@tag"]; when set with an @link whose import list omits "@tag", the build fails with a fatal error pointing at the @link. How-to: Apollo Federation transport covers the federation interaction.

descriptionNote

String

Optional sentence appended to every element’s description. Useful for marking a subgraph’s elements with a provenance hint (e.g. "From the orders subgraph") without editing the SDL by hand.

The minimum useful entry is one pattern:

<schemaInputs>
    <schemaInput>
        <pattern>src/main/resources/graphql/schema.graphqls</pattern>
    </schemaInput>
</schemaInputs>

<namedReference> binding

Each <namedReference> carries two required children:

Child Type Description

name

String

Lookup key the SDL uses to refer to this reference. Matched against the name: argument of ExternalCodeReference-bearing directives (see @condition, @externalField, @service). Names live in a flat namespace; collisions are an author error.

className

String (FQCN)

Fully qualified Java class name the reference resolves to. The classifier checks this against the consumer module’s classpath and fails the build with the offending name if the class can’t be resolved.

<namedReferences> is empty by default; consumers populate it when the same external class is referenced from many places and the FQCN repetition becomes painful. The directive-site form (without name:) takes a literal className, so this binding is purely an indirection and never adds new capability.

Silencing lint warnings

Graphitron’s schema linter reports style and convention warnings (naming, missing descriptions, deprecated-directive usage, and so on). Every rule is on by default. To silence a rule you disagree with, or to exclude types you cannot change, add a <lint> block to the plugin configuration:

<configuration>
  <lint>
    <!-- Turn a rule off everywhere, by its rule id. -->
    <disabledRules>
      <rule>input-object-name-suffix</rule>
      <rule>types-and-fields-have-descriptions</rule>
    </disabledRules>
    <!-- Skip linting types whose name matches a pattern (glob). -->
    <excludedTypes>
      <type>Legacy*</type>
    </excludedTypes>
  </lint>
</configuration>

Rule ids are the kebab-case names shown in each warning and in the graphitron:diagnostics MCP tool. A misspelled rule id fails the build with the list of valid ids.

disabledRules silences a rule everywhere it would fire: the build log, your editor’s squiggles, and the MCP diagnostics tool. excludedTypes skips the schema linter’s checks on the matching types (glob syntax: * matches any run of characters, ? one character). A handful of advisories come from graphitron’s schema classifier rather than the linter (for example redundant-record-directive); these are not tied to a single type name, so they are silenced by rule id in disabledRules, not by excludedTypes.

The <lint> block carries two list children:

Child Type Description

disabledRules

List<String> (<rule> entries)

Rule ids to silence everywhere. Each is validated against the built-in rule set at build start; an unknown id fails the build.

excludedTypes

List<String> (<type> entries)

Type-name globs whose matching types the SDL lint engine skips. Engine-scoped: a classifier advisory on an excluded type still fires and must be silenced through disabledRules.

Session identity

When you run the owned-connection engine (runtime.newGraphQL(schema)), graphitron pins one database connection per GraphQL operation. A <sessionState> block tells it how to mount per-request identity on that connection, at acquisition, and unmount it at release, so every generated query and mutation runs under the caller’s identity without any per-fetcher wiring. Omit the block and no identity is mounted.

There are two forms; configure one, not both.

Function-hook form

Name consumer-authored database callables. connect mounts identity from the opaque claims payload (typically the request’s JWT); disconnect unmounts it. This is the form to use on Oracle, and whenever identity lives behind a privilege fence (a definer-rights package, VPD, RAS):

<configuration>
  <sessionState>
    <connect>                          <!-- (p_claims IN, p_handle OUT) -->
      <call>Pk_Ras.Connect</call>
      <handle>true</handle>
    </connect>
    <disconnect>                       <!-- (p_handle IN) -->
      <call>Pk_Ras.Disconnect</call>
      <handle>true</handle>
    </disconnect>
  </sessionState>
</configuration>

<handle>true</handle> declares that connect returns an opaque OUT handle (for example a RAS session id) that disconnect is later called with. It must be declared on both sides or neither; a handle produced by one side and not bound by the other fails the build. Everything domain-shaped, claim parsing, entitlement filtering, session management, lives inside the callables, in the database’s own language; graphitron only guarantees the pair runs at mount and unmount.

Your callables must set and reset session-scoped connection state, never transaction-scoped state. On Postgres use set_config(key, value, false), never SET LOCAL or set_config(…​, true); Oracle RAS attach/detach and VPD context calls are session operations and already comply. Neither callable may rely on a surrounding transaction committing or rolling back, and both may assume graphitron invokes them outside any open transaction: acquisition normalizes the connection to autocommit before connect, and release settles any transaction the operation left open before disconnect. See the hook state contract for why a transaction-scoped mount is a security defect over a pooled connection.

Identity that mounts must unmount, so a <connect> without a <disconnect> fails the build. A genuinely unmount-free design opts out explicitly with an empty <disconnect/>:

<sessionState>
  <connect><call>Pk.SetContext</call></connect>
  <disconnect/> <!-- explicit unmount-free opt-out -->
</sessionState>

Transaction survival and the settle re-fire

Graphitron cannot see whether the state your connect callable mounts actually survives a transaction commit or rollback (identity parked in commit-sensitive storage would not). By default it therefore assumes it may not, and re-fires the pair, disconnect with the old handle, connect capturing a fresh one, after each top-level mutation-field transaction settles, so the field’s payload read-back and later mutation fields always see mounted identity. Query operations run in autocommit and never settle, so this safe default costs nothing on the read path; what it costs per mutation field is one extra pair of callable round trips riding the field’s commit.

If your callables mount plain session-scoped state (RAS sessions, VPD contexts, session variables), declare it and keep acquisition-scoped mounting with no re-fire:

<sessionState>
  <connect><call>Pk_Ras.Connect</call><handle>true</handle></connect>
  <disconnect><call>Pk_Ras.Disconnect</call><handle>true</handle></disconnect>
  <stateSurvivesTransactions>true</stateSurvivesTransactions>
</sessionState>

The declaration is a statement about your database code that graphitron trusts; declare it only when the mounted state is genuinely session-scoped (see the hook state contract). It applies only to a paired function hook: declaring it with the <variables> sugar fails the build (the sugar survives settles structurally and needs no declaration), as does declaring it with the empty <disconnect/> opt-out (an unmount-free hook has no pair to re-fire).

Postgres <variables> sugar

For the common Postgres case, graphitron generates both hook halves from a list of session variables, so you write no SQL:

<sessionState>
  <variables>
    <variable>
      <name>app.user_id</name>
      <claim>sub</claim>
    </variable>
    <variable>
      <name>app.tenant</name>
      <claim>tenant</claim>
    </variable>
  </variables>
</sessionState>

connect sets each variable from the named claim in the payload JSON in a single round trip (set_config('app.user_id', …​→>'sub', false)); disconnect clears exactly those variables. Both halves come from the one variable list, so they cannot drift.

Child Type Description

name

String (<name> element)

The Postgres session variable (GUC) to set, for example app.user_id. Your RLS policies read it with current_setting('app.user_id', true).

claim

String (<claim> element)

The claim key read from the payload JSON, for example sub.

A claim absent from the payload sets the variable to the empty string, and disconnect clears each variable to the empty string rather than to NULL (Postgres cannot restore a touched session variable to unset). Your RLS policies must therefore treat both NULL and the empty string as "no identity" to stay fail-closed, for example with NULLIF(current_setting('app.user_id', true), '') IS NULL.

The sugar generates PostgreSQL. If you build the runtime with a non-Postgres dialect, Graphitron.runtime(dataSource, dialect) fails immediately with a clear message rather than at the first request; use the function-hook form for other dialects.

validate-goal sentinel

The validate goal exists so consumers can run mvn graphitron:validate from the command line without editing their POM’s <execution> block to thread <outputPackage> / <jooqPackage>. The mojo substitutes an inert sentinel (validation.unused) for both packages when they are not configured, so the classifier stage still type-checks against `RewriteContext’s non-null contract. The sentinel never reaches generated code; the validate pipeline emits nothing.

If the parameters are configured (e.g. when validate is bound to the validate lifecycle phase alongside a generate execution), the configured values flow through normally; the sentinel only activates when the parameters are absent.

Putting it together

The canonical generate-and-deploy <execution> against the example schema:

<plugin>
    <groupId>no.sikt</groupId>
    <artifactId>graphitron-maven-plugin</artifactId>
    <version>${graphitron.version}</version>
    <executions>
        <execution>
            <id>rewrite-generate</id>
            <goals><goal>generate</goal></goals>
            <configuration>
                <schemaInputs>
                    <schemaInput>
                        <pattern>src/main/resources/graphql/schema.graphqls</pattern>
                    </schemaInput>
                </schemaInputs>
                <jooqPackage>com.example.jooq</jooqPackage>
                <outputPackage>com.example.generated</outputPackage>
            </configuration>
        </execution>
    </executions>
</plugin>

For the dev loop add a second execution bound to no phase, or invoke mvn graphitron:dev directly. graphitron-sakila-example’s pre-wired POM (`graphitron-sakila-example/pom.xml) is the canonical worked example.

Codegen classpath

The reflection sites that resolve consumer-declared classes (@service / @externalField / @tableMethod target classes, the classes a type’s backing record is reflected from off its producing field, @condition resolvers, the generated jOOQ DefaultCatalog) load from the project’s compile classpath plus every reactor sibling’s target/classes, not from the plugin realm. Any artifact already declared as a normal <dependency> of the consumer module is visible to the generator at codegen time; consumers do not need to mirror service / catalog jars under <plugin><dependencies>. The rare legitimate case where <plugin><dependencies> still makes sense is pinning a service or catalog jar to a different version from the project’s compile dep ; the plugin realm parents the codegen loader, so an override under <plugin><dependencies> still wins through the parent chain.

Drift protection

A bidirectional coverage test (MojoDocCoverageTest in graphitron-maven-plugin) asserts every @Parameter-annotated field on AbstractRewriteMojo, GenerateMojo, ValidateMojo, and DevMojo has a row in this page’s parameter tables, and every parameter row corresponds to an annotated field on one of those Mojos. A new @Parameter field cannot land green without a doc row; a removed field forces the row’s removal.

See also

  • Runtime API reference covers the Graphitron facade and GraphitronContext interface this plugin’s generate goal emits into the consumer module.

  • Directive reference covers the SDL surface every <schemaInput> parses against; <namedReference> entries match ExternalCodeReference.name: on the four directives that take external Java code.

  • Tutorial page 1: Prerequisites is the first place a consumer meets the plugin and the example consumer’s pre-wired POM.