docs/current_docs/extending/modules/interfaces.mdx
Dagger interfaces let a function accept or return any object that exposes a required set of functions, without coupling the function to a specific concrete object type or SDK language.
Interfaces are supported across all Dagger SDKs. The examples below focus on Go, Python, and TypeScript because they use language-native interface/protocol syntax when authoring modules. When a module exposes an interface, every SDK can consume that interface in generated clients and pass compatible objects to interface-typed arguments.
Declare an interface and use it as a function argument, return value, or object field. Dagger checks interfaces structurally: any object with compatible functions satisfies the interface, including objects from dependencies and objects written with another SDK. Interfaces can also satisfy other interfaces when their function sets are compatible.
<Tabs groupId="language" queryString="sdk"> <TabItem value="go" label="Go">Define a Go interface that embeds DaggerObject and use it in your Dagger
Function signature.
Here is an example of the definition of an interface Fooer with a single
function foo:
Functions defined in interface definitions must match the client-side API
signature style. If they return a scalar value or an array, they must accept a
context.Context argument and return an error return value. If they return a
chainable object value, they must not return an error value, and they do not
need to include a context.Context argument.
Note that you must also provide argument names, since they directly translate to the GraphQL field argument names.
</TabItem> <TabItem value="python" label="Python">Define a Python protocol decorated with @dagger.interface and use it in your
Dagger Function signature.
Here is an example of the definition of an interface Fooer with a single
function foo:
Functions defined in interface definitions must match the client-side API
signature style. If they don't return a chainable object, they must be
coroutines (async).
Note that function arguments need to be properly named, since they directly translate to the GraphQL field argument names.
</TabItem> <TabItem value="typescript" label="TypeScript">Define a TypeScript interface and use it in your Dagger Function signature.
Here is an example of the definition of an interface Fooer with a single
function foo:
Functions defined in interface definitions must match the client-side API signature style:
Promise<T>.foo(bar: number): Promise<string>) or a property signature (for example,
foo: (bar: number) => Promise<string>).No Dagger-specific implements marker is required. A Dagger object implements an
interface when it exposes every required function with compatible argument and
return types. You may still use language-native implements declarations where
they are useful for editor or compiler checks.
Here is an example of a module Example that implements the Fooer interface:
Any object that implements the interface can be passed as an argument to the function that uses the interface.
Dagger automatically detects whether an object from the current module or one of its dependencies implements an interface defined in the current module or its dependencies. The generated SDK code then uses the idiomatic pattern for that language:
As<Interface>() adapter methods on compatible objects.
These are client-side type conversions only; they are not GraphQL schema
fields and do not make network calls.Here is an example of a module that uses the Example module defined above and
passes it as argument to the foo function of the MyModule object:
Interfaces are first-class GraphQL interfaces in the Dagger API. Implementing objects are listed in the schema, and Dagger can use GraphQL fragments when it needs to resolve the concrete type behind an interface value.
All object IDs now use the standard ID scalar. The schema uses
@expectedType directives to preserve typed SDK signatures, and object loading
uses the standard node(id:) field plus the built-in Node interface instead
of per-type loadFooFromID fields. SDKs handle this internally in most cases,
so module code can continue to pass typed SDK objects instead of raw IDs.
If you do need to work with raw IDs:
dagger.Ref[T](client, id) for a lazy reference or
dagger.Load[T](ctx, client, id) to verify that the ID resolves to T.node(id:) and select concrete fields
with an inline fragment.In Go, generated interface clients also include a Concrete(ctx) method that
loads the underlying object as a Node, which can then be handled with a type
switch when concrete-type-specific methods are needed.