{"id":101897,"date":"2024-04-26T14:54:23","date_gmt":"2024-04-26T14:54:23","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=101897"},"modified":"2024-03-25T15:46:17","modified_gmt":"2024-03-25T15:46:17","slug":"building-restful-apis-in-rust-with-actix-and-diesel","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/web\/building-restful-apis-in-rust-with-actix-and-diesel\/","title":{"rendered":"Rust REST API Tutorial: Building with Actix-web and Diesel ORM &#8211; Handlers, Models, Migrations, and Database Integration"},"content":{"rendered":"<p><b>Rust is a systems programming language that has gained mainstream adoption for web services because of its performance (near-C speeds), memory safety (no garbage collector, no null pointer exceptions, no data races), and growing ecosystem of production-grade web libraries. This article builds a complete REST API in Rust using two cornerstone libraries: (1) Actix-web &#8211; the most widely-used Rust web framework, known for exceptional performance (consistently top of TechEmpower benchmarks) and a pragmatic actor-based architecture; (2) Diesel &#8211; the mature Rust ORM, providing compile-time-checked query construction, schema migrations, and support for PostgreSQL, MySQL, and SQLite. Walkthrough: (a) Cargo project setup with Actix-web and Diesel dependencies; (b) defining models and deriving Diesel&#8217;s table schema; (c) database migrations using the diesel CLI; (d) connection pooling with r2d2; (e) Actix-web route definitions and handler functions; (f) CRUD handlers (GET, POST, PUT, DELETE) with JSON request\/response via serde; (g) error handling patterns using Result<T, E> and custom error types. Ecosystem note: the Rust web ecosystem has evolved rapidly &#8211; Axum (by the Tokio team) has become a popular alternative to Actix-web, SQLx has become a popular alternative to Diesel (async-first, query-time checking rather than compile-time). Actix-web and Diesel remain solid choices and are production-proven; the decision between them vs Axum\/SQLx is largely based on async preferences and compile-time vs runtime check tradeoffs.<\/b><\/p>\n<p>There are many packages and tools that you can use to facilitate your API development with Rust. Rust has a rich third-party ecosystem of crates for building APIs, including web packages like <a href=\"https:\/\/actix.rs\/\">Actix<\/a> and <a href=\"https:\/\/rocket.rs\/\">Rocket<\/a> and ORMs like <a href=\"https:\/\/diesel.rs\/\">Diesel<\/a> and <a href=\"https:\/\/www.sea-ql.org\/SeaORM\/\">SeaORM<\/a>.<\/p>\n<p>This article delves into using Actix and Diesel to build web applications. You\u2019ll learn by building a CRUD API with persistence through Diesel on a <a href=\"https:\/\/sqlite.org\/download.html\">Sqlite<\/a> database.<\/p>\n<p>Actix is a high-performance web framework that runs on the actor model. It is great at handling concurrent requests with lightweight actors that communicate asynchronously. Actix\u2019s architecture promotes scalability and responsiveness, which is ideal for building performant web apps.<\/p>\n<p>For data persistence, Diesel is a mature ORM that flexibly acts as a bridge between Rust data types and database tables. You\u2019ll write native Rust programs, and Diesel will create statements and queries to execute on your preferred DBMS.<\/p>\n<h2>Setting up the API Development Environment<\/h2>\n<p>Setting up a development environment for building REST APIs in Rust is relatively simple. You must<a href=\"https:\/\/www.rust-lang.org\/tools\/install\"> download and install<\/a> a recent version of Rust on your computer to get started.<\/p>\n<p>Run this command on your terminal to verify that you\u2019ve successfully installed Rust and Cargo (Rust\u2019s package management tool).<\/p>\n<pre class=\"lang:c# theme:vs2012\">rustc --version\r\ncargo --version<\/pre>\n<p>Next, Run these commands on your terminal to create and initialize a new Rust project on your computer:<\/p>\n<pre class=\"lang:c# theme:vs2012\">mkdir rusting-actix-diesel &amp;&amp; cd rusting-actix-diesel\r\n\r\ncargo init rusting-actix-diesel<\/pre>\n<p>The <code>cargo init<\/code> command initializes a new Rust project in the specified working directory. The command also creates a <code>cargo.toml<\/code> file in the directory for managing your project\u2019s dependencies.<\/p>\n<p>You\u2019ll need some form of persistence for your database. In this tutorial, you\u2019ll learn how to use Diesel and an SQL database for persistence for your API. Diesel supports a variety of SQL databases, including <a href=\"https:\/\/sqlite.org\/download.html\">Sqlite<\/a>, <a href=\"https:\/\/dev.mysql.com\/downloads\/\">MySQL<\/a>, and <a href=\"https:\/\/www.postgresql.org\/download\/\">PostgreSQL<\/a>. Install your preferred database management system, and you\u2019re good to go.<\/p>\n<h2><a id=\"post-101897-_5e4uz9idj1p0\"><\/a>Setting Up the Database for Persistence<\/h2>\n<p>You have to add the <code>diesel<\/code> and <code>dotenv<\/code> crates as project dependencies in the dependencies section of your <code>cargo.toml<\/code> file.<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">[dependencies]\r\ndiesel = { version = \"2.1.0\", features = [\"sqlite\"] }\r\ndotenv = \"0.15.0\"<\/pre>\n<p>Once you\u2019ve added these crates as dependencies, you must install the <code>diesel_cli<\/code> tool to interact with Diesel over the command line for migrations and schema generation.<\/p>\n<p>Run this command to install the <code>diesel_cli<\/code> tool:<\/p>\n<pre class=\"lang:c# theme:vs2012\">cargo install diesel_cli<\/pre>\n<p>You can run the <code>diesel_cli<\/code> tool <code>diesel_cli<\/code> command after installing the tool.<\/p>\n<p>Next, create an environment variables file and specify your database URL with the <code>DATABASE_URL<\/code> field. Run this command to create and insert the database URL for an in-memory Sqlite database.<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">echo DATABASE_URL=database.db &gt; .env<\/pre>\n<p>Next, run the <code>setup<\/code> command for Diesel to set up a database for your project:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">diesel setup<\/pre>\n<p>The <code>setup<\/code> command creates a <code>migrations<\/code> directory, the database specified in the <code>DATABASE_URL<\/code>, and runs existing migrations.<\/p>\n<p>After you\u2019ve set up your database with Diesel, you\u2019ll use the <code>migration generate<\/code> command to generate SQL migration files. You\u2019ll add the name of the migration as an argument to the <code>migration generate<\/code> command:<\/p>\n<pre class=\"lang:c# theme:vs2012\">diesel migration generate create_humans<\/pre>\n<p>The command generates two SQL files in the <code>migrations<\/code> directory: <code>up.sql<\/code> and <code>down.sql<\/code>.<\/p>\n<p>Write SQL for your database table definitions in the <code>up.sql<\/code> file as thus:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">-- up.sql\r\n\r\n-- Your SQL goes here\r\nCREATE TABLE \"human\"\r\n(\r\n    \"id\"         INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\r\n    \"first_name\" TEXT    NOT NULL,\r\n    \"last_name\"  TEXT    NOT NULL,\r\n    \"age\"        INTEGER NOT NULL\r\n);<\/pre>\n<p>Then, write the SQL code to drop database tables in the <code>down.sql<\/code> file:<\/p>\n<pre class=\"lang:c# theme:vs2012\">-- down.sql\r\n\r\n-- This file should undo anything in `up.sql`\r\nDROP TABLE \"human\"<\/pre>\n<p>After writing the SQL files, run the <code>migration run<\/code> command to apply pending migrations.<\/p>\n<pre class=\"lang:c# theme:vs2012\">diesel migration run<\/pre>\n<p>After a successful process, you can use the <code>print-schema<\/code> command to print the schema. The command prints the contents of the schema.rs file.<\/p>\n<p>diesel print-schema<\/p>\n<p>The output of the <code>print_schema<\/code> command is Rust code that matches your SQL schema.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1734\" height=\"832\" class=\"wp-image-101898\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/03\/word-image-101897-1.png\" \/><\/p>\n<p>Attach the <code>schema.rs<\/code> file to your <code>main.rs<\/code> file with the <code>mod schema<\/code> directive to use the contents of the <code>schema.rs<\/code> file in the <code>main.rs<\/code> file and other parts of your package.<\/p>\n<p>You must declare structs for data serialization, migrations, and deserialization operations. You can create a models.rs file and add struct definitions to match your database schema.<\/p>\n<p>Here are the structs for the CRUD operations:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">\/\/ models.rs\r\nuse diesel::prelude::*;\r\nuse serde::{Deserialize, Serialize};\r\nuse crate::schema::human;\r\n#[derive(Queryable, Serialize)]\r\npub struct Human {\r\n    pub id: i32,\r\n    pub first_name: String,\r\n    pub last_name: String,\r\n    pub age: i32,\r\n}\r\n#[derive(Queryable, Insertable, Serialize, Deserialize)]\r\n#[table_name = \"human\"]\r\npub struct NewHuman {\r\n    pub first_name: String,\r\n    pub last_name: String,\r\n    pub age: i32,\r\n}\r\n#[derive(Deserialize, AsChangeset)]\r\n#[table_name = \"human\"]\r\npub struct UpdateHuman {\r\n    first_name: Option&lt;String&gt;,\r\n    last_name: Option&lt;String&gt;,\r\n    age: Option&lt;i32&gt;,\r\n}<\/pre>\n<p>The request handler functions returns the <code>Student<\/code> struct. You can use the <code>NewStudent<\/code> for data migration and the <code>UpdateStudent<\/code> struct for PUT requests.<\/p>\n<h3><a id=\"post-101897-_w67glqlj76t9\"><\/a>Connecting to Your SQL Database With Diesel<\/h3>\n<p>You\u2019ll use the <code>env<\/code> and <code>Connection<\/code> to connect your SQL database with Diesel.<\/p>\n<p>Here\u2019s how you can connect to an SQLite database with a function and return a connection instance:<\/p>\n<pre class=\"lang:c# theme:vs2012\">\/\/ src\/model.rs\r\nuse std::env;\r\nuse diesel::prelude::*;\r\nuse dotenv::dotenv;\r\npub fn establish_connection() -&gt; SqliteConnection {\r\n    dotenv().ok();\r\n    let database_url = env::var(\"DATABASE_URL\").expect(\r\n                  \"DATABASE_URL must be set\");\r\n    SqliteConnection::establish(&amp;database_url)\r\n        .unwrap_or_else(|_| panic!\r\n        (\"Error connecting to the database: {}\",  \r\n                         database_url))\r\n}<\/pre>\n<p>The <code>establish_connection<\/code> function returns the connection instance struct (<code>SqliteConnection<\/code>). The <code>establish_connection<\/code> loads the environment variables with the <code>ok<\/code> function accesses the database URL with the <code>var<\/code> function, and establishes a connection with the database via the URL with the <code>establish<\/code> function.<\/p>\n<h2><a id=\"post-101897-_pd6hve73xz72\"><\/a>Setting Up Actix-web for Routing and Server Operations<\/h2>\n<p>After setting up the database for persistence, you can set up a server with Actix with routes for your handler functions.<\/p>\n<p>Here are the contents of the [<code>main.rs<\/code>] file containing the <code>main<\/code> function that handles the routing and starts the server:<\/p>\n<pre class=\"lang:c# theme:vs2012\">\/\/ main.rs\r\n\/\/ attaches the files in the package to the `main.rs` file \r\nmod database;\r\nmod model;\r\nmod schema;\r\nmod handlers;\r\n\/\/ import the functionality for starting the server\r\nuse actix_web::{web, App, HttpServer};\r\n\/\/ import the handler function\r\nuse crate::handlers::{create_human, delete_human, get_humans, update_human};\r\n#[actix_web::main]\r\nasync fn main() -&gt; std::io::Result&lt;()&gt; {\r\n    HttpServer::new(|| {\r\n        App::new()\r\n            .route(\"\/human\", web::post().to(create_human))\r\n            .route(\"\/humans\", web::get().to(get_humans)) \r\n                      \/\/ changed this route path\r\n            .route(\"\/human\/{id}\", web::put().to(update_human))\r\n            .route(\"\/human\/{id}\", web::delete().to(delete_human))\r\n    })\r\n        .bind(\"127.0.0.1:8080\")?\r\n        .run()\r\n        .await\r\n}<\/pre>\n<p>The <code>main<\/code> function is an asynchronous function that starts the server with the <code>HttpServer::new<\/code> function that takes in the <code>App::new<\/code> function, which handles the routes. The <code>bind<\/code> function binds the routes to the specified host, and the <code>run<\/code> function runs the server.<\/p>\n<p>After setting up the server, you can import these functions in your <code>[handlers.rs](&lt;http:\/\/handlers.rs&gt;)<\/code> file, where you\u2019ll specify the handler functions for the API.<\/p>\n<pre class=\"lang:c# theme:vs2012\">\/\/ handlers.rs\r\n\/\/ import the database connection function\r\nuse crate::database::establish_connection;\r\n\/\/ import necessary actix functionality\r\nuse actix_web::{web, App, HttpResponse, HttpServer, Result};\r\n\/\/ import necessary diesel functionality\r\nuse diesel::{prelude::*, sqlite::SqliteConnection};\r\n\/\/ import the structs from model.rs\r\nuse crate::model::{Human, NewHuman, UpdateHuman};<\/pre>\n<p>Once you\u2019ve imported the necessary functions and types, you can start writing the handler functions that run when users make API requests to the server.<\/p>\n<h2><a id=\"post-101897-_ieekvloi65iv\"><\/a>The POST Request Handler Function<\/h2>\n<p>The <code>create_human<\/code> function is the POST request handler function. The <code>create_human<\/code> function will retrieve the JSON payload from the request and return a JSON payload containing a success message to the client.<\/p>\n<pre class=\"lang:c# theme:vs2012\">pub async fn create_human(new_human: web::Json&lt;NewHuman&gt;) -&gt; Result&lt;HttpResponse&gt; {\r\n    use crate::schema::human::dsl::*;\r\n    let mut connection = establish_connection();\r\n\t\t\r\n    diesel::insert_into(human)\r\n        .values(&amp;new_human.into_inner())\r\n        .execute(&amp;mut connection)\r\n        .expect(\"Error inserting new human\");\r\n    Ok(HttpResponse::Ok().json(\r\n                     \"data inserted into the database\"))\r\n}<\/pre>\n<p>The <code>create_human<\/code> handler function connects to the database with the <code>establish_connection<\/code> function, inserts the <code>new_human<\/code> data from the JSON payload inton the database with the <code>insert_into<\/code> function, and uses the <code>HttpResponse::Ok <\/code>function to return a <code>JSON<\/code> payload after a successful request.<\/p>\n<p>Here\u2019s a <code>CURL<\/code> request that you can use to test the <code>create_human<\/code> handler function.<\/p>\n<pre class=\"lang:c# theme:vs2012\">curl -X POST http:\/\/127.0.0.1:8080\/humans -H \"Content-Type: \r\napplication\/json\" -d '{\"first_name\": \"John\", \"last_name\": \r\n\"Doe\", \"age\": 30}'<\/pre>\n<p>Here\u2019s the result of a successful data insertion operation on the database:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1522\" height=\"766\" class=\"wp-image-101899\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/03\/word-image-101897-2.png\" \/><\/p>\n<h2><a id=\"post-101897-_wskwdkx9dy3h\"><\/a>The GET Request Handler Function<\/h2>\n<p>The <code>get_humans<\/code> function is a <code>GET<\/code> request handler function that retrieves all the entries in the database and returns them to the client.<\/p>\n<pre class=\"lang:c# theme:vs2012\">\/\/ READ ALL\r\npub async fn get_humans() -&gt; Result&lt;HttpResponse&gt; {\r\n    use crate::schema::human::dsl::*;\r\n    let mut connection = establish_connection();\r\n    let humans = human\r\n        .load::&lt;Human&gt;(&amp;mut connection)\r\n        .expect(\"Error loading humans\");\r\n    Ok(HttpResponse::Ok().json(humans))\r\n}<\/pre>\n<p>After connecting to the database, the <code>get_humans<\/code> handler function uses the <code>load<\/code> function to load all the <code>Human<\/code> entries in the database.<\/p>\n<p>Here\u2019s a <code>CURL<\/code> request that makes a call to trigger the functionality of the <code>get_humans<\/code> handler function:<\/p>\n<pre class=\"lang:c# theme:vs2012\">curl http:\/\/127.0.0.1:8080\/humans<\/pre>\n<p>On sending a GET request to the API via CURL you should expect to retrieve the entries in the database as such:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1998\" height=\"662\" class=\"wp-image-101900\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/03\/word-image-101897-3.png\" \/><\/p>\n<h2><a id=\"post-101897-_seqo739silfu\"><\/a>The PUT Request Handler Function<\/h2>\n<p>The <code>update_human<\/code> function is an asynchronous function that takes the id and a JSON payload from the client\u2019s request and returns the data from the payload after updating the database entry.<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">\/\/ UPDATE\r\npub async fn update_human(\r\n    id: web::Path&lt;i32&gt;,\r\n    human_update: web::Json&lt;UpdateHuman&gt;,\r\n) -&gt; Result&lt;HttpResponse&gt; {\r\n    use crate::schema::human::dsl::*;\r\n    let mut connection = establish_connection();\r\n    \/\/ Use the `update` method of the Diesel ORM \r\n    \/\/to update the student's record\r\n    let updated_human = diesel::update(human.find(id))\r\n        .set(&amp;human_update.into_inner())\r\n        .execute(&amp;mut connection)\r\n        .expect(\"Failed to update student\");\r\n    Ok(HttpResponse::Ok().json(updated_human))\r\n}<\/pre>\n<p>The <code>update_human<\/code> handler function uses the <code>update<\/code> function to update the entry after confirming that the <code>id<\/code> exists with the <code>find<\/code> function.<\/p>\n<p>Here\u2019s a CURL request that sends a PUT request to the database and triggers the <code>update_human<\/code> function.<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">curl -X PUT http:\/\/127.0.0.1:8080\/humans\/{id} -H \r\n\"Content-Type: application\/json\" -d '{\"first_name\": \"Jane\", \r\n\"last_name\": \"Doe\", \"age\": 32}'<\/pre>\n<p>On sending the <code>CURL<\/code> request, here\u2019s the result of the update operation:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1618\" height=\"862\" class=\"wp-image-101901\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/03\/word-image-101897-4.png\" \/><\/p>\n<h2><a id=\"post-101897-_whkvo05xr27i\"><\/a>The DELETE Request Handler Function<\/h2>\n<p>The <code>DELETE<\/code> request handler function will retrieve the <code>id<\/code> from the request and delete the row with the <code>id<\/code> from the database.<\/p>\n<pre class=\"lang:c# theme:vs2012\">\/\/ DELETE\r\npub async fn delete_human(id: web::Path&lt;i32&gt;) -&gt; Result&lt;HttpResponse&gt; {\r\n    use crate::schema::human::dsl::*;\r\n    let mut connection = establish_connection();\r\n    diesel::delete(human.find(id))\r\n        .execute(&amp;mut connection)\r\n        .expect(&amp;format!(\"Unable to find student {:?}\", id));\r\n    Ok(HttpResponse::Ok().json(\"Deleted successfully\"))\r\n}<\/pre>\n<p>The <code>delete_human<\/code> handler function deletes the entry with the id from the database with Diesel\u2019s <code>delete<\/code> function and returns a string response to confirm the operation&#8217;s success.<\/p>\n<p>Here\u2019s a <code>CURL<\/code> request that seeks to delete the entry having the <code>id<\/code> field of 1 from the database:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">curl -X DELETE http:\/\/127.0.0.1:8080\/humans\/{id}<\/pre>\n<p>The result from a successful <code>DELETE<\/code> request to the database should have this form:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1858\" height=\"640\" class=\"wp-image-101902\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/03\/word-image-101897-5.png\" \/><\/p>\n<h2><a id=\"post-101897-_u9zacqkm28lq\"><\/a>Conclusion<\/h2>\n<p>This guide explored setting up a RESTful API in Rust using Actix, Diesel, and SQLite. You have learned to define CRUD operations and used <code>CURL<\/code> commands to demonstrate how the API responds to different requests. Rust&#8217;s flexibility, combined with Actix&#8217;s speed and Diesel&#8217;s versatility, makes this stack a powerful choice for backend development.<\/p>\n<p>As you progress with Rust, you&#8217;ll find many more libraries and tools in the Rust ecosystem that you can use to extend and improve your API. You can take the functionality of this API a step further by adding authentication and authorization mechanisms to secure your API and implement logging and error handling to make debugging easier.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Building a REST API in Rust using Actix-web as the web framework and Diesel as the ORM &#8211; defining routes and handlers, connecting to PostgreSQL with Diesel, running migrations, and implementing CRUD endpoints with JSON serialization via serde. Complete tutorial with project structure.&hellip;<\/p>\n","protected":false},"author":340771,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,147005],"tags":[159071],"coauthors":[147592],"class_list":["post-101897","post","type-post","status-publish","format-standard","hentry","category-featured","category-web","tag-rust"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/101897","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/340771"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=101897"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/101897\/revisions"}],"predecessor-version":[{"id":101906,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/101897\/revisions\/101906"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=101897"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=101897"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=101897"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=101897"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}