rust 74 lines · 9 steps

Building a GitHub proxy handler in Axum

An Axum handler fetches a GitHub repo through a shared HTTP client and maps upstream failures onto clean HTTP status codes.

Explained by highlit
1use axum::{
2 extract::{Path, State},
3 http::StatusCode,
4 response::IntoResponse,
5 Json,
6};
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9 
10#[derive(Clone)]
11pub struct AppState {
12 pub http: reqwest::Client,
13 pub github_token: String,
14}
15 
16impl AppState {
17 pub fn new(github_token: String) -> Self {
18 let http = reqwest::Client::builder()
19 .user_agent("acme-gateway/1.0")
20 .timeout(Duration::from_secs(10))
21 .pool_max_idle_per_host(16)
22 .build()
23 .expect("failed to build reqwest client");
24 
25 Self { http, github_token }
26 }
27}
28 
29#[derive(Deserialize)]
30struct GithubRepo {
31 full_name: String,
32 stargazers_count: u64,
33 open_issues_count: u64,
34}
35 
36#[derive(Serialize)]
37struct RepoSummary {
38 name: String,
39 stars: u64,
40 open_issues: u64,
41}
42 
43pub async fn repo_summary(
44 State(state): State<AppState>,
45 Path((owner, repo)): Path<(String, String)>,
46) -> Result<Json<RepoSummary>, StatusCode> {
47 let url = format!("https://api.github.com/repos/{owner}/{repo}");
48 
49 let resp = state
50 .http
51 .get(&url)
52 .bearer_auth(&state.github_token)
53 .header("Accept", "application/vnd.github+json")
54 .send()
55 .await
56 .map_err(|_| StatusCode::BAD_GATEWAY)?;
57 
58 if resp.status() == reqwest::StatusCode::NOT_FOUND {
59 return Err(StatusCode::NOT_FOUND);
60 }
61 
62 let repo: GithubRepo = resp
63 .error_for_status()
64 .map_err(|_| StatusCode::BAD_GATEWAY)?
65 .json()
66 .await
67 .map_err(|_| StatusCode::BAD_GATEWAY)?;
68 
69 Ok(Json(RepoSummary {
70 name: repo.full_name,
71 stars: repo.stargazers_count,
72 open_issues: repo.open_issues_count,
73 }))
74}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Sharing a single reusable HTTP client through application state lets every request benefit from connection pooling and consistent timeouts.
  2. 2Deserializing only the fields you care about keeps your types decoupled from the full shape of an upstream API.
  3. 3Mapping every upstream error onto a deliberate StatusCode turns a fragile proxy into a predictable one.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a GitHub proxy handler in Axum — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code