nova_forms/hooks/
use_zip_service.rs

1use leptos::*;
2
3/// Provides a service to get the city for a given swiss zip code.
4/// This uses the swiss postal service address checker API (`https://service.post.ch/zopa/app/api/addresschecker/v1/zips`).
5/// Returns a tuple that contains a function which takes an input event, and a signal that contains the city name.
6/// The function taking an input event can be added to a input field and triggers the service call when the input field changes.
7/// The response of the service call is stored in the signal.
8pub fn use_zip_service() -> (Signal<Option<String>>, WriteSignal<String>) {
9    let (zip, set_zip) = create_signal(String::new());
10    let zip_service = create_server_action::<ZipService>();
11    create_effect(move |_| {
12        zip_service.dispatch(ZipService { zip: zip.get() });
13    });
14    let zip_service_value = zip_service.value();
15    let city = create_memo(move |_| {
16        if let Some(Ok(Some(city))) = zip_service_value.get() {
17            Some(city)
18        } else {
19            None
20        }
21    });
22
23    (city.into(), set_zip)
24}
25
26#[server]
27async fn zip_service(zip: String) -> Result<Option<String>, ServerFnError> {
28    use serde::Deserialize;
29
30    #[derive(Deserialize)]
31    struct Response {
32        zips: Vec<ResponseZip>,
33    }
34
35    #[derive(Deserialize)]
36    struct ResponseZip {
37        city18: String,
38    }
39
40    let zips = reqwest::get(format!(
41        "https://service.post.ch/zopa/app/api/addresschecker/v1/zips?limit=2&zip={zip}"
42    ))
43    .await?
44    .json::<Response>()
45    .await?
46    .zips;
47
48    if zips.len() != 1 {
49        return Ok(None);
50    }
51
52    Ok(Some(zips.first().unwrap().city18.to_owned()))
53}
54