Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1,7 @@
+++
title = "Blog"
description = "Blog"
sort_by = "date"
paginate_by = 10
template = "blog/section.html"
+++
@@ -0,0 +1,92 @@
+++
title = "Creating Frontend Website Using Angular"
description = "Setting up a Loco app for serving an Angular clientside app is easy. Learn how to configure and set up a full-stack Angular app with Loco."
date = 2024-01-25T18:03:52+01:00
updated = 2024-01-25T18:03:52+01:00
draft = false
template = "blog/page.html"
[taxonomies]
authors = ["LimpidCrypto"]
+++
## Overview
1. Create new SaaS project
2. Edit `.devcontainer/Dockerfile`
3. Reopen the project in the Dev Container
4. Delete frontend directory
5. Generate new Angular frontend
6. Build frontend
7. Edit `config/development.yml`
8. Start Loco
## Create new SaaS project
1. Run `loco new` to create a new project
2. Navigate through the instructions until you reach the point where to decide what type of project to create
3. Select "SaaS app (with DB and user auth)"
## Edit ".devcontainer/Dockerfile"
1. Open `.devcontainer/Dockerfile`
2. Replace the content with the following:
```Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/rust:0-1
# Install postgresql-client and sea-orm-cli
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends postgresql-client \
&& cargo install sea-orm-cli \
&& chown -R vscode /usr/local/cargo
# Install Node.js and npm
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \
&& apt-get install -y nodejs
# Install Angular CLI
RUN npm install -g @angular/cli
COPY .env /.env
```
The Dockerfile will provide you with everything you need to develop a Loco app with an Angular frontend.
## Reopen the project in the Dev Container
With VSCode it is super easy to reopen and run the project in a Dev Container.
1. Press `Crtl + Shift + P`
2. Select `Dev Containers: Repopen in Container`
3. VSCode will open the project in the dev container. This can take a while when it is built for the first time.
4. Delete the existing `frontend` directory
Loco comes with a Vite React frontend. We can delete the whole directory because the Angular CLI will set up everything we need
## Generate new Angular frontend
1. From the project root execute `ng new frontend` to create a new Angular project
2. Navigate through the instructions
## Build frontend
1. Run `ng build` to build the Angular frontend
## Edit "config/development.yml"
As you may have noticed Angular has built the frontend into `frontend/dist/frontend/browser`. We now need to configure Loco to access the built frontend from there.
1. Open `config/development.yml`
2. Set the configs to the frontend build path:
a. `server.middlewares.static.folder.path: "frontend/dist/frontend/browser"`
b. `server.middlewares.static.fallback: "frontend/dist/frontend/browser/index.html"`
## Start Loco
1. Start Loco with `cargo loco start`
2. Open http://localhost:5150/
You should now see the Angular starter Website :smile:
@@ -0,0 +1,197 @@
+++
title = "Building a Rust App with Axum Session"
description = "Add sessions to your app with Axum Sessions. Configure a session provider, and set up Axum Session and Loco with simple app hooks."
date = 2023-12-19T09:19:42+00:00
updated = 2023-12-19T09:19:42+00:00
draft = false
template = "blog/page.html"
[taxonomies]
authors = ["Team Loco"]
+++
To build a Rust app with [Axum session](https://crates.io/crates/axum_session), the first step is to choose your server. In this case, we'll use [loco](https://loco.rs) :)
Start by creating a new project and selecting the `SaaS app` template:
```sh
$ cargo install loco
$ loco new
App name? · myapp
? What would you like to build?
lightweight-service (minimal, only controllers and views)
Rest API (with DB and user auth)
SaaS app (with DB and user auth)
```
## Creating Session Memory Store Only
First, add the Axum session crate to Cargo.toml:
```toml
axum_session = {version = "0.10.1", default-features = false}
```
Then, add an Axum session layer to your router. Open app.rs and add the following hook:
```rust
pub struct App;
#[async_trait]
impl Hooks for App {
fn app_name() -> &'static str {
env!("CARGO_CRATE_NAME")
}
// Other hooks...
async fn after_routes(router: AxumRouter, _ctx: &AppContext) -> Result<AxumRouter> {
let session_config =
axum_session::SessionConfig::default().with_table_name("sessions_table");
let session_store =
axum_session::SessionStore::<axum_session::SessionNullPool>::new(None, session_config)
.await
.unwrap();
let router = router.layer(axum_session::SessionLayer::new(session_store));
Ok(router)
}
// Other hooks...
}
```
Now, you can create your controller that uses Axum session. Use the `cargo loco generate controller` command:
```sh
cargo loco generate controller mysession --api
Finished dev [unoptimized + debuginfo] target(s) in 0.36s
Running `target/debug/axum-session-cli generate controller mysession`
added: "src/controllers/mysession.rs"
injected: "src/controllers/mod.rs"
injected: "src/app.rs"
added: "tests/requests/mysession.rs"
injected: "tests/requests/mod.rs"
```
Open the `src/controllers/mysession.rs` file created by the controller generator and replace its content with the following code:
```rust
#![allow(clippy::unused_async)]
use axum_session::{Session, SessionNullPool};
use loco_rs::prelude::*;
pub async fn get_session(session: Session<SessionNullPool>) -> Result<()> {
println!("{:#?}", session);
format::empty()
}
pub fn routes() -> Routes {
Routes::new().prefix("mysession").add("/", get(get_session))
}
```
Now, you can call the `http://127.0.0.1:5150/mysession` endpoint to see the session.
## Creating Session With DB Encryption
To add session DB encryption, include the Axum session crate and PostgreSQL with SQLx in Cargo.toml:
```toml
axum_session = {version = "0.10.1"}
sqlx = { version = "0.7.2", features = [
"macros",
"postgres",
"_unstable-all-types",
"tls-rustls",
"runtime-tokio",
] }
```
Create a `session.rs` file with the following content:
The `connect_to_database` getting an `Database` configuration and returns a PgPool instance that axum session expected.
```rust
use sqlx::postgres::PgPool;
use loco_rs::{
config::Database,
errors::Error,
Result,
};
async fn connect_to_database(config: &Database) -> Result<PgPool> {
PgPool::connect(&config.uri)
.await
.map_err(|e| Error::Any(e.into()))
}
```
Add the Axum session layer to your router in `app.rs`:
```rust
use session; // This is the session.rs file
pub struct App;
#[async_trait]
impl Hooks for App {
fn app_name() -> &'static str {
env!("CARGO_CRATE_NAME")
}
// Other hooks...
async fn after_routes(router: AxumRouter, ctx: &AppContext) -> Result<AxumRouter> {
let conn = session.connect_to_database(&ctx.config.database).await?;
let session_config = axum_session::SessionConfig::default()
.with_table_name("sessions_table")
.with_key(axum_session::Key::generate())
.with_database_key(axum_session::Key::generate())
.with_security_mode(axum_session::SecurityMode::PerSession);
let session_store = axum_session::SessionStore::<axum_session::SessionPgPool>::new(
Some(conn.clone().into()),
session_config,
)
.await
.unwrap();
let router = router.layer(axum_session::SessionLayer::new(session_store));
Ok(router)
}
// Other hooks...
}
```
Create the controller as before using `cargo loco generate controller`
```sh
cargo loco generate controller mysession --api
Finished dev [unoptimized + debuginfo] target(s) in 0.36s
Running `target/debug/axum-session-cli generate controller mysession`
added: "src/controllers/mysession.rs"
injected: "src/controllers/mod.rs"
injected: "src/app.rs"
added: "tests/requests/mysession.rs"
injected: "tests/requests/mod.rs"
```
and replace the content of `src/controllers/mysession.rs` with the provided code.
```rust
#![allow(clippy::unused_async)]
use axum_session::{Session, SessionPgPool};
use loco_rs::prelude::*;
pub async fn get_session(session: Session<SessionPgPool>) -> Result<()> {
println!("{:#?}", session);
format::empty()
}
pub fn routes() -> Routes {
Routes::new().prefix("mysession").add("/", get(get_session))
}
```
Now, calling the `http://127.0.0.1:5150/mysession` endpoint will display the session.
@@ -0,0 +1,559 @@
+++
title = "Deploying Rust App with Terraform on AWS Fargate"
description = "Learn how to deploy a Loco app with Terraform (IaC). Generate a deployment with Loco generators and set it up step-by-step."
date = 2023-12-20T16:04:40+00:00
updated = 2023-12-16T04:20:40+00:00
draft = false
template = "blog/page.html"
[taxonomies]
authors = ["Antonio Souza"]
+++
In today's rapidly evolving technological landscape, Infrastructure as Code (IaC) has become a cornerstone for efficient, scalable, and maintainable cloud infrastructure deployment. IaC involves managing and provisioning computing infrastructure through machine-readable script files, rather than through physical hardware configuration or interactive configuration tools. This allows for the automation of infrastructure deployment and management, which in turn reduces the risk of human error and increases the speed of deployment.
In this article, we will explore how to deploy a Rust app built with [loco](https://loco.rs) on AWS Fargate using Terraform. We will start by creating a new project and selecting the `Rest API` template:
````sh
```sh
$ cargo install loco
$ loco new
App name? · myapp
? What would you like to build?
lightweight-service (minimal, only controllers and views)
Rest API (with DB and user auth)
SaaS app (with DB and user auth)
````
## Prerequisites
To deploy our app on AWS Fargate, we will need to have the following tools installed:
- [Docker](https://docs.docker.com/get-docker/) - Docker is a containerization platform that allows you to package your application and all of its dependencies into a standardized unit for software development.
- [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli) - Terraform is an open-source infrastructure as code software tool that enables you to safely and predictably create, change, and improve infrastructure.
- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) - The AWS Command Line Interface (CLI) is a unified tool to manage your AWS services.
## Creating the Docker Image
To create the Docker image for our app, we will use the loco CLI. The `cargo loco generate deployment` command will create a Docker image for our app. It will also create a `Dockerfile` for us, which we can use to build the image.
```sh
$ cargo loco generate deployment
? Choose your deployment
Docker
added: "Dockerfile"
added: ".dockerignore"
```
Now, we can build the Docker image which will be used to deploy our app on AWS Fargate.
```sh
$ docker build -t myapp .
[+] Building 237.1s (16/16) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring Dockerfile: 331B 0.0s
...
=> => writing image sha256:07416ca8195e4026ab65bc567f990ea83141aa10890f8443deb8f54a8bae7f0a 0.0s
=> => naming to docker.io/library/myapp
```
## Setting up AWS
To deploy our app on AWS Fargate, we will need to create an AWS account and set up the AWS CLI. You can create an AWS account [here](https://portal.aws.amazon.com/billing/signup#/start/email).
You will also need to install the AWS CLI. You can find instructions on how to do this [here](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html).
Finally, you need to create an IAM user to use with the AWS CLI. You can find instructions on how to do this [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html).
Now, we can configure the AWS CLI with the credentials of the IAM user we just created.
```sh
$ aws configure
AWS Access Key ID [None]: <your access key id>
AWS Secret Access Key [None]: <your secret access key>
Default region name [None]: <your region>
Default output format [None]: json
```
## Creating the repository on ECR
To deploy our app on AWS Fargate, we will need to create a repository on ECR. You can do this by running the following command:
```sh
$ aws ecr create-repository --repository-name myapp
{
"repository": {
"repositoryArn": "arn:aws:ecr:us-east-1:123456789012:repository/myapp",
"registryId": "123456789012",
"repositoryName": "myapp",
"repositoryUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp",
"createdAt": 1627981234.0,
"imageTagMutability": "MUTABLE",
"imageScanningConfiguration": {
"scanOnPush": false
}
}
}
```
## Pushing the Docker image to ECR
Now, we can push the Docker image to ECR. You can do this by running the following commands:
-1. Log in to ECR
```sh
$ aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
```
-2. Tag the Docker image
```sh
$ docker tag myapp:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
```
-3. Push the Docker image to ECR
```sh
$ docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
```
## Creating the main.tf file for Terraform
This is the main Terraform file that will be used to deploy our app on AWS Fargate. It will create the following resources:
```hcl
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
archive = {
source = "hashicorp/archive"
version = "~> 2.2.0"
}
}
required_version = "~> 1.0"
}
# Configure the AWS Provider
provider "aws" {
region = "us-east-1" // Change this to your region
access_key = "<your access key>" // Change this to your access key
secret_key = "your secret key" // Change this to your secret key
}
resource "aws_ecr_repository" "myapp" {
name = "myapp"
}
resource "aws_ecs_cluster" "myapp_cluster" {
name = "myapp_cluster"
}
resource "aws_cloudwatch_log_group" "myapp" {
name = "/ecs/myapp"
}
resource "aws_ecs_task_definition" "myapp_task" {
family = "myapp-task"
container_definitions = <<DEFINITION
[
{
"name": "myapp-task",
"image": "${aws_ecr_repository.myapp.repository_url}",
"essential": true,
"portMappings": [
{
"containerPort": 5150
}
],
"command": ["start"],
"memory": 512,
"cpu": 256,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-region": "us-east-2",
"awslogs-group": "/ecs/myapp",
"awslogs-stream-prefix": "ecs"
}
}
}
]
DEFINITION
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
memory = 512
cpu = 256
execution_role_arn = aws_iam_role.ecsTaskExecutionRole.arn
}
resource "aws_iam_role" "ecsTaskExecutionRole" {
name = "ecsTaskExecutionRoleMyapp"
assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
}
data "aws_iam_policy_document" "assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role_policy_attachment" "ecsTaskExecutionRole_policy" {
role = aws_iam_role.ecsTaskExecutionRole.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
resource "aws_alb" "myapp" {
name = "myapp-lb"
internal = false
load_balancer_type = "application"
enable_deletion_protection = true
subnets = [
aws_subnet.public_d.id,
aws_subnet.public_e.id,
]
security_groups = [
aws_security_group.http.id,
aws_security_group.https.id,
aws_security_group.egress_all.id,
]
depends_on = [aws_internet_gateway.igw]
}
resource "aws_security_group" "load_balancer_security_group" {
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_lb_target_group" "myapp" {
name = "myapp-tg"
port = 5150
protocol = "HTTP"
target_type = "ip"
vpc_id = aws_vpc.myapp_vpc.id
health_check {
enabled = true
path = "/_health"
matcher = "200,202"
}
depends_on = [aws_alb.myapp]
}
resource "aws_alb_listener" "myapp_http" {
load_balancer_arn = aws_alb.myapp.arn
port = "80"
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
resource "aws_alb_listener" "myapp_https" {
load_balancer_arn = aws_alb.myapp.arn
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = "<your arn for the certificate>" // Change this to your certificate ARN
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.myapp.arn
}
}
output "alb_url" {
value = "https://${aws_alb.myapp.dns_name}"
}
resource "aws_ecs_service" "myapp" {
name = "myapp-service"
cluster = aws_ecs_cluster.myapp_cluster.id
task_definition = aws_ecs_task_definition.myapp_task.arn
launch_type = "FARGATE"
desired_count = 1
load_balancer {
target_group_arn = aws_lb_target_group.myapp.arn
container_name = aws_ecs_task_definition.myapp_task.family
container_port = 5150
}
network_configuration {
assign_public_ip = false
security_groups = [
aws_security_group.egress_all.id,
aws_security_group.ingress_api.id,
]
subnets = [
aws_subnet.private_d.id,
aws_subnet.private_e.id,
]
}
}
resource "aws_security_group" "service_security_group" {
ingress {
from_port = 0
to_port = 0
protocol = "-1"
security_groups = ["${aws_security_group.load_balancer_security_group.id}"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
```
This file will create the following resources:
- An ECR repository for our app
- An ECS cluster for our app
- An ECS task definition for our app
- An ECS service for our app
Now, we need to create a `network.tf` file to define the network configuration for our app. This file will create the following resources:
```hcl
resource "aws_vpc" "myapp_vpc" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public_d" {
vpc_id = aws_vpc.myapp_vpc.id
cidr_block = "10.0.1.0/25"
availability_zone = "us-east-2a"
tags = {
"Name" = "public | us-east-2a"
}
}
resource "aws_subnet" "private_d" {
vpc_id = aws_vpc.myapp_vpc.id
cidr_block = "10.0.2.0/25"
availability_zone = "us-east-2b"
tags = {
"Name" = "private | us-east-2b"
}
}
resource "aws_subnet" "public_e" {
vpc_id = aws_vpc.myapp_vpc.id
cidr_block = "10.0.1.128/25"
availability_zone = "us-east-2c"
tags = {
"Name" = "public | us-east-2c"
}
}
resource "aws_subnet" "private_e" {
vpc_id = aws_vpc.myapp_vpc.id
cidr_block = "10.0.2.128/25"
availability_zone = "us-east-2c"
tags = {
"Name" = "private | us-east-2c"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.myapp_vpc.id
tags = {
"Name" = "public"
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.myapp_vpc.id
tags = {
"Name" = "private"
}
}
resource "aws_route_table_association" "public_d_subnet" {
subnet_id = aws_subnet.public_d.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private_d_subnet" {
subnet_id = aws_subnet.private_d.id
route_table_id = aws_route_table.private.id
}
resource "aws_route_table_association" "public_e_subnet" {
subnet_id = aws_subnet.public_e.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private_e_subnet" {
subnet_id = aws_subnet.private_e.id
route_table_id = aws_route_table.private.id
}
resource "aws_eip" "nat" {
vpc = true
}
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.myapp_vpc.id
}
resource "aws_nat_gateway" "ngw" {
subnet_id = aws_subnet.public_d.id
allocation_id = aws_eip.nat.id
depends_on = [aws_internet_gateway.igw]
}
resource "aws_route" "public_igw" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
resource "aws_route" "private_ngw" {
route_table_id = aws_route_table.private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.ngw.id
}
resource "aws_security_group" "http" {
name = "http"
description = "HTTP traffic"
vpc_id = aws_vpc.myapp_vpc.id
ingress {
from_port = 80
to_port = 80
protocol = "TCP"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "https" {
name = "https"
description = "HTTPS traffic"
vpc_id = aws_vpc.myapp_vpc.id
ingress {
from_port = 443
to_port = 443
protocol = "TCP"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "egress_all" {
name = "egress-all"
description = "Allow outbound traffic"
vpc_id = aws_vpc.myapp_vpc.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "ingress_api" {
name = "ingress-api"
description = "Allow ingress to App"
vpc_id = aws_vpc.myapp_vpc.id
ingress {
from_port = 5150
to_port = 5150
protocol = "TCP"
cidr_blocks = ["0.0.0.0/0"]
}
}
```
The network configuration will be responsible for creating all the infrastructure needed to deploy our app on AWS Fargate in terms of networking. I recommend you to read the [AWS Fargate documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html) to understand how it works, also you can read the Terraform documentation for [AWS Fargate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) and [AWS VPC](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc).
So, now we have the main Terraform file and the network configuration file for our app. We can now deploy our app on AWS Fargate.
## Deploying the app on AWS Fargate
To deploy our app on AWS Fargate, we will need to run the following commands:
-1. Initialize Terraform
```sh
$ terraform init
```
-2. Plan the deployment
```sh
$ terraform plan
```
-3. Apply the deployment
````sh
$ terraform apply
```****
Theses commands will create all the resources we need to deploy our app on AWS Fargate. After running you will see the url from our alb_url output.
```sh
Apply complete! Resources: 20 added, 0 changed, 0 destroyed.
Outputs:
alb_url = https://myapp-lb-1234567890.us-east-2.elb.amazonaws.com
````
Now, we can access our app by going to the url from our alb_url output.
## Conclusion
In this article, we explored how to deploy a Rust app built with loco on AWS Fargate using Terraform. We started by creating a new project and selecting the `Rest API` template. Then, we created the Docker image for our app and pushed it to ECR. Finally, we created the main Terraform file and the network configuration file for our app and deployed it on AWS Fargate.
This approach allows us to deploy our app on AWS Fargate in a fast and reliable way. It also allows us to easily scale our app by adding more instances of it.
@@ -0,0 +1,216 @@
+++
title = "Creating Frontend Website"
description = "Build a REST API quickly with Loco and then follow by building a React frontend app to use it. Learn about generators, configuring asset serving and client-side apps with Loco."
date = 2023-12-14T09:19:42+00:00
updated = 2023-12-14T09:19:42+00:00
draft = false
template = "blog/page.html"
[taxonomies]
authors = ["Team Loco"]
+++
## Overview
This guide provides a comprehensive walkthrough on using `Loco` to build a Todo list application with a REST API and a React frontend. The steps outlined cover everything from project creation to deployment.
Explore the example repository [here](https://github.com/loco-rs/todo-list-example)
The key steps include:
- Creating a Loco project with the SaaS starter
- Setting up a Vite frontend with React
- Configuring Loco to serve frontend static assets
- Implementing the Notes model/controller in the REST API
- Reloading the server and frontend during development
- Deploying the website to production
## Selecting SaaS Starter
To begin, run the following command to create a new Loco app using the SaaS starter:
```sh
& loco new
App name? · todolist
What would you like to build? · SaaS app (with DB and user auth)
🚂 Loco app generated successfully in:
/tmp/todolist
```
Follow the prompts to specify the app name (e.g., todolist) and choose the SaaS app option.
After generating the app, ensure you have the necessary resources by running:
```
$ cd todolist
$ cargo loco doctor
✅ SeaORM CLI is installed
✅ DB connection: success
✅ Redis connection: success
```
Verify that SeaORM CLI is installed, and the database and Redis connections are successful. If any resources fail, refer to the [quick tour guide](@/docs/tutorials/your-first-app.md) for troubleshooting.
Once `cargo loco doctor` shows all checks passed, start the server:
```
$ cargo loco start
Updating crates.io index
.
.
.
▄ ▀
▀ ▄
▄ ▀ ▄ ▄ ▄▀
▄ ▀▄▄
▄ ▀ ▀ ▀▄▀█▄
▀█▄
▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▀▀█
██████ █████ ███ █████ ███ █████ ███ ▀█
██████ █████ ███ █████ ▀▀▀ █████ ███ ▄█▄
██████ █████ ███ █████ █████ ███ ████▄
██████ █████ ███ █████ ▄▄▄ █████ ███ █████
██████ █████ ███ ████ ███ █████ ███ ████▀
▀▀▀██▄ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ██▀
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
https://loco.rs
environment: development
database: automigrate
logger: debug
modes: server
listening on port 5150
```
## Creating the Frontend
For the frontend, we'll use [Vite](https://vitejs.dev/guide/) with React. In the `todolist` folder, run:
```sh
$ npm create vite@latest
Need to install the following packages:
create-vite@5.1.0
Ok to proceed? (y) y
✔ Project name: … frontend
✔ Select a framework: React
✔ Select a variant: JavaScript
```
Follow the prompts to set up the `frontend` as a project name.
Navigate to the frontend folder and install dependencies:
```
$ cd todolist/frontend
$ pnpm install
```
Start the development server:
```sh
$ pnpm dev
```
### Serving Static Assets in Loco
First, move all our rest api endpoint under `/api` prefix. for doing it go to `src/app.rs`. in `routes` hooks function add `.prefix("/api")` to the default routes.
```rust
fn routes() -> AppRoutes {
AppRoutes::with_default_routes()
.prefix("/api")
.add_route(controllers::notes::routes())
}
```
Build the frontend for production:
```sh
pnpm build
```
In the `frontend` folder, a `dist` directory is created. Update the `config/development.yaml` file in the main folder to include a static middleware:
```yaml
server:
middlewares:
static:
enable: true
must_exist: true
folder:
uri: "/"
path: "frontend/dist"
fallback: "frontend/dist/index.html"
```
Now, run the Loco server again and you should see frontend app serving via Loco
```sh
$ cargo loco start
```
If you see the default fallback page, you have to disable the fallback middleware. The default fallback takes priority over the static handler, so no static content will be served if it is enabled. You can disable it like so:
```yaml
server:
middlewares:
fallback:
enable: false
static:
...
```
# Developing the UI
Install `react-router-dom`, `react-query` and `axios`
```sh
$ pnpm install react-router-dom react-query axios
```
1. Copy [main.jsx](https://github.com/loco-rs/todo-list-example/blob/main/frontend/src/main.jsx) to frontend/src/main.jsx.
2. Copy [App.jsx](https://github.com/loco-rs/todo-list-example/blob/main/frontend/src/App.jsx) to frontend/src/App.jsx.
3. Copy [App.css](https://github.com/loco-rs/todo-list-example/blob/main/frontend/src/App.css) to frontend/src/App.css.
Now, run the server `cargo loco start` and the UI pnpm dev in the frontend folder, and start adding your todo list!
## Improve Development
use [cargo-watch](https://crates.io/crates/cargo-watch) for hot reloading the server:
```sh
$ cargo watch --ignore "frontend" -x check -s 'cargo run start'
```
Now, any changes in your Rust code will automatically reload the server, and any changes in your frontend Vite will reload the frontend app.
## Deploy To Production
In the `frontend` folder, run `pnpm build`. After a successful build, go to the Loco server and run `cargo loco start`. Loco will serve the frontend static files directly from the server.
### Prepare Docker Image
Run `cargo loco generate deployment` and select Docker as the deployment type:
```sh
$ cargo loco generate deployment
Choose your deployment · Docker
added: "Dockerfile"
added: ".dockerignore"
```
Loco will add a `Dockerfile` and a `.dockerignore `file. Note that Loco detect the static assent and included them as part of the image
Build the container:
```sh
$ docker build . -t loco-todo-list
```
Now run the container:
```sh
$ docker run -e LOCO_ENV=production -p 5150:5150 loco-todo-list start
```
@@ -0,0 +1,94 @@
+++
title = "What if Rails was Built on Rust?"
description = "Introducing Loco: a Rails-inspired Rust web framework. See how Rust can be as expressive as Ruby and how we can build a good deal of magic that Rails has with Rust."
date = 2023-11-24T09:19:42+00:00
updated = 2023-11-24T09:19:42+00:00
draft = false
template = "blog/page.html"
[taxonomies]
authors = ["Team Loco"]
+++
<center>
<img width="150" src="/icon.svg"/>
**What if [Rails](https://rubyonrails.org) was built on Rust and not Ruby?**
</center>
Then it would look like this:
```rust
async fn current(
auth: middleware::auth::Auth,
State(ctx): State<AppContext>,
) -> Result<Response> {
let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?;
format::json(CurrentResponse::new(&user))
}
pub fn routes() -> Routes {
Routes::new().prefix("user").add("/current", get(current))
}
```
## Introducing: Loco
Loco is a Rails inspired web framework for Rust. It inlcudes _almost every Rails feature_ with best-effort Rust ergonomics:
* Controllers and routing via [axum](https://github.com/tokio-rs/axum)
* Models, migration, and ActiveRecord via [SeaORM](https://www.sea-ql.org/SeaORM/)
* Views via [serde](https://serde.rs/json.html)
* Seamless, Background jobs, multi modal: in process, out of process, async via Tokio
* Mailers
* Tasks
* Seeding
* Environment-aware configuration
* Tracing, logging, seamlessly integrated via [tracing](https://docs.rs/tracing)
* Generators via [rrgen](https://github.com/jondot/rrgen)
* Batteries-included authentication (like Rails' `devise`)
* Testing kit, with automatic truncation, fixture seeding, auto migration, snapshotting and redaction
It's full stack for real.
## Why not Rails?
If you're happy with Ruby, use Rails. Don't spend time looking elsewhere because of performance -- Rails and Ruby are good enough.
**But if you love Rust**, you can now build companies like Rubyists have been building for ages -- use Loco.
* You'll get **Rust's safety, strong typing, fantastic concurrency models, and super super stable libraries and ecosystem**. Build once, then forget about it.
* Deployment is copying a **single binary** over to a server.
* You'll be getting **an order of 100,000 requests/sec** without any effort. And 50k requests/sec with database calls. You will never need more than a couple servers. Heck, you can deploy on a Rasberry Pi and be happy..
## The One Person Framework
Inspired by [DHH's approach](https://world.hey.com/dhh/the-one-person-framework-711e6318), Loco's guiding principle is above all:
> The one person framework
From this single guiding principles comes everything else.
For example, one person team, or one person company:
* Has **no time to debate libraries**, tooling, linting rules: strong opinions are welcome. Tell me how I should work.
* **Needs a driving tool** in addition to their brainpower -- that's the Loco CLI. Generate code, operate your project.
* **Needs stability**, anything that breaks is a waste of time, any surprise is a waste of time
* **Needs simplicity** -- don't surprise me
* **Needs a single operability story**. Deploys should be simple. No Kubernetes, no IAC, no preconditions.
* **Needs control**. Send emails and author the emails locally, not on some remote service
* **Needs locality**. Everything that happens in production should first happen in development and locally
* **Needs ad-hocness**. No holy grail ceremonies. Build tasks to run birthday emails to your users, rather than go on a crusade for an "Admin" project.
Loco is the one person framework for **indy hackers, hobbyists, and startups**.
With around **20mb of a deploy binary, and 50k requests/sec** - all you need is a single small/medium server, Postgres or Sqlite and an internet connection. Startups should be cheap!
Get started with [Loco](https://loco.rs) today!