#front-end #surreal #connect #component #yew #hook

syewreal

Directly connect your Yew frontend to your Surreal DB

1 unstable release

0.2.0 Mar 18, 2024

#709 in Web programming

Download history 119/week @ 2024-03-18 13/week @ 2024-04-01

132 downloads per month

MIT/Apache

43KB
892 lines

Usage

Create a Connection

To connect to a database use the use_surreal_login hook. The hook returns a token that has to be used in a <SurrealContext/> context provider.

#[function_component]
fn MyComponent(props: &P) -> Html{
    let login = Database {
        username: "user",
        password: "user",
        database: "test",
        namespace: "test"
    };
    
    let token = use_surreal_login("localhost:8000".to_owned(), login);

    
    html! {
        <SurrealContext token={token}>
            // Your App goes here
        </SurrealContext>
    }
}

The <SurrealContext/> will suspend your app until the login was successful.

Query Components

A <Query/> component will retrieve all database entries matching the given query and display them one after the other

html! {
    <SurrealContext token={token}>
        <Query<Inner> selector="select * from myTable"/>
    </SurrealContext>
}

<Query<T>/> has four properties

  • selector: Something that can be turned into a SelectStatement, this includes Strings and surreal records (Things in the surreal source code)
  • parameters (Optional): a Vec of (String, String) touples that will be bound to the query
  • filter (Optional): a yew callback Fn(T::Properties) -> bool used for local filtering.
  • fallback (Optional): an Html element that will be rendered while the data is being fetched (uses yew Suspension under the hood).

<Query/> stores the result of the query in an internally managed state. If you want to manipulat the state or use the same query result in multiple places you will have to use <QueryWithState/> in combination with the use_query_state hook

let list_state = use_query_state::<ToDoItemProps>("SELECT * FROM item");
html!{
    <QueryWithState<ToDoItem> state={list_state}/>
}

Because the state is externally managed the <QueryWithState/> component has no selector and parameters field.

Properties

In order for the component <Inner/> to be rendered by <Query/> Inner::Properties needs to derive SurrealProps (in addition to Properties, PartialEq and Clone)

#[derive(SurrealProps, Properties, PartialEq, Clone)]
struct InnerProps {
    #[id] id: Option<ID>,
    name: AttrValue,
}


#[function_component]
fn Inner(props: &InnerProps) -> Html{
    // You know what to do here
}

The component's id property can be an ID or an Option<ID>, if you want to create new database entries from the components you should choose Option<ID> so you can set the id to None on new entries.

Local Properties

To add properties that can be set locally instead of retrieved from the server, mark them as #[local]

#[derive(SurrealProps, Properties, PartialEq, Clone)]
struct InnerProps {
    id: AttrValue,
    name: AttrValue,
    #[local]
    color: AttrValue
}

They will need to be set on the Wrapper Component and apply to all Inner components

<SurrealList<Inner> selector="select * from myTable" color="red"/>

ForeignKeys

There are two ways to deal with foreign keys:

A) Use a property of type ForeignKey (this is just an alias of ID but more readable)

B) Use a property of type StaticChild<T> where T is a deserializable struct. This allows for the data to be retrieved in one go with the FETCH keyword but will only write the id of the fetched data to surreal when updated/created

"Raw" Database access

Instead of rendering Components directly with a <Query/> you can use the use_surreal() hook to:

  • select: fetch arbitrary data from surreal using a Selector
  • update: update a components underlying data (local and remote). This always uses the MERGE mode since there is no guarantee that the local struct contains all fields of the remote table.
  • create: create a new database entry and use convinience methods to store the new entry locally
  • query: Execute an arbitrary SQL query and store the results
use_surreal().create(
    "item".to_owned(),
    ToDoItemPropsRemote {
        done: false,
        // Note how having the type of id be Option<ID> 
        // enables you to delegate id creation to the Database
        id: None,
        text: None,
        title: "Test".into(),
        img: None
    },
).execute();

Self Refs

The use_surreal().update() method takes a SurrealSelfRef as its argument, this can be obtained from inside a component by using the hook use_self_ref().

When the update is executed the data returned from the database will be used to replace the properties of the component iff the new data still matches the original selector, otherwise the data is dropped from local storage.

Note: It is highly recomended to use the use_update_callback hook when creating self-updating components.

Dependencies

~51–68MB
~1M SLoC