Skip to main content

Quickstart

This guide will get you up and running with Viaduct in just a few minutes. You’ll clone the CLI starter demo, run your first GraphQL query, and understand the basics of how Viaduct applications work.

Prerequisites

Before you begin, make sure you have:
  • Java JDK 21 or higher installed
  • JAVA_HOME environment variable set correctly or java in your PATH
  • Git installed
  • Basic familiarity with GraphQL (see GraphQL Foundation learning materials)
Viaduct is compatible with Gradle 8.10+ and Kotlin 1.9.x or 2.x. The CLI starter comes pre-configured with compatible versions.

Clone the CLI starter

1

Clone the repository

Clone the CLI starter demo application from GitHub:
git clone https://github.com/viaduct-dev/cli-starter.git
cd cli-starter
The CLI starter is a minimal Viaduct application that demonstrates the core concepts without the complexity of a web server.
2

Verify your environment

Run the tests to ensure everything is set up correctly:
./gradlew test
You should see output indicating that the build and tests completed successfully:
BUILD SUCCESSFUL in 5s
3

Run your first query

Execute the default GraphQL query:
./gradlew -q run --args="'{ greeting }'"
You should see the following JSON response:
{
  "data" : {
    "greeting" : "Hello, World!"
  }
}
Congratulations! You’ve just executed your first Viaduct query.
4

Try a different query

The CLI starter includes multiple fields. Try querying the author field:
./gradlew -q run --args="'{ author }'"
You should see:
{
  "data" : {
    "author" : "Brian Kernighan"
  }
}
You can also query multiple fields at once:
./gradlew -q run --args="'{ greeting author }'"

Understanding the application

Now that you have a working Viaduct application, let’s understand how it works.

Project structure

The CLI starter has a simple structure:
cli-starter/
├── build.gradle.kts          # Gradle build configuration
├── settings.gradle.kts       # Gradle settings
├── gradle/
│   └── viaduct.versions.toml # Dependency versions
└── src/
    └── main/
        ├── kotlin/
        │   └── com/example/viadapp/
        │       ├── ViaductApplication.kt
        │       └── resolvers/
        │           └── HelloWorldResolvers.kt
        └── viaduct/
            └── schema/
                └── schema.graphqls

The schema

The GraphQL schema is defined in src/main/viaduct/schema/schema.graphqls:
src/main/viaduct/schema/schema.graphqls
extend type Query {
  greeting: String @resolver
  author: String @resolver
}
The @resolver directive tells Viaduct that you’ll provide a custom function to compute the value of this field. All fields of Query must have @resolver applied to them.
Viaduct has built-in definitions for the root GraphQL types Query and Mutation, so you extend them rather than define them from scratch.

The resolvers

Resolvers contain the business logic for your schema. Here’s the greeting resolver from src/main/kotlin/com/example/viadapp/resolvers/HelloWorldResolvers.kt:
src/main/kotlin/com/example/viadapp/resolvers/HelloWorldResolvers.kt
package com.example.viadapp.resolvers

import com.example.viadapp.resolvers.resolverbases.QueryResolvers
import viaduct.api.Resolver

@Resolver
class GreetingResolver : QueryResolvers.Greeting() {
    override suspend fun resolve(ctx: Context): String {
        return "Hello, World!"
    }
}

@Resolver
class AuthorResolver : QueryResolvers.Author() {
    override suspend fun resolve(ctx: Context): String {
        return "Brian Kernighan"
    }
}
Notice that QueryResolvers.Greeting is a generated base class. Viaduct generates these base classes from your schema during the build process.
Each resolver:
  • Extends a generated base class (e.g., QueryResolvers.Greeting)
  • Is annotated with @Resolver
  • Overrides the resolve function to return the field value
  • Uses Kotlin coroutines (suspend fun) for asynchronous execution

The application

The main application file src/main/kotlin/com/example/viadapp/ViaductApplication.kt ties everything together:
src/main/kotlin/com/example/viadapp/ViaductApplication.kt
package com.example.viadapp

import viaduct.service.BasicViaductFactory
import viaduct.service.TenantRegistrationInfo
import viaduct.service.api.ExecutionInput
import kotlinx.coroutines.runBlocking

fun main(argv: Array<String>) {
    // Create a Viaduct engine
    val viaduct = BasicViaductFactory.create(
        tenantRegistrationInfo = TenantRegistrationInfo(
            tenantPackagePrefix = "com.example.viadapp"
        )
    )

    // Create an execution input from command-line arguments
    val executionInput = ExecutionInput.create(
        operationText = argv.getOrNull(0) ?: "query { greeting }",
        variables = emptyMap(),
    )

    // Execute the query
    val result = runBlocking {
        viaduct.execute(executionInput)
    }

    // Print the result as JSON
    println(result.toSpecification())
}
The application:
  1. Creates a Viaduct engine using BasicViaductFactory
  2. Creates an ExecutionInput from the command-line arguments
  3. Executes the query with viaduct.execute()
  4. Prints the result as JSON

The Gradle configuration

Viaduct requires two Gradle plugins in build.gradle.kts:
build.gradle.kts
plugins {
    alias(libs.plugins.kotlinJvm)
    alias(libs.plugins.viaduct.application)
    alias(libs.plugins.viaduct.module)
    application
}

viaductApplication {
    modulePackagePrefix.set("com.example.viadapp")
}

viaductModule {
    modulePackageSuffix.set("resolvers")
}

dependencies {
    implementation(libs.viaduct.api)
    implementation(libs.viaduct.runtime)
    // ... other dependencies
}
Both viaduct.application and viaduct.module plugins are required. The application plugin coordinates code generation across the entire application, while the module plugin indicates that the project contains resolver code.

Next steps

Now that you have a working Viaduct application, you can:

Add more fields

Extend the schema with new types and fields

Installation guide

Set up Viaduct in your own Gradle project

Core concepts

Learn about resolvers, GRTs, and code generation

Star Wars tutorial

Build a complete GraphQL API with advanced features

Troubleshooting

Make sure you have Java JDK 21 or higher installed and the JAVA_HOME environment variable is set:
# Check Java version
java -version

# Set JAVA_HOME (macOS/Linux)
export JAVA_HOME=$(/usr/libexec/java_home -v 21)

# Set JAVA_HOME (Windows)
set JAVA_HOME=C:\Program Files\Java\jdk-21
The CLI starter uses Kotlin 2.2.21 by default. If you see Kotlin version errors, check that your Gradle version is compatible:
  • Gradle 8.x: Kotlin 1.9.x or 2.x
  • Gradle 9.x: Kotlin 2.0+
Update gradle/viaduct.versions.toml to use a compatible Kotlin version.
Make sure your query syntax is correct and the field exists in the schema. Check src/main/viaduct/schema/schema.graphqls for available fields.You can also run ./gradlew build to see if there are any schema validation errors.
For more examples, check out the other demo applications including Ktor starter, Jetty starter, and the full-featured Star Wars app.

Build docs developers (and LLMs) love