Handle requests with endpoints

To make the Server return anything other than an HTTP 404 reply we need to tell it how to react to requests. We do this by adding one or more Endpoints;

#[async_std::main]
async fn main() -> tide::Result<()> {
    let mut server = tide::new();
    server.at("*").get(|_| async { Ok("Hello, world!") });
    server.listen("127.0.0.1:8080").await?;
    Ok(())
}

We use the at method to specify the route to the endpoint. We will talk about routes later. For now we’ll just use the "*" wildcard route that matches anything we throw at it. For this example we will add an async closure as the Endpoint. Tide expects something that implements the Endpoint trait here. But this closure will work because Tide implements the Endpoint trait for certain async functions with a signature that looks like this;

async fn endpoint(request: tide::Request) -> tide::Result<impl Into<Response>>

In this case Into<Response> is implemented for &str so our closure is a valid Endpoint. Because Into<Response> is implemented for several other types you can quickly set up endpoints. For example the next endpoint uses the json! macro provided by use tide::prelude::* to return a serde_json::Value.

use tide::prelude::*;
#[async_std::main]
async fn main() -> tide::Result<()> {
    let mut server = tide::new();
    server.at("*").get(|_| async {
        Ok(json!({
            "meta": { "count": 2 },
            "animals": [
                { "type": "cat", "name": "chashu" },
                { "type": "cat", "name": "nori" }
            ]
        }))
    });
    server.listen("127.0.0.1:8080").await?;
    Ok(())
}

Returning quick string or json results is nice for getting a working endpoint quickly. But for more control a full Response struct can be returned.

server.at("*").get(|_| async {
    Ok(Response::new(StatusCode::Ok).set_body("Hello world".into()))
});

The Response type is described in more detail in the next chapter.

More than one endpoint can be added by chaining methods. For example if we want to reply to a delete request as well as a get request endpoints can be added for both;

server.at("*")
    .get(|_| async { Ok("Hello, world!") })
    .delete(|_| async { Ok("Goodbye, cruel world!") });

Eventually, especially when our endpoint methods grow a bit, the route definitions will get a crowded. We could move our endpoint implementations to their own functions;

#[async_std::main]
async fn main() -> tide::Result<()> {
    let mut server = tide::new();
    server.at("*").get(endpoint);
    server.listen("127.0.0.1:8080").await?;
    Ok(())
}

async fn endpoint(_req: tide::Request<()>) -> Result<Response> {
    Ok(Response::new(StatusCode::Ok).set_body("Hello world".into()))
}