Skip to content

Commit 138cf28

Browse files
committed
docs: Add Kotlin and Scala examples for JVM language support
Closes #11 Added example code demonstrating how to use the Authzed Java client library from Kotlin and Scala, making it easier for developers in those ecosystems to integrate SpiceDB. The examples show idiomatic usage patterns for each language while using the same underlying Java client library. Changes: - Add Kotlin example (examples/kotlin/CallingCheck.kt) with concise syntax and better null safety - Add Scala example (examples/scala/CallingCheck.scala) with functional programming patterns using Try and Option monads - Add comprehensive README (examples/README.md) documenting installation and usage for Java, Kotlin, and Scala with build tool configurations - Fix unused import in Scala example - Make dependency instructions consistent for grpc-netty-shaded across all build tools - Add Eclipse IDE files to .gitignore Signed-off-by: ivanauth <ivan@authzed.com>
1 parent 19a4260 commit 138cf28

File tree

4 files changed

+515
-0
lines changed

4 files changed

+515
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ bin
55
gradle/wrapper
66
!gradle/wrapper/gradle-wrapper.jar
77
!gradle/wrapper/gradle-wrapper.properties
8+
9+
# Eclipse/Buildship
10+
.project
11+
.settings/

examples/README.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Authzed Java Client Examples
2+
3+
This directory contains examples demonstrating how to use the Authzed Java client library from various JVM languages.
4+
5+
## Available Examples
6+
7+
### Java Examples (`v1/`)
8+
- `CallingCheck.java` - Demonstrates basic permission checking with schema writing, relationship creation, and permission checks
9+
- `CallingWatch.java` - Demonstrates watching for changes in SpiceDB with automatic reconnection
10+
11+
### Kotlin Examples (`kotlin/`)
12+
- `CallingCheck.kt` - Same functionality as the Java example, but using idiomatic Kotlin syntax
13+
14+
### Scala Examples (`scala/`)
15+
- `CallingCheck.scala` - Same functionality as the Java example, but using idiomatic Scala syntax
16+
17+
## Installation
18+
19+
The Authzed Java client library works seamlessly with all JVM languages. Choose your build tool and language:
20+
21+
**Note:** You need to include a gRPC transport implementation like `grpc-netty-shaded` to run these examples. The `authzed` library doesn't include a transport by default to give you flexibility in choosing one.
22+
23+
### Maven (Java)
24+
25+
```xml
26+
<dependencies>
27+
<dependency>
28+
<groupId>com.authzed.api</groupId>
29+
<artifactId>authzed</artifactId>
30+
<version>1.5.4</version>
31+
</dependency>
32+
<dependency>
33+
<groupId>io.grpc</groupId>
34+
<artifactId>grpc-api</artifactId>
35+
<version>1.75.0</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>io.grpc</groupId>
39+
<artifactId>grpc-stub</artifactId>
40+
<version>1.75.0</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>io.grpc</groupId>
44+
<artifactId>grpc-netty-shaded</artifactId>
45+
<version>1.75.0</version>
46+
</dependency>
47+
</dependencies>
48+
```
49+
50+
### Gradle (Java/Kotlin)
51+
52+
For `build.gradle`:
53+
```groovy
54+
dependencies {
55+
implementation "com.authzed.api:authzed:1.5.4"
56+
implementation 'io.grpc:grpc-api:1.75.0'
57+
implementation 'io.grpc:grpc-stub:1.75.0'
58+
implementation 'io.grpc:grpc-netty-shaded:1.75.0'
59+
}
60+
```
61+
62+
For `build.gradle.kts` (Kotlin DSL):
63+
```kotlin
64+
dependencies {
65+
implementation("com.authzed.api:authzed:1.5.4")
66+
implementation("io.grpc:grpc-api:1.75.0")
67+
implementation("io.grpc:grpc-stub:1.75.0")
68+
implementation("io.grpc:grpc-netty-shaded:1.75.0")
69+
}
70+
```
71+
72+
### SBT (Scala)
73+
74+
```scala
75+
libraryDependencies ++= Seq(
76+
"com.authzed.api" % "authzed" % "1.5.4",
77+
"io.grpc" % "grpc-api" % "1.75.0",
78+
"io.grpc" % "grpc-stub" % "1.75.0",
79+
"io.grpc" % "grpc-netty-shaded" % "1.75.0"
80+
)
81+
```
82+
83+
## Usage
84+
85+
All examples follow the same pattern:
86+
87+
1. **Create a channel** - Connect to SpiceDB using gRPC
88+
2. **Authenticate** - Use a bearer token for authentication
89+
3. **Create service stubs** - Initialize the schema and permissions services
90+
4. **Write a schema** - Define your permission model
91+
5. **Write relationships** - Create relationships between objects
92+
6. **Check permissions** - Verify if a subject has permission on a resource
93+
94+
### Key Differences Between Languages
95+
96+
#### Java
97+
- Uses verbose builder pattern
98+
- Requires explicit exception handling with try-catch
99+
- Traditional class structure
100+
101+
#### Kotlin
102+
- More concise syntax with named parameters and string interpolation
103+
- Better null safety with nullable types (`?`)
104+
- Can use `try-catch` as expressions
105+
- Cleaner lambda syntax
106+
107+
#### Scala
108+
- Functional programming style with `Try` and `Option` monads
109+
- Pattern matching with `recover`
110+
- For-comprehensions for sequential operations (not shown in basic example)
111+
- Immutable by default
112+
113+
## Running the Examples
114+
115+
These examples are meant to be copied into your own project. They demonstrate the API usage patterns but are not standalone runnable programs without:
116+
117+
1. A valid SpiceDB instance (local or cloud)
118+
2. A valid authentication token
119+
3. Proper build configuration for your language
120+
121+
## Getting Started
122+
123+
For a complete guide on integrating SpiceDB with your application, see the [Protecting Your First App](https://authzed.com/docs/guides/first-app) guide.
124+
125+
## Additional Resources
126+
127+
- [Authzed Java Client Documentation](https://authzed.github.io/authzed-java/index.html)
128+
- [SpiceDB API Reference](https://authzed.com/docs/spicedb/api/http-api)
129+
- [Buf Registry SpiceDB API](https://buf.build/authzed/api/docs/main)
130+
- [SpiceDB Documentation](https://authzed.com/docs)

examples/kotlin/CallingCheck.kt

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Authzed API examples - Kotlin
3+
*
4+
* This example demonstrates how to use the Authzed Java client library from Kotlin.
5+
* It shows the more idiomatic Kotlin syntax while using the same Java client library.
6+
*/
7+
8+
import com.authzed.api.v1.*
9+
import com.authzed.grpcutil.BearerToken
10+
import io.grpc.ManagedChannel
11+
import io.grpc.ManagedChannelBuilder
12+
import java.util.concurrent.TimeUnit
13+
import java.util.logging.Level
14+
import java.util.logging.Logger
15+
16+
// Installation
17+
// Add to your build.gradle.kts:
18+
// dependencies {
19+
// implementation("com.authzed.api:authzed:1.5.4")
20+
// implementation("io.grpc:grpc-api:1.75.0")
21+
// implementation("io.grpc:grpc-stub:1.75.0")
22+
// implementation("io.grpc:grpc-netty-shaded:1.75.0")
23+
// }
24+
25+
class AuthzedClient(channel: ManagedChannel, token: String) {
26+
private val logger = Logger.getLogger(AuthzedClient::class.java.name)
27+
private val bearerToken = BearerToken(token)
28+
29+
private val schemaService: SchemaServiceGrpc.SchemaServiceBlockingStub =
30+
SchemaServiceGrpc.newBlockingStub(channel)
31+
.withCallCredentials(bearerToken)
32+
33+
private val permissionsService: PermissionsServiceGrpc.PermissionsServiceBlockingStub =
34+
PermissionsServiceGrpc.newBlockingStub(channel)
35+
.withCallCredentials(bearerToken)
36+
37+
fun writeSchema(): String {
38+
logger.info("Writing schema...")
39+
40+
val schema = """
41+
definition thelargeapp/article {
42+
relation author: thelargeapp/user
43+
relation commenter: thelargeapp/user
44+
45+
permission can_comment = commenter + author
46+
}
47+
48+
definition thelargeapp/user {}
49+
""".trimIndent()
50+
51+
val request = WriteSchemaRequest.newBuilder()
52+
.setSchema(schema)
53+
.build()
54+
55+
return try {
56+
val response = schemaService.writeSchema(request)
57+
logger.info("Response: $response")
58+
response.toString()
59+
} catch (e: Exception) {
60+
logger.log(Level.WARNING, "RPC failed: ${e.message}")
61+
""
62+
}
63+
}
64+
65+
fun readSchema(): String {
66+
logger.info("Reading schema...")
67+
68+
val request = ReadSchemaRequest.newBuilder().build()
69+
70+
return try {
71+
val response = schemaService.readSchema(request)
72+
logger.info(response.toString())
73+
response.toString()
74+
} catch (e: Exception) {
75+
logger.log(Level.WARNING, "RPC failed: ${e.message}")
76+
""
77+
}
78+
}
79+
80+
fun writeRelationship(): String {
81+
logger.info("Write relationship...")
82+
83+
val request = WriteRelationshipsRequest.newBuilder()
84+
.addUpdates(
85+
RelationshipUpdate.newBuilder()
86+
.setOperation(RelationshipUpdate.Operation.OPERATION_CREATE)
87+
.setRelationship(
88+
Relationship.newBuilder()
89+
.setResource(
90+
ObjectReference.newBuilder()
91+
.setObjectType("thelargeapp/article")
92+
.setObjectId("kotlin_test")
93+
.build()
94+
)
95+
.setRelation("author")
96+
.setSubject(
97+
SubjectReference.newBuilder()
98+
.setObject(
99+
ObjectReference.newBuilder()
100+
.setObjectType("thelargeapp/user")
101+
.setObjectId("george")
102+
.build()
103+
)
104+
.build()
105+
)
106+
.build()
107+
)
108+
.build()
109+
)
110+
.build()
111+
112+
return try {
113+
val response = permissionsService.writeRelationships(request)
114+
logger.info("Response: $response")
115+
response.writtenAt.token
116+
} catch (e: Exception) {
117+
logger.log(Level.WARNING, "RPC failed: ${e.message}")
118+
""
119+
}
120+
}
121+
122+
fun check(zedToken: ZedToken): CheckPermissionResponse.Permissionship? {
123+
logger.info("Checking...")
124+
125+
val request = CheckPermissionRequest.newBuilder()
126+
.setConsistency(
127+
Consistency.newBuilder()
128+
.setAtLeastAsFresh(zedToken)
129+
.build()
130+
)
131+
.setResource(
132+
ObjectReference.newBuilder()
133+
.setObjectType("thelargeapp/article")
134+
.setObjectId("kotlin_test")
135+
.build()
136+
)
137+
.setSubject(
138+
SubjectReference.newBuilder()
139+
.setObject(
140+
ObjectReference.newBuilder()
141+
.setObjectType("thelargeapp/user")
142+
.setObjectId("george")
143+
.build()
144+
)
145+
.build()
146+
)
147+
.setPermission("can_comment")
148+
.build()
149+
150+
return try {
151+
val response = permissionsService.checkPermission(request)
152+
logger.info("Response: $response")
153+
response.permissionship
154+
} catch (e: Exception) {
155+
logger.log(Level.WARNING, "RPC failed: ${e.message}")
156+
null
157+
}
158+
}
159+
}
160+
161+
fun main() {
162+
val target = "grpc.authzed.com:443"
163+
val token = "tc_test_def_token"
164+
165+
val channel = ManagedChannelBuilder
166+
.forTarget(target)
167+
.useTransportSecurity() // for local development without TLS, use .usePlaintext()
168+
.build()
169+
170+
try {
171+
val client = AuthzedClient(channel, token)
172+
173+
client.writeSchema()
174+
client.readSchema()
175+
176+
val tokenVal = client.writeRelationship()
177+
178+
val result = client.check(
179+
ZedToken.newBuilder()
180+
.setToken(tokenVal)
181+
.build()
182+
)
183+
184+
println("Check result: $result")
185+
} finally {
186+
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS)
187+
}
188+
}

0 commit comments

Comments
 (0)