Skip to main content
Lesson 2 of 2Advanced⏱ 45 mins

Project: Blog API

A complete CRUD API.

50% of path

Learning Objectives

  • Build a CRUD app

##Project: Blog API

Building a Blog API is the perfect capstone project to solidify your Actix Web knowledge. A Blog API involves CRUD (Create, Read, Update, Delete) operations, database integration, data validation, and potentially authentication.

#API Design

A standard Blog API might look like this:

  • GET /posts - List all posts
  • POST /posts - Create a new post
  • GET /posts/{id} - Get a specific post
  • PUT /posts/{id} - Update a post
  • DELETE /posts/{id} - Delete a post

#Defining the Models

You'll need models for your database entities and DTOs (Data Transfer Objects) for your API requests/responses.

code_blocksrust
use serde::{Serialize, Deserialize};

// What we return to the client
#[derive(Serialize)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}

// What the client sends to create a post
#[derive(Deserialize)]
pub struct CreatePostReq {
    pub title: String,
    pub body: String,
}

#Implementing CRUD Handlers

Here is an example of the create_post handler using SQLx:

code_blocksrust
use actix_web::{post, web, HttpResponse, Responder};
use sqlx::PgPool;

#[post("/posts")]
async fn create_post(
    req: web::Json<CreatePostReq>,
    db: web::Data<PgPool>
) -> impl Responder {
    let result = sqlx::query_as!(
        Post,
        "INSERT INTO posts (title, body, published) VALUES ($1, $2, false) RETURNING id, title, body, published",
        req.title,
        req.body
    )
    .fetch_one(db.get_ref())
    .await;

    match result {
        Ok(post) => HttpResponse::Created().json(post),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

#Structuring the Application

As your API grows, you shouldn't put all handlers in main.rs. A common pattern is to split the app into modules:

code_blockstext
src/
  main.rs      # Setup and HttpServer
  models.rs    # Structs and DTOs
  handlers.rs  # Actix Web routes
  db.rs        # Database query logic

You can then use web::scope to group routes together:

code_blocksrust
// in main.rs
App::new()
    .app_data(db_pool.clone())
    .service(
        web::scope("/api/v1")
            .service(handlers::get_posts)
            .service(handlers::create_post)
    )

This project prepares you for building scalable, real-world web services in Rust!

Practical Exercise

Implement the Blog API

Task

Requirements

    Try solving the exercise in your local environment first. Once you have a solution, or if you get completely stuck, reveal the answer.

    Key Takeaways

    • Design clear RESTful routes (`GET`, `POST`, `PUT`, `DELETE`).
    • Separate your database entities from your request DTOs (`Deserialize`).
    • Use `web::scope` to group endpoints under a common prefix like `/api/v1`.
    • Move handlers and models into separate files (`handlers.rs`, `models.rs`) for maintainability.

    Finished reading?

    Mark this lesson as done to track your progress and earn XP.