Skip to main content

Quickstart

Simplify with the Daggerverse

At this point, you have successfully built a pipeline for an application using the Dagger API. This is important knowledge to have, so that you understand the basics of using Dagger and can begin creating pipelines for your own applications.

That said, Dagger also lets you use Dagger Functions developed by others and published to the Daggerverse, Dagger's free and publicly-available index of Dagger modules.

THE DAGGERVERSE

Dagger's superpower is its community. All around the world, Daggernauts are encoding their expertise into Dagger Functions, and sharing them for anyone to reuse. The Daggerverse is a free service run by Dagger, which indexes all publicly available Dagger modules, and lets you easily search and consume them. Using the Daggerverse, you can easily discover great modules being developed by the community, learn how they work, and start using them. Since modules are just source code, it's easy to contribute to them, too!

Using the Daggerverse is optional, and does not change how you use Dagger. If you find a module you like, simply copy its URL, and use it the usual way.

To understand how this works in practice, let's simplify the pipeline using the Node module from the Daggerverse. This module contains tested, ready-to-use Dagger Functions for building, linting and testing Node.js applications.

Install a Daggerverse module

Install the Node module as a dependency by running the dagger install command shown on its Daggerverse page:

dagger install github.com/dagger/dagger/sdk/typescript/dev/node@f1aa5e117e163449208519ce6c6b1dbdb8ef5d79
VERSION PINNING

The exact Git commit for the module version is recorded in dagger.json. Dagger enforces version pinning, which guarantees that the module version you install is the one you'll be using.

Once you've installed the Node module, start exploring it to see what you can do with it. There are various ways to do this:

Simplify the pipeline

Next, update the pipeline to use this module.

Update the dagger/main.go file with the following code:

package main

import (
"context"
"fmt"
"math"
"math/rand"

"dagger/hello-dagger/internal/dagger"
)

type HelloDagger struct{}

// Publish the application container after building and testing it on-the-fly
func (m *HelloDagger) Publish(ctx context.Context, source *dagger.Directory) (string, error) {
_, err := m.Test(ctx, source)
if err != nil {
return "", err
}
address, err := m.Build(source).
Publish(ctx, fmt.Sprintf("ttl.sh/hello-dagger-%.0f", math.Floor(rand.Float64()*10000000))) //#nosec
if err != nil {
return "", err
}
return address, nil
}

// Build the application container
func (m *HelloDagger) Build(source *dagger.Directory) *dagger.Container {
build := dag.Node(dagger.NodeOpts{Ctr: m.BuildEnv(source)}).
Commands().
Run([]string{"build"}).
Directory("./dist")
return dag.Container().From("nginx:1.25-alpine").
WithDirectory("/usr/share/nginx/html", build).
WithExposedPort(80)
}

// Return the result of running unit tests
func (m *HelloDagger) Test(ctx context.Context, source *dagger.Directory) (string, error) {
return dag.Node(dagger.NodeOpts{Ctr: m.BuildEnv(source)}).
Commands().
Run([]string{"test:unit", "run"}).
Stdout(ctx)
}

// Build a ready-to-use development environment
func (m *HelloDagger) BuildEnv(source *dagger.Directory) *dagger.Container {
return dag.Node(dagger.NodeOpts{Version: "21"}).
WithNpm().
WithSource(source).
Install().
Container()
}

This code listing revises the Dagger Functions from earlier, replacing calls to the core Dagger API with calls to Dagger Functions from the Node module. This allows you to access pre-defined functionality for working with Node.js applications - for example, obtaining a base image with npm and cache volumes already configured, or executing common commands to lint, format, test, and build a Node.js codebase.

If you backtrack a little further and inspect the Node module's source code, you'll notice two important things:

  1. The Node module is written in TypeScript but is called from your Dagger module, which could be in Go, Python or TypeScript. Dagger takes care of the translation via its language-agnostic GraphQL layer.
  2. The Node module is a Dagger module very similar to the one you built in this quickstart. If you inspect its source code, you'll recognize the dag client and many of the core Dagger API methods you used when building your own Dagger Functions.

Run the pipeline

Run the pipeline:

dagger call publish --source=.

As before, you should see the application being tested, built, and published to the ttl.sh container registry.

PRIVATE CONTAINER REGISTRIES

This quickstart uses the public ttl.sh container registry, but Dagger also supports publishing to private registries, including Docker Hub, GitHub Container Registry, and many others.

Using a Daggerverse module instead of writing your own Dagger Functions is often advantageous because:

  • It provides ready-to-use functionality encapsulating the community's knowledge. This allows you to get started quickly and confidently, without needing to "roll your own code" from scratch.
  • It is written in accordance with language standards and best practices. Plus, its source code is open, allowing anyone to inspect it and suggest improvements to it.
  • It can include useful optimizations. For example, the Node module used here automatically creates and uses cache volumes for application dependencies.
UNLOCKING CROSS-LANGUAGE COLLABORATION

Dagger Functions can call other functions, across languages. So, even though the Node module in this section is written in TypeScript, you can transparently call its functions from another Dagger module written in Go, Python or any other supported language. This means that you no longer need to care which language your CI tooling is written in; you can use the one that you're most comfortable with or that best suits your requirements.

Dagger is able to do this because it uses GraphQL as its low-level language-agnostic API query language. Each Dagger SDK generates native code-bindings for all dependencies, which abstract away the underlying GraphQL queries. This gives you all the benefits of type-checking, code completion and other IDE features when developing Dagger Functions.