Lesson Details

When dealing with JSON in Rust, the first stop is the serde and serde_json crates.

serde provides the basis for serialization in general, while serde_json provides the specifics for JSON.

Add both crates to your project, and enable the derive feature for serde.

❯ cargo add serde serde_json -F serde/derive
    Updating crates.io index
      Adding serde v1.0.181 to dependencies.
             Features:
             + derive
             + serde_derive
             + std
             - alloc
             - rc
             - unstable
      Adding serde_json v1.0.104 to dependencies.
             Features:
             + std
             - alloc
             - arbitrary_precision
             - float_roundtrip
             - indexmap
             - preserve_order
             - raw_value
             - unbounded_depth
    Updating crates.io index

Bring serde::Serialize into scope, and use it as a derive macro on a new struct.

use serde::Serialize;

#[derive(Serialize)]
struct ApiResponse {
    data: String,
}

This new struct is our “ApiResponse”. Basically representing the data our API responds with.

Deriving Serialize enables serializing this struct to a JSON string. The JSON will be structured in the same way as our struct.

Then we can use the struct in our response.

async fn function_handler(
    event: Request,
) -> Result<Response<Body>, Error> {
    let who = event
        .query_string_parameters_ref()
        .and_then(|params| params.first("name"))
        .unwrap_or("world");

    let message = format!(
        "Hello {who}, this is an Netlify serverless request"
    );
    let api_response = ApiResponse { data: message };
    let body_text = serde_json::to_string(&api_response)?;

    let resp = Response::builder()
        .status(200)
        .header(CONTENT_TYPE, "application/json")
        .body(Body::Text(body_text))?;
    Ok(resp)
}

We swap out our html for a regular string showing a message for our JSON response.

Construct an instance of the ApiResponse with our message as the data, then use serde_json to serialize that data to a string.

The serialization could fail, so we handle the error with a ?, which returns the error from our function.

Finally, we need to change the CONTENT_TYPE header to application/json and set the new Body::Text to the body_text json string.

With cargo lambda watch and running our invoke again, we get a JSON response.

❯ cargo lambda invoke serverless-intro-netlify --data-file ./fixture.json
{
  "statusCode": 200,
  "headers":
  {
    "content-type": "application/json"
  },
  "multiValueHeaders":
  {
    "content-type":
    [
      "application/json"
    ]
  },
  "body": "{\"data\":\"Hello chris, this is an Netlify serverless request\"}",
  "isBase64Encoded": false
}

In production, we get just the JSON back.

{"data":"Hello chris, this is an Netlify serverless request"}