A framework-agnostic, ergonomic, and type-safe implementation of the JSON:API 1.1 specification for Rust applications.
- β Compliant & Modern: Fully compliant with the JSON:API 1.1 specification.
- π Framework-Agnostic: Designed to work seamlessly with any web framework, including Axum, Actix Web, and Rocket.
- π‘οΈ Type-Safe by Design: Leverages Rust's type system to prevent common errors at compile time. Application-specific models are used directly, reducing the need for manual and error-prone conversions.
- β¨ Ergonomic API: A fluent, builder-based API for constructing responses and a powerful extractor for handling requests makes your code cleaner and more readable.
- π¦ Lightweight & Efficient: A minimal dependency footprint and an architecture focused on performance ensure your application remains fast.
- π§ Extensible: Easily extendable to fit your application's specific needs.
- π Comprehensive Documentation: Clear and detailed documentation with practical examples.
Add this to your Cargo.toml:
[dependencies]
rjapi = "0.0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"To make your models compatible with rjapi, implement the JsonApiResource trait.
use rjapi::JsonApiResource;
use serde::Serialize;
use serde_json::json;
#[derive(Serialize)]
struct Post {
id: String,
title: String,
content: String,
}
impl JsonApiResource for Post {
const RESOURCE_TYPE: &'static str = "posts";
fn id(&self) -> String {
self.id.clone()
}
fn attributes(&self) -> serde_json::Value {
json!({
"title": self.title,
"content": self.content,
})
}
}Use the JsonApiResponse builder to construct type-safe, compliant JSON:API responses.
use rjapi::{JsonApiResponse, Resource};
# use rjapi::JsonApiResource;
# use serde::Serialize;
# use serde_json::json;
# #[derive(Serialize)]
# struct Post {
# id: String,
# title: String,
# content: String,
# }
# impl JsonApiResource for Post {
# const RESOURCE_TYPE: &'static str = "posts";
# fn id(&self) -> String { self.id.clone() }
# fn attributes(&self) -> serde_json::Value { json!({ "title": self.title, "content": self.content }) }
# }
let post = Post {
id: "1".to_string(),
title: "Hello, World!".to_string(),
content: "This is the first post.".to_string(),
};
let resource = Resource {
resource_type: Post::RESOURCE_TYPE.to_string(),
id: post.id(),
attributes: Some(post.attributes()),
relationships: None,
links: None,
meta: None,
};
let response = JsonApiResponse::new(resource).build();
println!("{}", serde_json::to_string_pretty(&response).unwrap());The JsonApiRequest extractor simplifies request handling in your web framework.
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
Json, Router,
};
use rjapi::{JsonApiResponse, JsonApiResource, Resource, JsonApiRequest};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
// Application state
#[derive(Clone)]
struct AppState {
posts: Arc<RwLock<Vec<Post>>>,
}
# #[derive(Clone, Serialize, Deserialize)]
# struct Post {
# id: String,
# title: String,
# content: String,
# }
# impl JsonApiResource for Post {
# const RESOURCE_TYPE: &'static str = "posts";
# fn id(&self) -> String { self.id.clone() }
# fn attributes(&self) -> serde_json::Value { serde_json::json!({ "title": self.title, "content": self.content }) }
# }
// A specific type for post creation attributes
#[derive(Deserialize)]
struct PostAttributes {
title: String,
content: String,
}
// Axum handler for creating a post
async fn create_post(
State(state): State<AppState>,
Json(request): Json<JsonApiRequest<PostAttributes>>,
) -> impl IntoResponse {
let attributes = request.data.attributes;
let post = Post {
id: uuid::Uuid::new_v4().to_string(),
title: attributes.title,
content: attributes.content,
};
state.posts.write().await.push(post.clone());
let resource = Resource {
resource_type: Post::RESOURCE_TYPE.to_string(),
id: post.id(),
attributes: Some(serde_json::to_value(post).unwrap()),
..Default::default()
};
let response = JsonApiResponse::new(resource).build();
(StatusCode::CREATED, Json(response))
}For full documentation, visit docs.rs/rjapi.
Check out the examples directory for more detailed usage examples:
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please read our Contributing Guide for details on how to submit pull requests, report issues, and suggest improvements.
- Framework-agnostic JSON:API 1.1 implementation
- Support for resources, relationships, errors, and metadata
- Middleware for content-type validation
- Comprehensive examples and documentation