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
idfield is essentially "reserved" and is automatically set for all records upon creation. Consequently, when I specifiedid: Option<String>, it conflicted with surrealdb's default serialization, which expects theidfield to be of typeThing.To overcome this, I opted to rename the
idfield in myTodostruct to_id, and voila, everything is now functioning as expected now.