Sample application
The following sample app implementations are all designed to connect to the IAM
database located at the TypeDB server with 0.0.0.0:1729
address. Please make sure to have TypeDB server running
with the iam
database created and both schema and data loaded. Use the
Installation guide to prepare the server
and the Quickstart guide to prepare the database.
The sample application has the following implementations:
Implementation
-
Java
-
Python
-
Node.js
The following Java code can be built into a sample application that sends four simple requests to the iam
database.
We can save it locally, build with Maven and run it with Java v.19+. Make sure to add TypeDB Java Driver to the project.
Alternatively, we can clone the full repository with Maven specs and some other quality of life add-ons.
Use the source code below or the explanation section to explore four requests performed in the sample app.
package org.example2;
import com.vaticle.typedb.client.api.TypeDBClient;
import com.vaticle.typedb.client.api.TypeDBOptions;
import com.vaticle.typedb.client.api.TypeDBSession;
import com.vaticle.typedb.client.api.TypeDBTransaction;
import com.vaticle.typedb.client.TypeDB;
import com.vaticle.typeql.lang.TypeQL;
import static com.vaticle.typeql.lang.TypeQL.*;
import com.vaticle.typeql.lang.query.TypeQLMatch;
import com.vaticle.typeql.lang.query.TypeQLInsert;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Main {
static int k = 0; // Counter
public static void main(String[] args) {
System.out.println("IAM Sample App");
System.out.println("Connecting to the server");
TypeDBClient client = TypeDB.coreClient("0.0.0.0:1729"); // client is connected to the server
System.out.println("Connecting to the `iam` database");
try (TypeDBSession session = client.session("iam", TypeDBSession.Type.DATA)) { // session is open
System.out.println("");
System.out.println("Request #1: User listing");
try (TypeDBTransaction readTransaction = session.transaction(TypeDBTransaction.Type.READ)) { // READ transaction is open
k = 0; // reset the counter
readTransaction.query().match( // Executing query
"match $u isa user, has full-name $n, has email $e;" // TypeQL query
).forEach(result -> { // Iterating through results
String name = result.get("n").asAttribute().asString().getValue();
String email = result.get("e").asAttribute().asString().getValue();
k += 1;
System.out.println("User #" + k + ": " + name + ", has E-mail: " + email);
});
System.out.println("Users found: " + k);
}
System.out.println("");
System.out.println("Request #2: Files that Kevin Morrison has access to");
try (TypeDBTransaction readTransaction = session.transaction(TypeDBTransaction.Type.READ)) { // READ transaction is open
// String getQuery = "match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission; " +
// "$o isa object, has path $fp; $pa($o, $va) isa access; get $fp;"; // Example of the same TypeQL query
TypeQLMatch.Filtered getQuery = TypeQL.match( // Java query builder to prepare TypeQL query string
cVar("u").isa("user").has("full-name", "Kevin Morrison"),
cVar("p").rel(cVar("u")).rel(cVar("pa")).isa("permission"),
cVar("o").isa("object").has("path", cVar("fp")),
cVar("pa").rel(cVar("o")).rel(cVar("va")).isa("access")
).get(cVar("fp"));
k = 0; // reset the counter
readTransaction.query().match(getQuery).forEach(result -> { // Executing query
k += 1;
System.out.println("File #" + k + ": " + result.get("fp").asAttribute().asString().getValue());
});
System.out.println("Files found: " + k);
}
System.out.println("");
System.out.println("Request #3: Files that Kevin Morrison has view access to (with inference)");
try (TypeDBTransaction readTransaction = session.transaction(TypeDBTransaction.Type.READ, TypeDBOptions.core().infer(true))) { // READ transaction is open
// String getQuery = "match $u isa user, has full-name 'Kevin Morrison';
// $p($u, $pa) isa permission;
// $o isa object, has path $fp;
// $pa($o, $va) isa access;
// $va isa action, has name 'view_file';
// get $fp; sort $fp asc; offset 0; limit 5;"
TypeQLMatch.Limited getQuery = TypeQL.match( // Java query builder to prepare TypeQL query string
cVar("u").isa("user").has("full-name", "Kevin Morrison"),
cVar("p").rel(cVar("u")).rel(cVar("pa")).isa("permission"),
cVar("o").isa("object").has("path", cVar("fp")),
cVar("pa").rel(cVar("o")).rel(cVar("va")).isa("access"),
cVar("va").isa("action").has("name", "view_file")
).get(cVar("fp")).sort(cVar("fp")).offset(0).limit(5);
k = 0; // reset the counter
readTransaction.query().match(getQuery).forEach(result -> { // Executing query
k += 1;
System.out.println("File #" + k + ": " + result.get("fp").asAttribute().asString().getValue());
});
getQuery = TypeQL.match( // Java query builder to prepare TypeQL query string
cVar("u").isa("user").has("full-name", "Kevin Morrison"),
cVar("p").rel(cVar("u")).rel(cVar("pa")).isa("permission"),
cVar("o").isa("object").has("path", cVar("fp")),
cVar("pa").rel(cVar("o")).rel(cVar("va")).isa("access"),
cVar("va").isa("action").has("name", "view_file")
).get(cVar("fp")).sort(cVar("fp")).offset(5).limit(5);
readTransaction.query().match(getQuery).forEach(result -> { // Executing query
k += 1;
System.out.println("File #" + k + ": " + result.get("fp").asAttribute().asString().getValue());
});
System.out.println("Files found: " + k);
}
System.out.println("");
System.out.println("Request #4: Add a new file and a view access to it");
try (TypeDBTransaction writeTransaction = session.transaction(TypeDBTransaction.Type.WRITE)) { // WRITE transaction is open
String filepath = "logs/" + new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS").format(new Date(System.currentTimeMillis())) + ".log";
// "insert $f isa file, has path '" + filepath + "';"
TypeQLInsert insertQuery = TypeQL.insert(cVar("f").isa("file").has("path", filepath)); // Java query builder to prepare TypeQL query string
System.out.println("Inserting file: " + filepath);
writeTransaction.query().insert(insertQuery); // Executing query
// "match $f isa file, has path '" + filepath + "';
// $vav isa action, has name 'view_file';
// insert ($vav, $f) isa access;"
insertQuery = TypeQL.match( // Java query builder to prepare TypeQL query string
cVar("f").isa("file").has("path", filepath),
cVar("vav").isa("action").has("name", "view_file")
)
.insert(cVar("pa").rel(cVar("vav")).rel(cVar("f")).isa("access"));
System.out.println("Adding view access to the file");
writeTransaction.query().insert(insertQuery); // Executing query
writeTransaction.commit(); // to persist changes, a 'write' transaction must be committed
}
}
client.close(); // closing server connection
}
}
The following Python script executes four simple requests to the iam
database.
We can save it locally and run it with Python v.3.9+. Make sure to install the TypeDB Python Driver with pip.
Use the source code below or the explanation section to explore four requests performed in the sample app.
from typedb.client import TypeDB, SessionType, TransactionType, TypeDBOptions
from datetime import datetime
print("IAM Sample App")
print("Connecting to the server")
with TypeDB.core_client("0.0.0.0:1729") as client: # Connect to TypeDB server
print("Connecting to the `iam` database")
with client.session("iam", SessionType.DATA) as session: # Access data in the `iam` database as Session
print("Request #1: User listing")
with session.transaction(TransactionType.READ) as transaction: # Open transaction to read
typeql_read_query = "match $u isa user, has full-name $n, has email $e;"
iterator = transaction.query().match(typeql_read_query) # Executing query
k = 0 # Reset counter
for item in iterator: # Iterating through results
k += 1
print("User #" + str(k) + ": " + item.get("n").get_value() + ", has E-Mail: " + item.get("e").get_value())
print("Users found:", k) # Print number of results
print("\nRequest #2: Files that Kevin Morrison has access to")
with session.transaction(TransactionType.READ) as transaction: # Open transaction to read
typeql_read_query = "match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission; " \
"$o isa object, has path $fp; $pa($o, $va) isa access; get $fp;"
iterator = transaction.query().match(typeql_read_query) # Executing query
k = 0 # Reset counter
for item in iterator: # Iterating through results
k += 1
print("File #" + str(k) + ": " + item.get("fp").get_value())
print("Files found:", k) # Print number of results
print("\nRequest #3: Files that Kevin Morrison has view access to (with inference)")
with session.transaction(TransactionType.READ, TypeDBOptions.core().set_infer(True)) as transaction: # Open transaction to read with inference
typeql_read_query = "match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission; " \
"$o isa object, has path $fp; $pa($o, $va) isa access; " \
"$va isa action, has name 'view_file'; get $fp; sort $fp asc; offset 0; limit 5;"
iterator = transaction.query().match(typeql_read_query) # Executing query
k = 0 # Reset counter
for item in iterator: # Iterating through results
k += 1
print("File #" + str(k) + ": " + item.get("fp").get_value())
typeql_read_query = "match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission; " \
"$o isa object, has path $fp; $pa($o, $va) isa access; " \
"$va isa action, has name 'view_file'; get $fp; sort $fp asc; offset 5; limit 5;"
iterator = transaction.query().match(typeql_read_query) # Executing query
for item in iterator: # Iterating through results
k += 1
print("File #" + str(k) + ": " + item.get("fp").get_value())
print("Files found:", k) # Print number of results
print("\nRequest #4: Add a new file and a view access to it")
with session.transaction(TransactionType.WRITE) as transaction: # Open transaction to write
filepath = "logs/" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + ".log"
typeql_insert_query = "insert $f isa file, has path '" + filepath + "';"
transaction.query().insert(typeql_insert_query) # Executing query
print("Inserting file:", filepath)
typeql_insert_query = "match $f isa file, has path '" + filepath + "'; " \
"$vav isa action, has name 'view_file'; " \
"insert ($vav, $f) isa access;"
print("Adding view access to the file")
transaction.query().insert(typeql_insert_query) # Executing query
transaction.commit() # to persist changes, a 'write' transaction must be committed
The following Javascript code executes four simple requests to the iam
database.
We can save it locally or clone the repository.
Make sure to install Node.js version 16+, npm
, and
TypeDB Node.js driver with npm install typedb-client
.
To run this sample application use the following command from the directory with the source code:
node sample.js
Use the source code below or the Explanation section below to explore four requests performed in the sample app.
const { TypeDB } = require("typedb-client/TypeDB");
const { SessionType } = require("typedb-client/api/connection/TypeDBSession");
const { TransactionType } = require("typedb-client/api/connection/TypeDBTransaction");
const { TypeDBOptions } = require("typedb-client/api/connection/TypeDBOptions");
async function main() {
console.log("IAM Sample App");
console.log("Connecting to the server");
const client = await TypeDB.coreClient("0.0.0.0:1729"); // client is connected to the server
console.log("Connecting to the `iam` database");
let k; // define counter
let session // define session for later use
try {
session = await client.session("iam", SessionType.DATA); // session is open
console.log("");
console.log("Request #1: User listing");
let transaction;
try {
transaction = await session.transaction(TransactionType.READ); // READ transaction is open
let match_query = "match $u isa user, has full-name $n, has email $e;"; // TypeQL query
let iterator = transaction.query.match(match_query); // Executing query
let answers = await iterator.collect();
let result = await Promise.all(
answers.map(answer =>
[answer.map.get("n").value,
answer.map.get("e").value]
)
);
k = 0; // reset the counter
for(let i = 0; i < result.length; i++) {
k++;
console.log("User #" + k + ": " + result[i][0] + ", has E-mail: " + result[i][1]);
};
console.log("Users found: " + k);
} finally {
transaction?.close();
}
console.log("");
console.log("Request #2: Files that Kevin Morrison has access to");
try {
transaction = await session.transaction(TransactionType.READ); // READ transaction is open
match_query = "match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission; $o isa object, has path $fp; $pa($o, $va) isa access; get $fp;";
iterator = transaction.query.match(match_query); // Executing query
answers = await iterator.collect();
result = await Promise.all(
answers.map(answer =>
[answer.map.get("fp").value]
)
);
k = 0; // reset the counter
for(let i = 0; i < result.length; i++) {
k++;
console.log("File #" + k + ": " + result[i]);
}
console.log("Files found: " + k);
} finally {
await transaction.close();
};
console.log("");
console.log("Request #3: Files that Kevin Morrison has view access to (with inference)");
let options = TypeDBOptions.core();
options.infer = true; // set option to enable inference
try {
transaction = await session.transaction(TransactionType.READ, options); // READ transaction is open
match_query = "match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission; $o isa object, has path $fp; $pa($o, $va) isa access; $va isa action, has name 'view_file'; get $fp; sort $fp asc; offset 0; limit 5;"
iterator = transaction.query.match(match_query); // Executing query
answers = await iterator.collect();
result = await Promise.all(
answers.map(answer =>
[answer.map.get("fp").value]
)
);
k = 0; // reset the counter
for(let i = 0; i < result.length; i++) {
k++;
console.log("File #" + k + ": " + result[i]);
};
match_query = "match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission; $o isa object, has path $fp; $pa($o, $va) isa access; $va isa action, has name 'view_file'; get $fp; sort $fp asc; offset 5; limit 5;"
iterator = transaction.query.match(match_query); // Executing query
answers = await iterator.collect();
result = await Promise.all(
answers.map(answer =>
[answer.map.get("fp").value]
)
);
for(let i = 0; i < result.length; i++) {
k++;
console.log("File #" + k + ": " + result[i]);
};
console.log("Files found: " + k);
} finally {
await transaction.close();
};
console.log("");
console.log("Request #4: Add a new file and a view access to it");
const today = new Date(Date.now());
try {
transaction = await session.transaction(TransactionType.WRITE); // WRITE transaction is open
let filepath = "logs/" + today.toISOString() + ".log";
let insert_query = "insert $f isa file, has path '" + filepath + "';";
console.log("Inserting file: " + filepath);
transaction.query.insert(insert_query); // Executing query
insert_query = "match $f isa file, has path '" + filepath + "'; $vav isa action, has name 'view_file'; insert ($vav, $f) isa access;";
console.log("Adding view access to the file");
await transaction.query.insert(insert_query); // Executing query
await transaction.commit(); // to persist changes, a 'write' transaction must be committed
} finally {
if (transaction.isOpen()) {await transaction.close()};
};
} finally {
await session?.close(); // close session
client.close(); // close server connection
};
};
main();
Explanation
List names and e-mails for all users that have them
TypeQL query used:
match $u isa user, has full-name $n, has email $e;
Simple explanation: we go through all entities of user
subtype (assigning a variable $u
for those) that have
full-name
attribute (variable $n
assigned for those) and email
attribute (variable $e
). Since we don’t have an
explicit get
statement it is assumed that we get all the variables that were assigned in the query.
Note that users that do not have For more information, see the matching patterns explanation. |
List all the files that Kevin Morrison has access to
-
TypeQL syntax
-
Java Builder syntax
TypeQL query used:
match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission;
$o isa object, has path $fp; $pa($o, $va) isa access; get $fp;
TypeQL query builder clause used:
TypeQLMatch.Filtered getQuery = TypeQL.match( // Java query builder to prepare TypeQL query string
cVar("u").isa("user").has("full-name", "Kevin Morrison"),
cVar("p").rel(cVar("u")).rel(cVar("pa")).isa("permission"),
cVar("o").isa("object").has("path", cVar("fp")),
cVar("pa").rel(cVar("o")).rel(cVar("va")).isa("access")
).get(cVar("fp"));
Simple explanation: we look for a user
(variable $u
) with attribute full-name
of value Kevin Morrison
assigned.
Then we search for a permission
relation ($p
) in between this user $u
and potential access $pa
.
Finally, we state that an object
($o
) with a path $fp
should be a part of $pa
access
relation, without
having to specify what kind of action $va
it should be. From all the variables requested, we only want it to
return the path
attributes ($fp
) of any object
that the user
has permission
to access
.
Note that users and files don’t have a singular relation that connects them directly. According to the |
List all the files Kevin has view_file access to (with inference)
-
TypeQL syntax
-
Java Builder syntax
TypeQL query used:
match $u isa user, has full-name 'Kevin Morrison'; $p($u, $pa) isa permission;
$o isa object, has path $fp; $pa($o, $va) isa access;
$va isa action, has name 'view_file'; get $fp; sort $fp asc; offset 0; limit 5;
TypeQL query builder clause #1 used:
TypeQLMatch.Limited getQuery = TypeQL.match( // Java query builder to prepare TypeQL query string
cVar("u").isa("user").has("full-name", "Kevin Morrison"),
cVar("p").rel(cVar("u")).rel(cVar("pa")).isa("permission"),
cVar("o").isa("object").has("path", cVar("fp")),
cVar("pa").rel(cVar("o")).rel(cVar("va")).isa("access"),
cVar("va").isa("action").has("name", "view_file")
).get(cVar("fp")).sort(cVar("fp")).offset(0).limit(5);
Simple explanation: This is a similar request to the previous one. The difference is we set the type of action ($va
)
that the user has access to the view_file
. We still get only path
($fp
), but can now sort in ascending order
and get it in two portions: this particular request gets the very first five entries. Later ones will get another five,
starting from number six.
Note that Kevin has been assigned only |
To make things a bit more interesting we split this into two separate queries by using an |
Insert a new file and then insert an access relation to it
First, we generate a new value for the path
attribute of the query (stored locally in the variable called filepath),
consisting of logs/
prefix, current date and time in compact format, and .log
ending.
The following queries are displayed in their final form, with the path
attribute generated value.
Query #1:
-
TypeQL syntax
-
Java Builder syntax
insert $f isa file, has path 'logs/2023-06-30T12:04:36.351.log';
TypeQLInsert insertQuery = TypeQL.insert(cVar("f").isa("file").has("path", "logs/2023-06-30T12:04:36.351.log"));
Simple explanation: we insert a file
entity (instance of data) that has an attribute path
with the value we
generated before.
Query #2:
-
TypeQL syntax
-
Java Builder syntax
match
$f isa file, has path 'logs/2023-06-30T12:04:36.351.log';
$vav isa action, has name 'view_file';
insert
($vav, $f) isa access;
insertQuery = TypeQL.match( // Java query builder to prepare TypeQL query string
cVar("f").isa("file").has("path", "logs/2023-06-30T12:04:36.351.log"),
cVar("vav").isa("action").has("name", "view_file")
)
.insert(cVar("pa").rel(cVar("vav")).rel(cVar("f")).isa("access"));
Simple explanation: we look for a file
entity that has an attribute path
with the value we generated before.
And we find an action
, that has a name
attribute with the value of view_file
. Then we insert an access
relation in between the file
and the action
.
Note that we create |