Build From Scratch
This guide walks you through creating a Viaduct application from the ground up, giving you a deeper understanding of how Viaduct projects are structured.
Getting Started
We’ll build a simple CLI application that demonstrates the core concepts of Viaduct. This will be a single-module project where both the Viaduct application and schema are located inside the src directory.
Project Setup
Create a new directory for your project and navigate into it:
mkdir viaduct-hello-world
cd viaduct-hello-world
Configuring Gradle
Create a settings.gradle.kts file with the following content:
val viaductVersion: String by settings
// When part of composite build, use local gradle-plugins
// When standalone, use Maven Central (only after version is published)
pluginManagement {
if (gradle.parent != null) {
includeBuild("../../gradle-plugins")
} else {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
}
dependencyResolutionManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
versionCatalogs {
create("libs") {
// This injects a dynamic value that your TOML can reference.
version("viaduct", viaductVersion)
}
}
}
Create a gradle.properties file to specify the Viaduct version:
viaductVersion=0.7.0-SNAPSHOT
You’ll need to create a gradle/libs.versions.toml file:
[plugins]
viaduct-application = { id = "com.airbnb.viaduct.application-gradle-plugin", version.ref = "viaduct" }
viaduct-module = { id = "com.airbnb.viaduct.module-gradle-plugin", version.ref = "viaduct" }
Create a build.gradle.kts file. The key requirement is to include both Viaduct plugins:
// tag::plugins-config[7] How plugins for viaduct are setup.
plugins {
kotlin("jvm") version "1.9.24"
alias(libs.plugins.viaduct.application)
alias(libs.plugins.viaduct.module)
application
}
viaductApplication {
modulePackagePrefix.set("com.example.viadapp")
}
viaductModule {
modulePackageSuffix.set("resolvers")
}
dependencies {
implementation("ch.qos.logback:logback-classic:1.3.7")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
implementation("com.fasterxml.jackson.core:jackson-databind:2.9.10")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2")
testImplementation("org.junit.platform:junit-platform-launcher:1.9.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2")
}
application {
mainClass.set("com.example.viadapp.ViaductApplicationKt")
}
tasks.test {
useJUnitPlatform()
}
Creating the Schema
Create the directory structure for your schema:
mkdir -p src/main/viaduct/schema
Create src/main/viaduct/schema/schema.graphqls with the following content:
extend type Query {
greeting: String @resolver
author: String @resolver
}
Creating the Application Code
Create the directory structure for your Kotlin code:
mkdir -p src/main/kotlin/com/example/viadapp
mkdir -p src/main/kotlin/com/example/viadapp/resolvers
Create src/main/kotlin/com/example/viadapp/ViaductApplication.kt:
package com.example.viadapp
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import com.fasterxml.jackson.databind.ObjectMapper
import kotlinx.coroutines.runBlocking
import org.slf4j.LoggerFactory
import viaduct.service.BasicViaductFactory
import viaduct.service.TenantRegistrationInfo
import viaduct.service.api.ExecutionInput
fun main(argv: Array<String>) {
val rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger
rootLogger.level = Level.ERROR
// Create a Viaduct engine using the BasicViaductFactory
// tag::building-viaduct[5] Building viaduct from BasicViaductFactory
val viaduct = BasicViaductFactory.create(
tenantRegistrationInfo = TenantRegistrationInfo(
tenantPackagePrefix = "com.example.viadapp"
)
)
// Create an execution input
// tag::create-execution-input[12] Creating an execution input
val executionInput = ExecutionInput.create(
operationText = (
argv.getOrNull(0)
?: """
query {
greeting
}
""".trimIndent()
),
variables = emptyMap(),
)
// Run the query
// tag::viaduct-execute-operation[3] Execute a query through Viaduct.
val result = runBlocking {
viaduct.execute(executionInput)
}
// [toSpecification] converts to JSON as described in the GraphQL
// specification.
val mapper = ObjectMapper().writerWithDefaultPrettyPrinter()
println(
mapper.writeValueAsString(result.toSpecification())
)
}
Create 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
// tag::greeting-resolver[6] How to create a 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"
}
}
Building and running the Application
Build your application:
./gradlew build
Run the application:
./gradlew -q run --args="'{ greeting }'"
You should see the GraphQL response with your greeting!
What’s Next
Continue to Touring the Application to understand the structure of a Viaduct application.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.