# Full Stack Application - Social Network

## [](#_introduction)Introduction

In this tutorial, we’ll build a sample application capable of basic interaction with TypeDB:

*   Connect to a TypeDB server,
    
*   Manage databases and transactions,
    
*   Send different types of queries.
    

Follow the steps below or see the [full-stack full source code](https://github.com/typedb/typedb-examples/tree/master/fullstack) or the [equivalent webapp source code](https://github.com/typedb/typedb-examples/tree/master/webapp).

This example can communicate with TypeDB using one of the following service layers:

*   Rust backend (using Axum)
    
*   Java backend (using Spring Boot)
    
*   Python backend (using Flask)
    
*   Directly from the Typescript webapp
    

## [](#_running_the_application)Running the application

To run this sample application, you’ll need:

1.  TypeDB: either a [TypeDB Cloud](https://cloud.typedb.com/) cluster or a self-hosted deployment. For installation instructions, see the [Install](../../../home/install/index.md) page.
    
    *   You will need the `social-network` sample schema and data from the TypeDB Examples repository. Either select it when launching your TypeDB Cloud cluster, or install it following the instructions in [the README](https://github.com/typedb/typedb-examples/blob/master/use-cases/social-network/README.md#setup).
        
    
2.  Frontend:
    
    *   With backend (Rust, Python, Java)
        
    *   Frontend only (Typescript)
        
    
    *   Requirements: Node.js, pnpm or npm
        
    *   Select folder:
        
        ```bash
        cd fullstack/frontend
        ```
        
    *   Install dependencies:
        
        ```bash
        npm install
        # or
        pnpm install
        ```
        
    *   Run:
        
        ```bash
        npm run dev
        # or
        pnpm dev
        ```
        
    
    *   Requirements: Node.js, pnpm or npm
        
    *   Select folder:
        
        ```bash
        cd webapp
        ```
        
    *   Install dependencies:
        
        ```bash
        npm install
        # or
        pnpm install
        ```
        
    *   Run:
        
        ```bash
        npm run dev
        # or
        pnpm dev
        ```
        
    
3.  Backend:
    
    *   Rust
        
    *   Python
        
    *   Java
        
    
    *   Requirements: Rust toolchain
        
    *   Run:
        
        ```bash
        cd fullstack/backend/rust
        cargo run
        ```
        
    
    *   Requirements: Python 3.8+, Flask
        
    *   Install dependencies:
        
        ```bash
        cd fullstack/backend/python
        pip install -r requirements.txt
        ```
        
    *   Run:
        
        ```bash
        python app.py
        ```
        
    
    *   Requirements: Java 17+, Gradle
        
    *   Run:
        
        ```bash
        cd fullstack/backend/java
        ./gradlew bootRun
        ```
        
    

## [](#_data_model)Data model

This example is based on the `social-network` sample schema and data from the TypeDB Examples repository, and can be found at [https://github.com/typedb/typedb-examples/tree/master/use-cases/social-network](https://github.com/typedb/typedb-examples/tree/master/use-cases/social-network).

### [](#_frontend)Frontend

We mirror elements of the schema model in our frontend Typescript data model. Similar to how in TypeQL, we have `user sub profile` and `profile sub page`, in our Typescript model we have `User extends Profile` and `Profile extends Page` - omitting certain attributes and relations that are not used in this example.

*   TypeQL
    
*   TypeScript
    

Excerpts from [schema.tql](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/use-cases/social-network/schema.tql):

*   [page, profile](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/use-cases/social-network/schema.tql#L21)
    
*   [organization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/use-cases/social-network/schema.tql#L97)
    

```typeql
define
  entity page @abstract, sub content,
    owns page-id,
    owns name,
    owns bio,
    owns profile-picture,
    owns badge,
    owns is-active,
    plays posting:page,
    plays viewing:viewed,
    plays following:page;

  entity profile @abstract, sub page,
    owns username,
    owns can-publish,
    plays group-membership:member,
    plays location:located,
    plays viewing:viewer,
    plays content-engagement:author,
    plays following:follower,
    plays subscription:subscriber;

  entity organization sub profile,
    owns tag @card(0..),
    plays employment:employer;
```

Excerpts from:

*   [Page.tsx](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/frontend/src/model/Page.tsx):
    
    *   [Page](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/frontend/src/model/Page.tsx#L9)
        
    *   [Profile](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/frontend/src/model/Page.tsx#L31)
        
    
*   [Organization.tsx](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/frontend/src/model/Organization.tsx#L3)
    

```typescript
export interface Page {
    id: string;
    type: PageType;
    name: string;
    bio: string;
    profilePicture?: string;
    badge?: string;
    isActive?: boolean;
    posts?: string[];
    numberOfFollowers?: number;
    followers?: string[]
}


export interface Profile extends Page {
    type: 'person' | 'organization';
    username?: string;
    canPublish?: boolean;
    location?: LocationItem[];
}

export interface Organization extends Profile {
    type: PageType<'organization'>;
    tags?: string[];
}
```

### [](#_query_examples)Query examples

With setup complete and a data model in place, we can turn to looking at a couple of example queries used by the application - one for reading data, and one for writing:

#### [](#_list_pages)List pages

This query will list all `page` entities in the database, so that they can be displayed on the frontend’s landing page.

```typeql
match $page isa page;
fetch {
    "name": $page.name,
    "bio": $page.bio,
    "id": $page.page-id,
    "profile-picture": $page.profile-picture,
    "type": (
        match
        { $ty label person; } or { $ty label organization; } or { $ty label group; };
        $page isa $ty;
        return first $ty;
    ),
};
```

Using TypeQL’s `fetch` syntax, we can retrieve our data in a JSON format that matches our data model - this allows the data to be transferred directly to the frontend without further modification.

This also allows us to use a subquery to determine which of a few subtypes a `page` is, and return that as a property of the resulting JSON. We use the `first` keyword to convert the stream returned by the subquery into a single value.

#### [](#_create_organization)Create organization

This query will create a new `organization` in the database - in the application, we would replace the values with user input.

```typeql
insert $_ isa organization,
    has name "Acme Inc.",
    has username "acmeinc",
    has profile-picture "https://example.com/acmeinc.png",
    has badge "badge",
    has tag "acme",
    has tag "company",
    has bio "Welcome to Acme Inc.",
    has is-active true,
    has can-publish true;
```

Note how we use `$_` as an anonymous variable in the query, since we don’t need to refer to the new `organization` later. Additionally, since an organization can have multiple tags, we use `has tag "…​"` multiple times. Since some of these attributes are optional, we could simply omit them if they were unset - as below.

```typeql
insert $_ isa organization,
    has name "Acme Inc.",
    has username "acmeinc",
    has bio "Welcome to Acme Inc.",
    has is-active true,
    has can-publish true;
```

Now that we have some queries to run, let’s look at integrating those queries into our application, starting with connecting to the database.

## [](#_application)Application

### [](#_connection_configuration)Connection configuration

We pull values used for connection from a set of environment variables, with defaults provided. When running the application yourself, you may need to set them to fit your setup:

*   Rust
    
*   Python
    
*   Java
    
*   TypeScript (HTTP)
    

Excerpt from [config.rs](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/rust/src/config.rs)

```rust
use std::env::var;

pub fn typedb_address() -> String {
    return var("TYPEDB_ADDRESS").unwrap_or("localhost:1729".to_string());
}
pub fn typedb_username() -> String {
    return var("TYPEDB_USERNAME").unwrap_or("admin".to_string());
}
pub fn typedb_password() -> String {
    return var("TYPEDB_PASSWORD").unwrap_or("password".to_string());
}
pub fn typedb_tls_enabled() -> bool {
    return var("TYPEDB_TLS_ENABLED").unwrap_or("false".to_string()).parse().unwrap();
}
pub fn typedb_database() -> String {
    return var("TYPEDB_DATABASE").unwrap_or("social-network".to_string());
}
```

Excerpt from [config.py](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/config.py)

```python
import os

TYPEDB_ADDRESS = os.getenv("TYPEDB_ADDRESS", "localhost:1729")
TYPEDB_USERNAME = os.getenv("TYPEDB_USERNAME", "admin")
TYPEDB_PASSWORD = os.getenv("TYPEDB_PASSWORD", "password")
TYPEDB_TLS_ENABLED = os.getenv("TYPEDB_TLS_ENABLED", "false").lower() == "true"
TYPEDB_DATABASE = os.getenv("TYPEDB_DATABASE", "social-network")
```

Excerpt from [TypeDBConfig.java](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/TypeDBConfig.java#L12)

```java
private Map<String, String> env = System.getenv();
private String TYPEDB_ADDRESS = env.getOrDefault("TYPEDB_ADDRESS", "localhost:1729");
private String TYPEDB_USERNAME = env.getOrDefault("TYPEDB_USERNAME", "admin");
private String TYPEDB_PASSWORD = env.getOrDefault("TYPEDB_PASSWORD", "password");
private boolean TYPEDB_TLS_ENABLED = env.getOrDefault("TYPEDB_TLS_ENABLED", "false").toLowerCase().equals("true");
public String TYPEDB_DATABASE = env.getOrDefault("TYPEDB_DATABASE", "social-network");
```

For the frontend-only setup we store values for use by the connection as constants in the source code. When running the application yourself, you may need to change them to fit your setup:

Excerpt from [config.tsx](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/webapp/src/config.tsx)

```typescript
export const TYPEDB_ADDRESS = "localhost:8000";
export const TYPEDB_USERNAME = "admin";
export const TYPEDB_PASSWORD = "password";
export const TYPEDB_DATABASE = "social-network";
```

where `TYPEDB_DATABASE` — the name of the database to use; `TYPEDB_ADDRESS` — address of the TypeDB server to connect to; `TYPEDB_USERNAME`/`TYPEDB_PASSWORD` — authentication credentials. `TYPEDB_TLS_ENABLED` — whether TLS is enabled. This is unneeded for the HTTP driver, as TLS is determined based on the URL scheme (`http://` vs. `https://`).

### [](#_typedb_connection)TypeDB connection

Once the connection configuration is in place, we can connect to TypeDB by creating a new driver instance:

*   Rust
    
*   Python
    
*   Java
    
*   TypeScript (HTTP)
    

Excerpt from [main.rs](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/rust/src/main.rs#238)

```rust
let tls_config = match config::typedb_tls_enabled() {
    true => DriverTlsConfig::enabled_with_native_root_ca(),
    false => DriverTlsConfig::disabled(),
};
let driver = Arc::new(
    TypeDBDriver::new(
        config::typedb_address(),
        Credentials::new(config::typedb_username(), config::typedb_password()),
        DriverOptions::new(tls_config).unwrap(),
    )
    .await
    .unwrap(),
);
```

Excerpt from [app.py](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/app.py#L10)

```python
tls_config = DriverTlsConfig.enabled_with_native_root_ca() if TYPEDB_TLS_ENABLED else DriverTlsConfig.disabled()
typedb = TypeDB.driver(TYPEDB_ADDRESS, Credentials(TYPEDB_USERNAME, TYPEDB_PASSWORD), DriverOptions(tls_config))
```

Excerpt from [TypeDBConfig.java](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/TypeDBConfig.java)

```java
@Configuration
public class TypeDBConfig {
    private Map<String, String> env = System.getenv();
    private String TYPEDB_ADDRESS = env.getOrDefault("TYPEDB_ADDRESS", "localhost:1729");
    private String TYPEDB_USERNAME = env.getOrDefault("TYPEDB_USERNAME", "admin");
    private String TYPEDB_PASSWORD = env.getOrDefault("TYPEDB_PASSWORD", "password");
    private boolean TYPEDB_TLS_ENABLED = env.getOrDefault("TYPEDB_TLS_ENABLED", "false").toLowerCase().equals("true");
    public String TYPEDB_DATABASE = env.getOrDefault("TYPEDB_DATABASE", "social-network");

    @Bean
    public Driver typeDBDriver() {
        DriverTlsConfig tlsConfig = TYPEDB_TLS_ENABLED ? DriverTlsConfig.enabledWithNativeRootCA() : DriverTlsConfig.disabled();
        return TypeDB.driver(TYPEDB_ADDRESS, new Credentials(TYPEDB_USERNAME, TYPEDB_PASSWORD), new DriverOptions(tlsConfig));
    }
}
```

Excerpt from [AppService.tsx](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/webapp/src/AppService.tsx#L28)

```typescript
const driver = new TypeDBHttpDriver({
    addresses: [TYPEDB_ADDRESS],
    username: TYPEDB_USERNAME,
    password: TYPEDB_PASSWORD,
});
```

### [](#_querying)Querying the database

With a driver instance ready to use, we can now issue queries to the database

*   Rust
    
*   Python
    
*   Java
    
*   TypeScript (HTTP)
    

We must open a transaction to execute queries - the transaction will be automatically close when the scope ends. We use `Read` and `Write` transactions as required. For the `Write` transaction, we must use `commit()` to commit the changes to the database.

Excerpts from:

*   [main.rs](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/rust/src/main.rs)
    
    *   [get\_page\_list](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/rust/src/main.rs#L22)
        
    *   [post\_create\_organization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/rust/src/main.rs#L202)
        
    
*   [query.rs](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/rust/src/query.rs#L247)
    

```rust
async fn get_page_list(State(driver): State<Arc<TypeDBDriver>>) -> Json<Vec<Box<RawValue>>> {
    let transaction = driver.transaction(config::typedb_database(), TransactionType::Read).await.unwrap();
    let result = transaction.query(query::PAGE_LIST_QUERY).await.unwrap();
    Json(
        result
            .into_documents()
            .map_ok(|page| RawValue::from_string(page.into_json().to_string()).unwrap())
            .try_collect::<Vec<_>>()
            .await
            .unwrap(),
    )
}

#[serde_as]
#[derive(Debug, Deserialize)]
struct CreateOrganizationPayload {
    username: String,
    name: String,
    #[serde_as(as = "NoneAsEmptyString")]
    #[serde(rename = "profilePicture")]
    profile_picture: Option<String>,
    #[serde_as(as = "NoneAsEmptyString")]
    badge: Option<String>,
    #[serde(rename = "isActive")]
    is_active: bool,
    #[serde(rename = "canPublish")]
    can_publish: bool,
    tags: Vec<String>,
    bio: String,
}

async fn post_create_organization(
    State(driver): State<Arc<TypeDBDriver>>,
    Json(payload): Json<CreateOrganizationPayload>,
) -> impl IntoResponse {
    let transaction = driver.transaction(config::typedb_database(), TransactionType::Write).await.unwrap();
    transaction
        .query(query::create_organization_query(payload))
        .await
        .unwrap()
        .into_rows()
        .map_ok(drop)
        .try_collect::<()>()
        .await
        .unwrap();
    transaction.commit().await.unwrap();
    (StatusCode::OK, Json(RawValue::NULL.to_owned()))
}

pub fn create_organization_query(payload: CreateOrganizationPayload) -> String {
    let CreateOrganizationPayload { username, name, profile_picture, badge, is_active, can_publish, tags, bio } =
        payload;

    let mut query = String::from("insert $_ isa organization");
    write!(&mut query, ", has name {name:?}").unwrap();
    write!(&mut query, ", has username {username:?}").unwrap();
    if let Some(profile_picture) = profile_picture {
        write!(&mut query, ", has profile-picture {profile_picture:?}").unwrap();
    }
    write!(&mut query, ", has bio {bio:?}").unwrap();
    write!(&mut query, ", has is-active {is_active}").unwrap();
    write!(&mut query, ", has can-publish {can_publish}").unwrap();
    if let Some(badge) = badge {
        write!(&mut query, ", has badge {badge:?}").unwrap();
    }
    for tag in tags {
        write!(&mut query, ", has tag {tag:?}").unwrap();
    }
    query.push(';');

    query
}
```

We must open a transaction to execute queries - by using a `with` statement we can ensure that the transaction will be closed when the scope ends. We use `READ` and `WRITE` transactions according to the query we actually want to execute. For the `WRITE` transaction, we must use `commit()` to commit the changes to the database.

Excerpts from:

*   [app.py](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/app.py)
    
    *   [get\_page\_list](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/app.py#L17)
        
    *   [post\_create\_organization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/app.py#L66)
        
    
*   [queries.py](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/queries.py#L193)
    

```python
def get_page_list():
    with typedb.transaction(TYPEDB_DATABASE, TransactionType.READ) as tx:
        return jsonify(list(tx.query(queries.PAGE_LIST_QUERY).resolve().as_concept_documents()))

def post_create_organization():
    payload = request.json
    with typedb.transaction(TYPEDB_DATABASE, TransactionType.WRITE) as tx:
        tx.query(queries.create_organization_query(payload)).resolve()
        tx.commit()
    return jsonify(None), 200

def create_organization_query(payload):
    query = "insert $_ isa organization"
    query += f", has name \"{payload['name']}\""
    query += f", has username \"{payload['username']}\""
    if payload['profilePicture']:
        query += f", has profile-picture \"{payload['profilePicture']}\""
    query += f", has bio \"{payload['bio']}\""
    query += f", has is-active {payload['isActive']}".lower()
    query += f", has can-publish {payload['canPublish']}".lower()
    if payload['badge']:
        query += f", has badge \"{payload['badge']}\""
    for tag in payload['tags']:
        query += f", has tag \"{tag}\""
    query += ";"
    return query
```

We must open a transaction to execute queries - the transaction will be automatically close when the scope ends. We use `READ` and `WRITE` transactions according to the query we actually want to execute. For the `WRITE` transaction, we must use `commit()` to commit the changes to the database.

Excerpts from:

*   [PageController.java](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/PageController.java)
    
    *   [getPages](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/PageController.java#L105)
        
    *   [createOrganization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/PageController.java#L177)
        
    

```java
public class PageController {
    private final Driver driver;
    private final TypeDBConfig config = new TypeDBConfig();

    public PageController(Driver driver) {
        this.driver = driver;
    }

    public String getPages() {
        try (Transaction tx = driver.transaction(config.TYPEDB_DATABASE, Transaction.Type.READ)) {
            return tx.query(Query.PAGE_LIST_QUERY).resolve().asConceptDocuments().stream().map(JSON::toString).collect(Collectors.toList()).toString();
        }
    }

    public ResponseEntity<?> createOrganization(@RequestBody CreateOrganizationPayload payload) {
        try (Transaction tx = driver.transaction(config.TYPEDB_DATABASE, Transaction.Type.WRITE)) {
            tx.query(Query.createOrganizationQuery(payload)).resolve();
            tx.commit();
            return ResponseEntity.ok().body("null");
        } catch (Exception e) {
            return ResponseEntity.status(500).body(e.getMessage());
        }
    }
}
```

For the HTTP driver, we can use the `oneShotQuery` method to have a transaction of a chosen type opened and closed automatically for a single query. We can pass a boolean argument to indicate whether the transaction should be committed or not - which is required for the `write` transaction to actually commit the changes to the database.

Excerpts from:

*   [AppService.tsx](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/webapp/src/AppService.tsx)
    
    *   [fetchPages](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/webapp/src/AppService.tsx#L86)
        
    *   [createOrganization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/webapp/src/AppService.tsx#L226)
        
    

```typescript
async function fetchPages(): Promise<Page[]> {
    return readConceptDocuments(`
        match $page isa page;
        fetch {
            "name": $page.name,
            "bio": $page.bio,
            "id": $page.page-id,
            "profile-picture": $page.profile-picture,
            "type": (
                match {
                    $page isa person;
                    let $ty = "person";
                } or {
                    $page isa organization;
                    let $ty = "organization";
                } or {
                    $page isa group;
                    let $ty = "group";
                };
                return first $ty;
            ),
        };
    `);
}

async function readConceptDocuments<T>(query: string): Promise<T[]> {
    const res = await driver.oneShotQuery(query, false, TYPEDB_DATABASE, "read");
    if (isApiErrorResponse(res)) throw res.err;
    if (res.ok.answerType !== 'conceptDocuments') throw new Error('Expected conceptDocuments repsonse');
    return res.ok.answers as T[];
}

async function createOrganization(payload: Partial<Organization>) {
    const query = "insert $_ isa organization"
        + `, has name "${payload.name}"`
        + `, has username "${payload.username}"`
        + (payload.profilePicture ? `, has profile-picture "${payload.profilePicture}"` : '')
        + (payload.badge ? `, has badge "${payload.badge}"` : '')
        + (payload.tags?.map(tag => `, has tag "${tag}"`).join('') ?? '')
        + `, has bio "${payload.bio}"`
        + `, has is-active ${payload.isActive}`
        + `, has can-publish ${payload.canPublish};`

    const res = await driver.oneShotQuery(query, true, TYPEDB_DATABASE, "write");
    if (isApiErrorResponse(res)) throw res.err;
}
```

### [](#_frontend_service_layer)Frontend Service Layer

Whether the application uses a backend or not, we expose a service layer with a common interface for either querying the backend or TypeDB directly - which will return the types established in our data model.

Excerpt from [ServiceContext.tsx](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/frontend/src/service/ServiceContext.tsx#L8)

```typescript
export type ServiceContextType = {
    fetchUser: (id: string) => Promise<User | null>;
    fetchGroup: (id: string) => Promise<Group | null>;
    fetchOrganization: (id: string) => Promise<Organization | null>;

    fetchPages: () => Promise<Page[]>;
    fetchLocationPages: (locationName: string) => Promise<any>;
    fetchPosts: (pageId: string) => Promise<PostType[]>;
    fetchComments: (postId: string) => Promise<Comment[]>;

    fetchMedia: (mediaId: string) => Promise<Blob | null>;

    uploadMedia: (file: File) => Promise<string>;
    createUser: (payload: Partial<User>) => Promise<void>;
    createOrganization: (payload: Partial<Organization>) => Promise<void>;
    createGroup: (payload: Partial<Group>) => Promise<void>;
};
```

As examples, let’s set up `fetchPages` and `createOrganization` function both with and without a backend.

*   With backend (Rust, Python, Java)
    
*   Frontend only (Typescript)
    

With a backend, our service layer simply directly queries the appropriate backend route, which we will set up in the next steps.

Excerpts from:

*   [AppService.tsx](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/frontend/src/AppService.tsx)
    
    *   [fetchPages](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/frontend/src/AppService.tsx#L23)
        
    *   [createOrganization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/frontend/src/AppService.tsx#L53)
        
    

```typescript
async function fetchPages(): Promise<Page[]> {
    return fetch('http://localhost:8080/api/pages')
        .then(jsonOrError('Failed to fetch pages'));
}

async function createOrganization(payload: Partial<Organization>) {
    return fetch('http://localhost:8080/api/create-organization', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
    }).then(jsonOrError('Failed to create organization'));
}

function jsonOrError(error: string) {
    return (res: Response) => {
        if (!res.ok) throw new Error(error);
        return res.json();
    }
}
```

When only using the frontend, our service layer uses the Typescript HTTP driver to query TypeDB directly - which we’ve already seen.

Excerpts from:

*   [AppService.tsx](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/webapp/src/AppService.tsx)
    
    *   [fetchPages](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/webapp/src/AppService.tsx#L86)
        
    *   [createOrganization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/webapp/src/AppService.tsx#L226)
        
    

```typescript
async function fetchPages(): Promise<Page[]> {
    return readConceptDocuments(`
        match $page isa page;
        fetch {
            "name": $page.name,
            "bio": $page.bio,
            "id": $page.page-id,
            "profile-picture": $page.profile-picture,
            "type": (
                match {
                    $page isa person;
                    let $ty = "person";
                } or {
                    $page isa organization;
                    let $ty = "organization";
                } or {
                    $page isa group;
                    let $ty = "group";
                };
                return first $ty;
            ),
        };
    `);
}

async function readConceptDocuments<T>(query: string): Promise<T[]> {
    const res = await driver.oneShotQuery(query, false, TYPEDB_DATABASE, "read");
    if (isApiErrorResponse(res)) throw res.err;
    if (res.ok.answerType !== 'conceptDocuments') throw new Error('Expected conceptDocuments repsonse');
    return res.ok.answers as T[];
}

async function createOrganization(payload: Partial<Organization>) {
    const query = "insert $_ isa organization"
        + `, has name "${payload.name}"`
        + `, has username "${payload.username}"`
        + (payload.profilePicture ? `, has profile-picture "${payload.profilePicture}"` : '')
        + (payload.badge ? `, has badge "${payload.badge}"` : '')
        + (payload.tags?.map(tag => `, has tag "${tag}"`).join('') ?? '')
        + `, has bio "${payload.bio}"`
        + `, has is-active ${payload.isActive}`
        + `, has can-publish ${payload.canPublish};`

    const res = await driver.oneShotQuery(query, true, TYPEDB_DATABASE, "write");
    if (isApiErrorResponse(res)) throw res.err;
}
```

### [](#_routing_setup)Routing and server setup

For the backends, we need to set up routing for the API endpoints, and start the server.

*   Rust
    
*   Python
    
*   Java
    

Excerpt from [main.rs](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/rust/src/main.rs#L247)

```rust
let app = Router::new()
    .route("/api/pages", get(get_page_list))
    .route("/api/create-organization", post(post_create_organization))
    .with_state(driver)
    .layer(CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any));
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
println!("Backend running at http://{addr}");
axum::serve(listener, app).await.unwrap();
```

Excerpts from [app.py](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/app.py):

*   [get\_page\_list](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/app.py#L16)
    
*   [post\_create\_organization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/python/app.py#L65)
    

```python
app = Flask(__name__)
CORS(app)

@app.route('/api/pages')
def get_page_list():
    with typedb.transaction(TYPEDB_DATABASE, TransactionType.READ) as tx:
        return jsonify(list(tx.query(queries.PAGE_LIST_QUERY).resolve().as_concept_documents()))

@app.route('/api/create-organization', methods=['POST'])
def post_create_organization():
    payload = request.json
    with typedb.transaction(TYPEDB_DATABASE, TransactionType.WRITE) as tx:
        tx.query(queries.create_organization_query(payload)).resolve()
        tx.commit()
    return jsonify(None), 200

if __name__ == '__main__':
    app.run(debug=True, port=8080)
```

Excerpts from:

*   [BackendjavaApplication.java](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/BackendjavaApplication.java)
    
*   [PageController.java](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/PageController.java)
    
    *   [getPages](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/PageController.java#L104)
        
    *   [createOrganization](https://github.com/typedb/typedb-examples/blob/3f992da5eabc273f2b2c304fc654bc1ed93c76ae/fullstack/backend/java/src/main/java/com/example/backendjava/PageController.java#L176)
        
    

```java
@SpringBootApplication
public class BackendjavaApplication {

	public static void main(String[] args) {
		SpringApplication.run(BackendjavaApplication.class, args);
	}

	@Bean
	public WebMvcConfigurer corsConfigurer() {
		return new WebMvcConfigurer() {
			@Override
			public void addCorsMappings(CorsRegistry registry) {
				registry.addMapping("/**")
						.allowedOrigins("*")
						.allowedMethods("*")
						.allowedHeaders("*");
			}
		};
	}

}

@RestController
public class PageController {
    private final Driver driver;
    private final TypeDBConfig config = new TypeDBConfig();

    @Autowired
    public PageController(Driver driver) {
        this.driver = driver;
    }

    @GetMapping(value = "/api/pages", produces = "application/json")
    public String getPages() {
        try (Transaction tx = driver.transaction(config.TYPEDB_DATABASE, Transaction.Type.READ)) {
            return tx.query(Query.PAGE_LIST_QUERY).resolve().asConceptDocuments().stream().map(JSON::toString).collect(Collectors.toList()).toString();
        }
    }

    @PostMapping(value = "/api/create-organization", produces = "application/json")
    public ResponseEntity<?> createOrganization(@RequestBody CreateOrganizationPayload payload) {
        try (Transaction tx = driver.transaction(config.TYPEDB_DATABASE, Transaction.Type.WRITE)) {
            tx.query(Query.createOrganizationQuery(payload)).resolve();
            tx.commit();
            return ResponseEntity.ok().body("null");
        } catch (Exception e) {
            return ResponseEntity.status(500).body(e.getMessage());
        }
    }
}
```

## [](#_learn_more)Learn more

[Source code with backends](https://github.com/typedb/typedb-examples/tree/master/fullstack)

The full source code of the sample application when used with one of the backends.

[Frontend-only source code](https://github.com/typedb/typedb-examples/tree/master/webapp)

The full source code of the sample application when used querying TypeDB directly from the frontend.

[Graph Visualisation for TypeDB Data](../graph-viz/index.md) [AI Agents persistence with LangGraph and TypeDB](../langgraph/index.md)

[Edit on GitHub](https://github.com/typedb/typedb-docs/edit/3.x-development/guides/modules/ROOT/pages/integrations/social-network.adoc) Edit this page on GitHub.