{"id":15679810,"url":"https://github.com/yukinarit/rocket-webapi","last_synced_at":"2025-05-07T10:43:03.312Z","repository":{"id":83343205,"uuid":"138741252","full_name":"yukinarit/rocket-webapi","owner":"yukinarit","description":"Making JSON webapi server with Rust and Rocket :rocket:","archived":false,"fork":false,"pushed_at":"2020-08-27T03:21:54.000Z","size":32,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-31T09:11:13.949Z","etag":null,"topics":["rocket","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yukinarit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-26T13:20:51.000Z","updated_at":"2021-03-07T10:50:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"b79028d8-65b2-4917-93d3-448ffb2db593","html_url":"https://github.com/yukinarit/rocket-webapi","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukinarit%2Frocket-webapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukinarit%2Frocket-webapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukinarit%2Frocket-webapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukinarit%2Frocket-webapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yukinarit","download_url":"https://codeload.github.com/yukinarit/rocket-webapi/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252862240,"owners_count":21815825,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rocket","rust"],"created_at":"2024-10-03T16:36:54.477Z","updated_at":"2025-05-07T10:43:03.301Z","avatar_url":"https://github.com/yukinarit.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"Making a RESTful JSON API with Rust and Rocket\n==============================================\n\n[English](README.md)\n\nRocketは素晴らしいWebアプリケーションフレームワークで、サンプルコードも充実しているけど、WebAPIサーバーを作る包括的なチュートリアルが無かったように思えたので書いてみることにする。なお、このリポジトリコードはこの[記事](https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask)を参考にしている。\n\n\nTable of Contents\n=================\n\n* [Setup](#setup)\n* [Run](#run)\n* [Usage](#usage)\n* [Tutorial](#tutorial)\n  - [Rocketとは？](#rocketとは)\n  - [Hello Rocket!](#hello-rocket)\n  - [ToDOアプリのWebAPIをつくる](#todoアプリのwebapiをつくる)\n  - [Responder](#responder)\n\n\nSetup\n=====\n\n```bash\n# rustupをインストールする.\n$ curl https://sh.rustup.rs -sSf | /bin/bash -s -- -y --default-toolchain nightly\n```\n\nRun\n===\n\n```bash\n$ cd rocket-webapi\n$ cargo run\n\n\n🔧  Configured for development.\n    =\u003e address: localhost\n    =\u003e port: 8000\n    =\u003e log: normal\n    =\u003e workers: 8\n    =\u003e secret key: generated\n    =\u003e limits: forms = 32KiB\n    =\u003e tls: disabled\n🛰  Mounting '/':\n    =\u003e GET /\n    =\u003e GET /todos\n    =\u003e GET /todos/\u003ctodoid\u003e\n🚀  Rocket has launched from http://localhost:8000\n```\n\n\nUsage\n=====\n\n* Hello, world!\n\t```\n\thttp://localhost:8000\n\t```\n\n* Get all ToDOs\n\t```\n\thttp://localhost:8000/todos\n\t```\n\n* Get ToDO by ID\n\t```\n\thttp://localhost:8000/todos/10\n\t```\n\n\nTutorial\n========\n\nなぜRustなのか？\n----------------\n\nRustは型安全でゼロコスト抽象化を実現したシステムプログラミング言語だ。巷ではC言語の代替と言われることもあるが、実際使ってみるとより安全なC++としての趣きが強いと思う。筆者は10年以上C++でMMOゲームや金融系のハイパフォーマンスサーバーを書いてきて、C++17とかModern CMakeとか、Rangeとかテンプレートプログラミングとかを色々追ってきたが、Rustに出会ってからC++の最新を追うのをやめた。Rustは例えるなら(まだない)C++23に安全性とSaneなメタプロシステムと最高のビルドシステムと最高のパッケージマネージャが付いてきた、みたいな感じ。それほどまでにRustは素晴らしい機能と言語としての表現力を持っている(と思う)。\n\nRocketとは？\n-----------\n\nRocketはRustで書かれたタイプセーフなマイクロウェブアプリケーションフレームワークで、PythonのFlaskに似ている感じ。それでは、シンプルなTODOアプリのWebAPIサーバーを作ってみて、Rocketの使い方を解説してみる。\n\nHello Rocket!\n-------------\n\nまずは、Hello, worldを返すだけのアプリを作ってみる。\n\n* プロジェクト作成\n\t```bash\n\t$ cargo new rocket-webapi\n\t$ cd rocket-webapi\n\t```\n\n* rust-toolchain\n\t```\n\tnightly\n\t```\n\n* Cargo.toml\n\t```\n\t[package]\n\tname = \"rocket-jsonapi\"\n\tversion = \"0.1.0\"\n\t\n\t[dependencies]\n\trocket = \"0.4\"\n\trocket_contrib = { version = \"0.4\", features = [\"json\"] }\n\t```\n\n* src/main.rs\n\t```rust\n\t#![feature(proc_macro_hygiene)]\n\t#![feature(decl_macro)]\n\t\n\t#[macro_use]\n\textern crate rocket;\n\t\n\t/// GETがきたときに\"Hello, world!\"というレスポンスを返す\n\t#[get(\"/\")]\n\tfn index() -\u003e \u0026'static str {\n\t    \"Hello, world!\"\n\t}\n\t\n\tfn main() {\n\t    rocket::ignite()\n\t        .mount(\"/\", routes![index])  // ここにルーティングをセットする\n\t        .launch();\n\t}\n\t```\n\n実行してみる。\n\n```bash\n$ cargo run\n\n\n🔧  Configured for development.\n    =\u003e address: localhost\n    =\u003e port: 8000\n    =\u003e log: normal\n    =\u003e workers: 8\n    =\u003e secret key: generated\n    =\u003e limits: forms = 32KiB\n    =\u003e tls: disabled\n🛰  Mounting '/':\n    =\u003e GET /\n🚀  Rocket has launched from http://localhost:8000\n```\n\n```bash\n$ curl http://localhost:8000\nHello, world!\n```\n\n動いた！ ＼(^o^)／\n\nここでコンパイルエラーが出た人は、以下を試してみてほしい\n\n```\n# Rust toolchainを更新する\nrustup update\n```\n\nToDOアプリのWebAPIをつくる\n--------------------------\n\n次にいきおいでToDoのWebAPIを作ってみる。\n\n* Cargo.toml\n\t```\n\t[package]\n\tname = \"rocket-webapi\"\n\tversion = \"0.1.0\"\n\t\n\t[dependencies]\n\trocket = \"0.4\"\n\trocket_contrib = { version = \"0.4\", features = [\"json\"] }\n\t# serdeのcrateを追加する\n    serde = { version = \"1.0\", features = [\"derive\"] }\n    serde_json = \"1.0.0\"\n\t```\n\n* main.rs \n\t```rust\n\t#![feature(proc_macro_hygiene)]\n\t#![feature(decl_macro)]\n\t\n\t#[macro_use]\n\textern crate rocket;\n\t\n\tmod models;\n\tmod routes;\n\t\n\t// WebAPIのURLルーティングはroutes.rsに移動する\n\tuse routes::*;\n\t\n\tfn main() {\n\t    rocket::ignite()\n\t        .mount(\"/\", routes![index, todos, new_todo, todo_by_id])\n\t        .launch();\n\t}\n\t```\n\n* routes.rs\n\t```rust\n\t// JSONを返すのに必要\n    use rocket_contrib::json::Json;\n\t\n\tuse crate::models::ToDo;\n\t\n\t#[get(\"/\")]\n\tpub fn index() -\u003e \u0026'static str {\n\t    \"Hello, world!\"\n\t}\n\t\n\t/// TODOリストを返す。\n\t/// Jsonの型がResponderをimplしているので、JSON文字列を返すことができる\n\t#[get(\"/todos\")]\n\tpub fn todos() -\u003e Json\u003cVec\u003cToDo\u003e\u003e {\n\t    Json(vec![ToDo {\n\t        id: 1,\n\t        title: \"Read Rocket tutorial\".into(),\n\t        description: \"Read https://rocket.rs/guide/quickstart/\".into(),\n\t        done: false,\n\t    }])\n\t}\n\t\n\t/// 新しいTODOを作成する\n\t/// POSTの時はこうする\n\t#[post(\"/todos\", data = \"\u003ctodo\u003e\")]\n\tpub fn new_todo(todo: Json\u003cToDo\u003e) -\u003e String {\n\t    format!(\"Accepted post request! {:?}\", todo.0)\n\t}\n\t\n\t\n\t/// TODOを取得する\n\t#[get(\"/todos/\u003ctodoid\u003e\")]\n\tpub fn todo_by_id(todoid: u32) -\u003e String {\n\t    let todo = ToDo {\n\t        id: 1,\n\t        title: \"Read Rocket tutorial\".into(),\n\t        description: \"Read https://rocket.rs/guide/quickstart/\".into(),\n\t        done: false,\n\t    };\n\t    format!(\"{:?}\", todo)\n\t}\n\t```\n\n* models.rs\n\t```rust\n    use serde::{Deserialize, Serialize};\n\n\t/// TODOのモデルはmodels.rsに定義\n\t#[derive(Debug, Serialize, Deserialize)]\n\tpub struct ToDo {\n\t    pub id: u32,\n\t    pub title: String,\n\t    pub description: String,\n\t    pub done: bool,\n\t}\n\t```\n\n実行してみる。\n\n```bash\n$ cargo run\n\n🔧  Configured for development.\n    =\u003e address: localhost\n    =\u003e port: 8000\n    =\u003e log: normal\n    =\u003e workers: 8\n    =\u003e secret key: generated\n    =\u003e limits: forms = 32KiB\n    =\u003e tls: disabled\n🛰  Mounting '/':\n    =\u003e GET /\n    =\u003e GET /todos\n    =\u003e POST /todos\n    =\u003e GET /todos/\u003ctodoid\u003e\n🚀  Rocket has launched from http://localhost:8000\n```\n\ncurlでリクエストしてみる。\n\n```bash\n$ curl -i http://localhost:8000/todos\n\nHTTP/1.1 200 OK\nContent-Type: application/json\nServer: Rocket\nContent-Length: 111\nDate: Wed, 04 Jul 2018 13:44:50 GMT\n\n[{\"id\":1,\"title\":\"Read Rocket tutorial\",\"description\":\"Read https://rocket.rs/guide/quickstart/\",\"done\":false}]\n```\n\nOK, いい感じ。\n\n次、POST。\n\n```bash\n$ curl -i -H \"Content-Type: application/json\" -X POST -d '{\"id\": 100, \"title\":\"Read this book\", \"description\": \"http://shop.oreilly.com/product/0636920040385.do\", \"done\": false}' http://localhost:20000/todos\n\nHTTP/1.1 200 OK\nContent-Type: text/plain; charset=utf-8\nServer: Rocket\nContent-Length: 142\nDate: Thu, 05 Jul 2018 03:55:22 GMT\n\nAccepted post request! ToDo { id: 100, title: \"Read this book\", description: \"http://shop.oreilly.com/product/0636920040385.do\", done: false }\n```\n\nPOSTもOK。\n\nResponder\n---------\n\n上記の例でJson型の戻り値を返すとJSONの文字列がレスポンスとして返った。この仕組みを実現いているのがResponderトレイトだ。Rocketのルーティングの関数はすべてResponderトレイトをimplしなければならない。\n\n難しそうい聞こえるが、実際にはRocketがいろいろな型のResponderトレイトをあらかじめimplしといてくれるので、自分でimplする場面は意外に少ないかもしれない。以下に主なResponderのimplを示す。\n\n| 型                         | レスポンス                                     |\n| -------------------------- | ---------------------------------------------- |\n| \u0026'static str, \u0026str, String | text/plainの文字列が返る                       |\n| NamedFile                  | ファイルの文字列が返る                         |\n| Redirect                   | 別のURLにリダイレクトする                      |\n| Stream                     | HTTPストリーミングレスポンスが返る             |\n| Json                       | application/jsonのJSON文字列が返る             |\n| Template                   | Templateをレンダリングした結果が返る           |\n| rocket::response::statusにある型 | 例えばAcceptedの場合あ203 Acceptedになる |\n| Option\u003cT\u003e | Some(T)の場合はTのResponder、Noneの場合は404 Not Foundになる    |\n| Result\u003cT,E\u003e | Ok(T)の場合はT、Err(E)の場合はUのResponderの結果が返る        |\n\n最後の3つはWrapping Responderと言われておりWrapした中身のResponderの結果を装飾したり、中身の型によって動きを動的に返る役割を持つ。\n\n以下の例を見てみよう。\n\n* JSONを一つ返す\n\t```rust\n\tfn sample() -\u003e Json\u003cToDo\u003e {\n\t    Json(ToDo {\n\t        id: 1,\n\t        title: \"Read Rocket tutorial\".into(),\n\t        description: \"Read https://rocket.rs/guide/quickstart/\".into(),\n\t        done: false,\n\t    })\n\t}\n\t```\n\t```bash\n\t$ curl http://localhost:8000/todos\n\n\t{\"id\":1,\"title\":\"Read Rocket tutorial\",\"description\":\"Read https://rocket.rs/guide/quickstart/\",\"done\":false}\n\t```\n\nなるほど。\n\n* Vectorにしてみる\n\t```rust\n\tfn sample() -\u003e Vec\u003cJson\u003cToDo\u003e\u003e {\n\t\tJson(vec![ToDo {\n\t\t    id: 1,\n\t\t    title: \"Read Rocket tutorial\".into(),\n\t\t    description: \"Read https://rocket.rs/guide/quickstart/\".into(),\n\t\t    done: false,\n\t\t}])\n\t}\n\t```\n\t```bash\n\t$ curl http://localhost:8000/todos\n\n\t[{\"id\":1,\"title\":\"Read Rocket tutorial\",\"description\":\"Read https://rocket.rs/guide/quickstart/\",\"done\":false}]\n\t```\n\nJSONがArrayになった。おぉいいね。\n\n* rocket::response::status::Acceptedでwrapすると\n\t```rust\n\tuse rocket::response::status::Accepted;\n\tfn sample() -\u003e Accepted\u003cVec\u003cJson\u003cToDo\u003e\u003e\u003e {\n\t\tAccepted(Some(Json(vec![ToDo {\n\t\t\tid: 1,\n\t\t\ttitle: \"Read Rocket tutorial\".into(),\n\t\t\tdescription: \"Read https://rocket.rs/guide/quickstart/\".into(),\n\t\t\tdone: false,\n\t\t}])))\n\t}\n\t```\n\t```bash\n\t$ curl -i http://localhost:8000/todos\n\tHTTP/1.1 202 Accepted\n\tContent-Type: application/json\n\tServer: Rocket\n\tContent-Length: 111\n\tDate: Fri, 06 Jul 2018 15:32:19 GMT\n\n\t[{\"id\":1,\"title\":\"Read Rocket tutorial\",\"description\":\"Read https://rocket.rs/guide/quickstart/\",\"done\":false}]\n\t```\nStatus codeが202 Acceptedになる。\n\nスバラシイ。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyukinarit%2Frocket-webapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyukinarit%2Frocket-webapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyukinarit%2Frocket-webapi/lists"}