Creating web applications can be an exciting process. It involves crafting custom codes for your web server, testing it with simple cURL commands, and developing a user interface that can make HTTP requests and receive responses from the server. Ultimately, the goal is to have a functional user interface (UI) for desktop or mobile use, as well as a reliable server.
Traditionally, JavaScript and PHP are the preferred languages for building web servers. These languages have established a reputation as the top choices over the years, and most popular web UI frameworks, such as ReactJS, NextJS, and Laravel, are compatible with them. Some of these frameworks can be integrated directly into Android and iOS apps. As a result, using languages like Rust can present certain challenges. However, there are notable frameworks like Yew and Tauri that work well with Rust.
Rust brings to the table memory safety without garbage collection and helps us create blazingly fast applications. I wanted a language that could be integrated seamlessly with other programs and execute them efficiently. Also, I am most comfortable using Rust. 🤷🏾♂️
In the context of web application systems, if we decide to build our web user interface directly in Rust, we would have to insert our HTML directly into the code. Given the example below using Actix as our web framework, we can have a typical server that returns an HTML view in response to a GET request from a browser to a route.
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(home))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
async fn home() -> actix_web::HttpResponse {
actix_web::HttpResponse::Ok()
.content_type(ContentType::html())
.body(include_str!("home.html"))
}
We could even take a step further and incorporate JavaScript by utilizing Yew, which uses WebAssembly.
As the project expands, the team grows, it gets more complicated and if care is not taken messy. Also, how do you integrate mobile interfaces into your project?
In the world of building systems, modularization is a crucial strategy to implement, and web applications are no different. Modularization involves breaking down a software system into distinct and independent modules, with each module representing a separate functional unit of the software. This approach provides a host of benefits, such as improved code readability, ease of maintenance, and enhanced scalability.
Considering this, we can modularize our project and completely detach our Rust web server. This would allow us to give our web server the ability to handle specific tasks. This leads us to the benefits of using GraphQL.
GraphQL, a product designed by Facebook, offers a powerful yet flexible query for APIs. It is a query language, not like SQL, as it does not communicate with a database. It is declarative, hence one can get the exact data it needs from the API.
Let's connect the dots. As we already have a Rust server that handles database queries and other tasks, we can integrate GraphQL to enable GraphQL queries. For this purpose, we'll utilize the Async-graphql crate. Here's an example of what it might resemble.
async fn stream_datas<'ctx>(
&self,
ctx: &Context<'ctx>,
id: String,
) -> Result, FieldError> {
let pool = ctx.data_opt::>().unwrap();
let data = get_streamdatas(pool, &id)
.await
.map_err(|e| {
FieldError::new(format!("No data found. More info - {e}"));
})
.unwrap();
Ok(data)
}
GraphQL schema is then attached as an app data with our Actix web server
async fn run(graphql_schema: GraphqlSchema) -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(home))
.app_data(graphql_schema.clone())
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
We can have literally any UI be it mobile or web via a JavaScript web framework such as NextJS make a query to our server that can handle GraphQL
query {
streamDatas(id: "123547hdjcnm"){
streamlink
platform
videoId
timestampz
description
}
}
Response:
"data": {
"streamDatas": [
{
"streamlink": "https://www.youtube.com/embed/watch?v=g10eoQQE2IA?autoplay=1",
"platform": "youtube",
"videoId": "g10eoQQE2IA",
"timestampz": "2023-09-05 12:17:58.222259",
"description": null
},
{
"streamlink": "https://www.youtube.com/embed/watch?v=rtC1vV1-MoY?autoplay=1",
"platform": "youtube",
"videoId": "rtC1vV1-MoY",
"timestampz": "2023-09-05 12:18:03.656647",
"description": null
}
]
},
Utilizing modular systems improves scalability. by enabling the addition of new features or functionalities as separate modules. This allows for the seamless expansion of your application without adding unnecessary complexity to existing modules. In addition, managing these modules is straightforward and hassle-free.
It also allows for flexibility in choosing the programming language best suited for the task.