Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
With the ability to pass in arguments to our SQL query, we need a way to pass through arguments from the `event` our function receives as an argument to the SQL query. We're going to do that through the url path. `event.path` gives us the url path for the request to our function. By default this will be something like `/.netlify/function/bulbasaur`. When we implement redirects to clean up our API routes later, that will become `/api/pokemon/bulbasaur`. So to get the pokemon slug we should pass to the SQL query, we'll grab the last path segment in the URL path. First we need to give a name to the event. We haven't changed the type, it is still an `ApiGatewayProxyReques`. ```rust async fn handler( event: ApiGatewayProxyRequest, _: Context, ) -> Result<ApiGatewayProxyResponse, Error> { ``` We can then take the `event.path`, which according to the `ApiGatewayProxyRequest` type, might not exist. `path.split("/")` will give us an iterator over the path segments. Notably the beginning and end of the iterator can be `""` if it starts or ends with a `/`. `last` will consume the entire iterator until it produces its last value, which in this case is the last path segment. ```rust let path = event .path .expect("expect there to always be an event path"); let requested_pokemon = path.split("/").last(); ``` We can `match` on `requested_pokemon` to handle any errors. `Some("")` allows us to match on potentially empty values, for example when someone sends a request with a trailing slash, or if the final path segment is an empty string. `None` is actually a hard error for us. It means that `path.split("/")` is an empty iterator, because otherwise `last()` will return `Some`. Since `path.split("/")` even on an empty string will result in `Some("")`, we can fail hard here because we expect `None` to never happen. Finally we have the success case, where a `pokemon_name` was successfully retrieved from the path. This code is the same code we had before for our `handler`, with the addition of using `pokemon_name` instead of a hardcoded string. ```rust match requested_pokemon { Some("") => todo!(), None => todo!(), Some(pokemon_name) => { let pool = MySqlPoolOptions::new() .max_connections(5) .connect(&database_url) .await?; let result = sqlx::query_as!( PokemonHp, r#"SELECT name, hp FROM pokemon WHERE slug = ?"#, pokemon_name ) .fetch_one(&pool) .await?; let json_pokemon = serde_json::to_string(&result)?; let response = ApiGatewayProxyResponse { status_code: 200, headers: HeaderMap::new(), multi_value_headers: HeaderMap::new(), body: Some(Body::Text(json_pokemon)), is_base64_encoded: Some(false), }; Ok(response) } } ``` We need to update our test to account for the new logic. Our fake `event` will have a `path` field that will have a Pokemon slug at the end of the segments. This will now fetch `bulbasaur` from the database. ```rust async fn handler_handles() { let event = ApiGatewayProxyRequest { resource: None, path: Some( "/api/pokemon/bulbasaur".to_string(), ), ... }; assert_eq!( handler(event.clone(), Context::default()) .await .unwrap(), ApiGatewayProxyResponse { status_code: 200, headers: HeaderMap::new(), multi_value_headers: HeaderMap::new(), body: Some(Body::Text( serde_json::to_string(&PokemonHp { name: String::from("Bulbasaur"), hp: 45 },) .unwrap() )), is_base64_encoded: Some(false), } ) } ``` Running cargo test with the database url will now pass. ```rust DATABASE_URL=mysql://127.0.0.1 cargo test ```
- Loading branch information