Let's build a REST API in Rust π¦ - Part 2
Nov 03, 2024Last week we built a very simple REST API in Rust with a health endpoint. Today I want to show you step-by-step how to add a second endpoint that will return historical NYC Taxi Data.
Github repository
You can find all the source code in this repository
Give it a star β on Github to support my work
The starting point π
Our initial API does nothing especial, apart from saying “Hey! I am alive”.
We accomplished this by:
-
Adding a /health route to our HttpServer,
-
Attaching an async function health() to handle requests to this endpoint, and
-
Implementing this health() function.
Let me show you step-by-step how to add a second endpoint to our HttpServer, to serve historical data of NYC taxi trips from this website.
π¨ Attention π¨
To keep things simple, and not get overwhelmed, we will be returning fake data today.
Next week I will show you how to get real data by
-
Parsing from the GET request the time period for which the client app requests data,
-
Downloading the necessary Parquet files asynchronously from this website.
-
Processing the the data using Polars (similar to pandas)
-
Returning the data as JSON
Step 1. Add a new /trips endpoint π©
The steps are:
-
Add a trips route to our actix web app
-
Define the trips() function to handles get requests to this endpoint, and
-
Parse 2 input parameters from the GET request
-
from_ms → timestamp in milliseconds from which we want data, and
-
n_results → number of data points to return to the client app.
-
What is web::Query<TripQuery> β
This is Actix-web's way of handling query parameters in URLs.
-
web::Query is a struct provided by Actix-web that extracts and deserializes query parameters from the URL
-
TripsQuery is our custom struct that specifies the expected structure of the query parameters:
When a client app makes a request to your endpoint like:
GET /trips?from_ms=1234567890&n_results=10
Actix-web will:
-
Extract these query parameters from the URL
-
Automatically deserialize them into your TripsQuery struct
-
Make them available to our handler function as query.from_ms and query.n_results
Cool! We have an endpoint that extracts the parameters of the GET request. Let’s now move to the business logic.
Step 2. Extract business logic to another file π‘
It is best to decouple the function handlers, and the underlying functions that do the heavy work.
Create another file called backend.rs, with one public function called get_fake_trips()
Why is get_fake_trips() publicβ
The pub keyword in this function makes it publicly accessible from our main.rs file. In Rust, privacy is enforced by the compiler:
-
pub = like a normal Python function (accessible everywhere)
-
no modifier = truly private (only accessible in the same module)
-
pub(crate) = accessible only within your project (no equivalent in Python)
So while Python's privacy is "we trust you not to use this", Rust's privacy is "you literally cannot use this unless allowed".
Inside the get_fake_trips we randomly generate a vector of n_result Trips
Let’s now use this function.
Step 3. Call the get_fake_trips π
Finally, we
-
import the get_fake_trips function into scope, and
-
call it, and
-
handle its return value using the match expression
What is match and why do we need it hereβ
get_fake_trips() returns a Result type, which in Rust is an enum that can be either:
-
Ok(value) - containing the successful result
-
Err(error) - containing an error if something went wrong
The match expression checks which variant we got and handles each case:
-
If we got Ok(trips), it returns an HTTP 200 response with the trips data as JSON
-
If we got Err(e), it returns an HTTP 500 error with the error message
Step 4. Test the endpoint works π
From the command line run
$ curl -X GET "http://localhost:$(PORT)/trips?from_ms=1714204800000&n_results=100"
and you should see a nice batch of 100 (fake) taxi trips.
Next steps π£
Next week we will
-
fetch and serve Real World data of NYC taxi trips from this website,
-
add some logging, and
-
dockerize our REST API for production
Let’s Rust ! π¦
On November 18th, 19 brave students will join my on a LIVE ADVENTURE, Let’s Rust!
It will take us 2 weeks, 4 sessions, and 12 hours of intensive coding sessions π₯π₯π₯ to build and deploy an ML model in Rust.
Are you ready for the challenge? π
Talk to you next week,
Enjoy the weekend,
Pau