I am building an API with rust and using surrealdb and I am getting this "invalid type: map expected a string" in the create
method. I am using the official SDK.
This is the complete error:
Err(Api(FromValue { value: Array(Array([Object(Object({"completed": Bool(false), "content": Strand(Strand("Teste content")), "createdAt": Strand(Strand("2023-12-06T11:51:13.110266200Z")), "id": Thing(Thing { tb: "todo", id: String("47f46af4-27e8-4c22-9711-f49b9a866e82") }), "title": Strand(Strand("Teste")), "updatedAt": Strand(Strand("2023-12-06T11:51:13.110266200Z"))}))])), error: "invalid type: map, expected a string" }))
This is the method on the repository:
// todo_repository.rs
pub async fn create(&self, content: Todo) -> Result<Vec<Todo>, Error> {
let record = DB.create(&self.table).content(content).await?;
Ok(record)
}
The error is kinda big, so I changed the method to be like in the docs:
// todo_repository.rs
pub async fn create(&self, content: Todo) -> Result<Vec<Todo>, Error> {
let record = DB.create("todo").await?;
Ok(record)
}
And this is the new error:
Err(Api(FromValue { value: Array(Array([Object(Object({"id": Thing(Thing { tb: "todo", id: String("08kipc79181usjbuxlnu") })}))])), error: "invalid type: map, expected a string" }))
This is how I am configuring the connection:
// surreal_context.rs
pub static DB: Lazy<Surreal<Client>> = Lazy::new(Surreal::init);
pub async fn connect_db() -> Result<()> {
let _ = DB.connect::<Ws>("localhost:8000").await?;
let _ = DB.signin(Root {
username: "root",
password: "root",
})
.await;
let _ = DB.use_ns("todo").use_db("todo").await?;
Ok(())
}
// main.rs
#[tokio::main]
async fn main() {
let _ = connect_db().await;
...
}
minimal to reproduce:
#![allow(unused)]
use chrono::{DateTime, Utc};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use surrealdb::{Surreal, Result, engine::remote::ws::{Client, Ws}, opt::auth::Root};
#[allow(non_snake_case)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Todo {
pub id: Option<String>,
pub title: String,
pub content: String,
pub completed: Option<bool>,
pub createdAt: Option<DateTime<Utc>>,
pub updatedAt: Option<DateTime<Utc>>,
}
pub static DB: Lazy<Surreal<Client>> = Lazy::new(Surreal::init);
pub async fn connect() -> Result<()> {
let _ = DB.connect::<Ws>("localhost:8000").await?;
let _ = DB.signin(Root {
username: "root",
password: "root",
})
.await;
let _ = DB.use_ns("todo").use_db("todo").await?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
let _ = connect().await;
let record: Vec<Todo> = DB.create("todo").await?;
Ok(())
}
These are the dependencies:
[dependencies]
axum = "0.7.1"
chrono = { version = "0.4.31", features = ["serde"] }
once_cell = "1.18.0"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
surrealdb = "1.0.0"
tokio = { version = "1.34.0", features = ["full"] }
tower-http = { version = "0.5.0", features = ["cors"] }
uuid = { version = "1.6.1", features = ["v4", "serde"] }
I've managed to resolve the issue, and it turns out the solution is quite straightforward. In surrealdb, the id
field is essentially "reserved" and is automatically set for all records upon creation. Consequently, when I specified id: Option<String>
, it conflicted with surrealdb's default serialization, which expects the id
field to be of type Thing
.
To overcome this, I opted to rename the id
field in my Todo
struct to _id
, and voila, everything is now functioning as expected now.