Warum Rust der ideale Kandidat für die Arbeit mit APIs ist?
\n\nWenn Sie immer noch denken, dass Rust nur für Systemprogrammierung und als Ersatz für C++ gedacht ist, sind Sie der Realität weit hinterher. Rust hat sich selbstbewusst in die Welt der Webentwicklung und Backend-Dienste vorgewagt. Und nicht nur das: Es hat sich eine Nische erobert, in der Geschwindigkeit, Zuverlässigkeit und Speichersicherheit entscheidend sind.
\n\nAber seien wir ehrlich: Mit APIs in Rust zu arbeiten bedeutet nicht einfach, „eine fertige Bibliothek zu nehmen und zu vergessen". Es ist eine Kunst des Gleichgewichts zwischen der Leistungsfähigkeit der Sprache und der Entwicklungsfreundlichkeit. In diesem Artikel betrachten wir drei Hauptansätze: REST, GraphQL und gRPC. Jeder hat seine Vorzüge, und die Wahl hängt von Ihrer Aufgabe ab. Los geht's!
\n\nREST mit Rust: Klassiker, die funktionieren
\n\nREST bleibt der beliebteste API-Stil. Er ist einfach, intuitiv und wird buchstäblich von allem unterstützt. Für Rust gibt es mehrere hervorragende Frameworks, aber der König ist hier actix-web. Es ist schnell, asynchron und hat ein großartiges Ökosystem.
Einen einfachen REST-Dienst mit actix-web schreiben
\n\nErstellen wir einen Minidienst zur Verwaltung einer Aufgabenliste (Todo). Wir benötigen actix-web, serde für die Serialisierung und tokio als asynchrone Laufzeitumgebung.
// Cargo.toml\n// [dependencies]\n// actix-web = "4"\n// serde = { version = "1", features = ["derive"] }\n// serde_json = "1"\n// tokio = { version = "1", features = ["full"] }\n\nuse actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};\nuse serde::{Deserialize, Serialize};\nuse std::sync::Mutex;\n\n#[derive(Serialize, Deserialize, Clone)]\nstruct Task {\n id: u32,\n title: String,\n completed: bool,\n}\n\nstruct AppState {\n tasks: Mutex<Vec<Task>>,\n}\n\n#[get("/tasks")]\nasync fn get_tasks(data: web::Data<AppState>) -> impl Responder {\n let tasks = data.tasks.lock().unwrap();\n HttpResponse::Ok().json(tasks.clone())\n}\n\n#[post("/tasks")]\nasync fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> impl Responder {\n let mut tasks = data.tasks.lock().unwrap();\n let new_id = tasks.len() as u32 + 1;\n let task = Task {\n id: new_id,\n title: body.title.clone(),\n completed: false,\n };\n tasks.push(task.clone());\n HttpResponse::Created().json(task)\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n let state = web::Data::new(AppState {\n tasks: Mutex::new(vec![]),\n });\n\n HttpServer::new(move || {\n App::new()\n .app_data(state.clone())\n .service(get_tasks)\n .service(create_task)\n })\n .bind("127.0.0.1:8080")?\n .run()\n .await\n}\n\n\nDas Schöne an diesem Code ist, dass er speichersicher ist und keinen Garbage Collector benötigt. actix-web verarbeitet Tausende von Anfragen pro Sekunde in einem einzigen Thread unter Verwendung der asynchronen Mechanismen von Rust. Versuchen Sie, ihn auszuführen – Sie werden überrascht sein, wie schnell er selbst auf schwacher Hardware ist.
Wann ist REST nicht die beste Wahl?
\n\nREST eignet sich hervorragend für CRUD-Operationen, aber wenn Sie Daten flexibel abfragen müssen (z. B. verschachtelte Ressourcen), erfordert REST viele Endpunkte. Hier kommt GraphQL ins Spiel.
\n\nGraphQL mit Rust: Flexibilität ohne Schmerz
\n\nGraphQL ermöglicht es dem Client, genau die Daten abzufragen, die benötigt werden. Für Rust ist die beste Wahl async-graphql. Es lässt sich eng in actix-web integrieren und bietet ein leistungsstarkes DSL zur Beschreibung von Schemata.
Beispiel: API für eine Bibliothek von Büchern
\n\nAngenommen, wir möchten Clients die Möglichkeit geben, Bücher und Autoren abzufragen. Mit GraphQL wird dies mit einem einzigen Schema erledigt.
\n\n// Cargo.toml\n// [dependencies]\n// async-graphql = "7"\n// async-graphql-actix-web = "7"\n// actix-web = "4"\n// serde = { version = "1", features = ["derive"] }\n\nuse async_graphql::{Context, EmptyMutatio