GraphQL API with Rust: Step-by-Step Tutorial for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to GraphQL API in Rust

Modern web development requires flexible and high-performance APIs. GraphQL is a query language that allows clients to get exactly the data they need without redundancy. Rust, in turn, offers high performance and memory safety. In this tutorial, we will create a simple but full-featured GraphQL server in Rust using the async-graphql library. You will learn how to set up a project, define data types, write resolvers, and run the server. This article is intended for beginner developers familiar with the basics of Rust.



Project Setup and Dependency Installation

First, let's create a new Rust project using Cargo:

cargo new graphql-rust-tutorialcd graphql-rust-tutorial

Open the Cargo.toml file and add the following dependencies:

[dependencies]async-graphql = "7.0"async-graphql-actix-web = "7.0"actix-web = "4"serde = { version = "1.0", features = ["derive"] }tokio = { version = "1", features = ["full"] }

Explanation of library choices:

  • async-graphql — the main library for working with GraphQL in Rust.
  • async-graphql-actix-web — integration with the Actix-web web framework.
  • actix-web — a popular asynchronous HTTP server.
  • serde — for data serialization/deserialization.
  • tokio — asynchronous runtime.

Run cargo build to ensure all dependencies are installed correctly.



Defining the GraphQL Schema and Resolvers

Let's create a simple data model — a book. We'll define the structure and resolvers for queries and mutations.

Creating the Data Model

Create a file src/models.rs and add the following code:

use async_graphql::*;

#[derive(SimpleObject)]pub struct Book { pub id: ID, pub title: String, pub author: String,}

#[derive(InputObject)]pub struct NewBook { pub title: String, pub author: String,}

Here, SimpleObject automatically generates a GraphQL type for the Book struct, and InputObject does the same for mutation input data.



Creating Storage and Resolvers

Let's create a file src/resolvers.rs that will contain the query and mutation logic:

use std::sync::Mutex;use async_graphql::*;use crate::models::{Book, NewBook};

pub struct QueryRoot;

#[Object]impl QueryRoot { async fn books(&self) -> Vec<Book> { let storage = STORAGE.lock().unwrap(); storage.clone() }

async fn book(&self, id: ID) -> Option<Book> { let storage = STORAGE.lock().unwrap(); storage.iter().find(|b| b.id == id).cloned() }}

pub struct MutationRoot;

#[Object]impl MutationRoot { async fn add_book(&self, input: NewBook) -> Book { let mut storage = STORAGE.lock().unwrap(); let new_book = Book { id: ID::from(format!("book_{}", storage.len() + 1)), title: input.title, author: input.author, }; storage.push(new_book.clone()); new_book }}

lazy_static::lazy_static! { static ref STORAGE: Mutex<Vec<Book>> = Mutex::new(vec![]);}

Note: we use Mutex for thread-safe access to storage. In a real project, it's better to use a database.



Building the Schema

Now let's combine everything in the src/main.rs file:

use actix_web::{web, App, HttpServer, HttpResponse, middleware};use async_graphql::{http::GraphiQLSource, EmptySubscription, Schema};use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};use std::sync::Arc;

mod models;mod resol

Blogs

Book Recommendations