# Fetching linked records

All the fields of a SurrealDB linked record can be fetched and deserialised into a Rust type

**3.x**

Start a running database using the following command:

```bash
surreal start --user root --pass secret 
```

To follow along interactively, connect [using SurrealDB Studio](/docs/explore/studio.md) or the following command to open up the CLI:

```bash
surrealdb % surreal sql --user root --pass secret --pretty
```

Then use the `cargo add` command to add the crates `surrealdb` and `tokio`.

## Fetching all the fields of a record link

The following example shows a classroom joined to a few students by [record links](/docs/reference/query-language/language-primitives/record-links.md).

```surql
CREATE teacher:one;
CREATE student:one, student:two, student:three;

CREATE classroom SET
    location = (-16.7, 64.4),
    school_name = "Jöklaskóli",
    teacher = teacher:one,
    students = [student:one, student:two, student:three];
```

A query using `SELECT * FROM classroom:one` will show the teacher and all of the students, but only their record IDs.

```surql title="Query and response"
SELECT * FROM classroom:one;

[
	{
		id: classroom:one,
		location: (-16.7, 64.4),
		school_name: 'Jöklaskóli',
		students: [
			student:one,
			student:two,
			student:three
		],
		teacher: teacher:one
	}
]
```

[The `.*` operator](/docs/reference/query-language/language-primitives/idioms.md#all-elements) for the `teacher` and `student` fields can be used in this case.

```surql
SELECT *, teacher.*, students.* FROM classroom;
```

Here is the result:

```surql title="Output"
[
	{
		id: classroom:one,
		location: (-16.7, 64.4),
		school_name: 'Jöklaskóli',
		students: [
			{
				id: student:one,
				name: 'one'
			},
			{
				id: student:two,
				name: 'two'
			},
			{
				id: student:three,
				name: 'three'
			}
		],
		teacher: {
			id: teacher:one,
			name: 'one'
		}
	}
]
```

## The Rust code

The code below that shows an example of `FETCH` is another example related to classes and students. Note that in one part it passes a [`Resource`](https://docs.rs/surrealdb/latest/surrealdb/opt/enum.Resource.html) into the [`create`](https://docs.rs/surrealdb/latest/surrealdb/struct.Surreal.html#method.create) method in order to return a [`Value`](https://docs.rs/surrealdb/latest/surrealdb/types/enum.Value.html) and thus not have to specify a return type to deserialise into. For more information on this technique, see [the page on flexible typing](/docs/reference/rust/concepts/flexible-typing.md).

```rust
use surrealdb::{
    engine::remote::ws::Ws,
    opt::{auth::Root, Resource},
    Surreal,
};
use surrealdb::types::{Datetime, RecordId, SurrealValue};

// Dance classes table name
const DANCE: &str = "dance";
// Students table name
const STUDENT: &str = "student";

// Dance class table schema
#[derive(Debug, SurrealValue)]
struct DanceClass {
    id: RecordId,
    name: String,
    created_at: Datetime,
}

// Student table schema
#[derive(Debug, SurrealValue)]
struct Student {
    id: RecordId,
    name: String,
    classes: Vec<RecordId>,
    created_at: Datetime,
}

// Student model with full class details
#[derive(Debug, SurrealValue)]
#[allow(dead_code)]
struct StudentClasses {
    id: RecordId,
    name: String,
    classes: Vec<DanceClass>,
    created_at: Datetime,
}

#[tokio::main]
async fn main() -> surrealdb::Result<()> {
    // Connect to the database server
    let db = Surreal::new::<Ws>("localhost:8000").await?;

    // Sign in into the server
    db.signin(Root {
        username: "root".to_string(),
        password: "secret".to_string(),
    })
    .await?;

    // Select the namespace and database to use
    db.use_ns("main").use_db("main").await?;

    // Create a dance class and store the result
    let classes: Option<DanceClass> = db
        .create(DANCE)
        .content(DanceClass {
            id: RecordId::new(DANCE, "dc101"),
            name: "Introduction to Dancing".to_owned(),
            created_at: Datetime::default(),
        })
        .await?;

    // Create a student and assign her to the previous dance class
    // We don't care about the result here so we don't need to
    // type-hint and store it. We use `Resource::from` to return
    // a `Value` instead and ignore it.
    db.create(Resource::from(STUDENT))
        .content(Student {
            id: RecordId::new(STUDENT, "jane"),
            name: "Jane Doe".to_owned(),
            classes: classes.into_iter().map(|class| class.id).collect(),
            created_at: Datetime::default(),
        })
        .await?;

    // Run a query to retrieve students and full class info
    let mut results = db.query(format!("SELECT * FROM {STUDENT} FETCH classes")).await?;

    // Extract the first query statement result and deserialise it as a vector of students
    let students: Vec<StudentClasses> = results.take(0)?;

    // Use the result as you see fit. In this case we are simply pretty printing it.
    println!("Students = {:?}", students);

    Ok(())
}
```

Here is the final output:

```text
Students = [StudentClasses { id: RecordId { table: "student", key: String("jane") }, name: "Jane Doe", classes: [DanceClass { id: RecordId { table: "dance", key: String("dc101") }, name: "Introduction to Dancing", created_at: Datetime(2025-11-06T02:15:05.116807Z) }], created_at: Datetime(2025-11-06T02:15:05.117644Z) }]
```

**2.x**

Start a running database using the following command:

```bash
surreal start --user root --pass secret 
```

To follow along interactively, connect [using SurrealDB Studio](/docs/explore/studio.md) or the following command to open up the CLI:

```bash
surrealdb % surreal sql --user root --pass secret --ns namespace --db database --pretty
```

Then use the `cargo add` command to add three crates: `surrealdb` and `tokio`, and with `serde` with the "serde_derive" feature (`cargo add serde --features serde_derive`). The dependencies inside `Cargo.toml` should look something like this:

cargo add serde --features serde_derive

```toml
[dependencies]
serde = { version = "1.0.228", features = ["serde_derive"] }
surrealdb = "2.4.1"
tokio = "1.49.0"
```

## When to use FETCH

The following example shows a classroom joined to a few students by [record links](/docs/reference/query-language/language-primitives/record-links.md).

```surql
CREATE teacher:one;
CREATE student:one, student:two, student:three;

CREATE classroom SET
    location = (-16.7, 64.4),
    school_name = "Jöklaskóli",
    teacher = teacher:one,
    students = [student:one, student:two, student:three];
```

A query using `SELECT * FROM classroom:one` will show the teacher and all of the students, but only their record IDs.

```surql title="Query and response"
SELECT * FROM classroom:one;

[
	{
		id: classroom:one,
		location: (-16.7, 64.4),
		school_name: 'Jöklaskóli',
		students: [
			student:one,
			student:two,
			student:three
		],
		teacher: teacher:one
	}
]
```

[The `.*` operator](/docs/reference/query-language/language-primitives/idioms.md#all-elements) for the `teacher` and `student` fields can be used in this case, but note that the `.*` must be used twice in the case of the students: once to access each member of the array, and once more to access all of its fields.

```surql
SELECT *, teacher.*, students.*.* FROM classroom;
```

Using `FETCH` may be a nicer option in this case. The syntax is a bit more readable, and there is no need to think about which fields are single records and which ones are arrays.

```surql
SELECT * FROM classroom FETCH teacher, students;
```

Here is the result:

```surql title="Output"
[
	{
		id: classroom:one,
		location: (-16.7, 64.4),
		school_name: 'Jöklaskóli',
		students: [
			{
				id: student:one,
				name: 'one'
			},
			{
				id: student:two,
				name: 'two'
			},
			{
				id: student:three,
				name: 'three'
			}
		],
		teacher: {
			id: teacher:one,
			name: 'one'
		}
	}
]
```

## The Rust code

The code below that shows an example of `FETCH` is another example related to classes and students. Note that in one part it passes a [`Resource`](https://docs.rs/surrealdb/latest/surrealdb/opt/enum.Resource.html) into the [`create`](https://docs.rs/surrealdb/latest/surrealdb/struct.Surreal.html#method.create) method in order to return a [`Value`](https://docs.rs/surrealdb/latest/surrealdb/types/enum.Value.html) and thus not have to specify a return type to deserialise into. For more information on this technique, see [the page on flexible typing](/docs/reference/rust/concepts/flexible-typing.md).

```rust
use serde::{Deserialize, Serialize};
use surrealdb::{
    engine::remote::ws::Ws,
    opt::{auth::Root, Resource},
    sql::Datetime,
    RecordId, Surreal,
};

// Dance classes table name
const DANCE: &str = "dance";
// Students table name
const STUDENT: &str = "student";

// Dance class table schema
#[derive(Debug, Serialize, Deserialize)]
struct DanceClass {
    id: RecordId,
    name: String,
    created_at: Datetime,
}

// Student table schema
#[derive(Debug, Serialize)]
struct Student {
    id: RecordId,
    name: String,
    classes: Vec<RecordId>,
    created_at: Datetime,
}

// Student model with full class details
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct StudentClasses {
    id: RecordId,
    name: String,
    classes: Vec<DanceClass>,
    created_at: Datetime,
}

#[tokio::main]
async fn main() -> surrealdb::Result<()> {
    // Connect to the database server
    let db = Surreal::new::<Ws>("localhost:8000").await?;

    // Sign in into the server
    db.signin(Root {
        username: "root",
        password: "secret",
    })
    .await?;

    // Select the namespace and database to use
    db.use_ns("namespace").use_db("database").await?;

    // Create a dance class and store the result
    let classes: Option<DanceClass> = db
        .create(DANCE)
        .content(DanceClass {
            id: RecordId::from((DANCE, "dc101")),
            name: "Introduction to Dancing".to_owned(),
            created_at: Datetime::default(),
        })
        .await?;

    // Create a student and assign her to the previous dance class
    // We don't care about the result here so we don't need to
    // type-hint and store it. We use `Resource::from` to return
    // a `sql::Value` instead and ignore it.
    db.create(Resource::from(STUDENT))
        .content(Student {
            id: RecordId::from((STUDENT, "jane")),
            name: "Jane Doe".to_owned(),
            classes: classes.into_iter().map(|class| class.id).collect(),
            created_at: Datetime::default(),
        })
        .await?;

    // Run a query to retrieve students and full class info
    let mut results = db.query(format!("SELECT * FROM {STUDENT} FETCH classes")).await?;

    // Extract the first query statement result and deserialise it as a vector of students
    let students: Vec<StudentClasses> = results.take(0)?;

    // Use the result as you see fit. In this case we are simply pretty printing it.
    println!("Students = {:?}", students);

    Ok(())
}
```

Here is the final output:

```text
Students = [StudentClasses { id: RecordId { table: "student", key: String("jane") }, name: "Jane Doe", classes: [DanceClass { id: RecordId { table: "dance", key: String("dc101") }, name: "Introduction to Dancing", created_at: Datetime(2025-11-06T02:15:05.116807Z) }], created_at: Datetime(2025-11-06T02:15:05.117644Z) }]
```
