Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)
Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)

🔧 Programmierung 🕛 vor 3 Jahren 15 Min Lesezeit
0

Full-stack authentication system using rust (actix-web) and sveltekit

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht




Introduction



Howdy guys! It's been a while here. I have been learning some rust while I was away and I will be sharing some of the things I've learned.



An authentication system is an integral part of modern applications. It's so important that almost all modern applications have some sort of it. Because of their critical nature, such systems should be secure and should follow and some awesome crates for the backend service. , or any other rust's web framework for the backend and or any other javascript framework for the frontend. You can even use rust's or some templating engines such as at the frontend. It's entirely up to you. Our focus will be more on the concepts.




NOTE: We'll be utilizing the book, - or, more securely and appropriately, , email verification, token generation and destruction, some templating and a host of others.






Technology stack



For emphasis, our tech stack comprises:




  • Backend - Some crates that will be used are:




    • - An asynchronous runtime for Rust


    • - Templating engine


    • - Database


    • - Main frontend framework


    • - Styles

    • HTML5 - Structure





Assumption



A simple prerequisite to follow along is some familiarity with the Rust Programming language — like some understanding of structs, ownership model, borrow checker, module system, and co. — JavaScript (Typescript) and CSS. You do not need to be an expert — I ain't one in any of the technologies.






Source code



The source code for this series is hosted on /








Initial project structure




You can get this full starter template from . I have fallen in love with the structure and will most likely be using it for most of my Rust web project irrespective of the framework of choice. This starter template is available . It's fantastic!!! Currently, the backend structure looks like this:




CODE
├── Cargo.lock
├── Cargo.toml
├── settings
│   ├── base.yaml
│   ├── development.yaml
│   └── production.yaml
├── src
│   ├── lib.rs
│   ├── main.rs
│   ├── routes
│   │   ├── health.rs
│   │   └── mod.rs
│   ├── settings.rs
│   ├── startup.rs
│   └── telemetry.rs
└── tests









Step 1: Create a new project and install some dependencies



Create a directory that will house the entire (both frontend and backend) application. I called mine rust-auth. Change the directory into the newly created folder and issue the following command in your terminal:




CODE
~/rust-auth$ cargo new backend






This creates a new project called backend with Cargo.toml, Cargo.lock, and src/main.rs files created. Open it up in your editor of choice. Make your Cargo.toml file look like this:




CODE
# Cargo.toml

[package]
name = "backend"
version = "0.1.0"
authors = ["Your name <your email>"]
edition = "2021"

[lib]
path = "src/lib.rs"

[[bin]]
path = "src/main.rs"
name = "backend"

[dependencies]
actix-web = "4"
config = { version = "0.13.3", features = ["yaml"] }
dotenv = "0.15.0"
serde = "1.0.160"
tokio = { version = "1.27.0", features = ["macros", "rt-multi-thread"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = [
"fmt",
"std",
"env-filter",
"registry",
'json',
'tracing-log',
] }






We added authors to the [package] segment. Then we created a new segment, [lib], which points to the path of the project's lib.rs file. A project can have only one lib.rs file. Next is the binary segment, [[bin]]. Double square brackets in .toml files mean an array. It was used because we can have more than one binary package in a Rust project. These two new segments make it easier for us to write seamlessly integrated testing where tests are "independent" of the web framework used. Then, the [dependencies] section. We registered the preliminary crates we will be using. config helps with the easy transformation of .yaml or .json files containing some app-wide settings, like the variables in Django's settings.py file, into rust's structs. dotenv loads environment variables from a .env file. serde is rust's generic serialization/deserialization framework. tokio is an industry-standard runtime for writing reliable, asynchronous, and slim applications with rust. At runtime or when our application is in production, we ultimately need to log requests and responses. Sometimes, our users make some complaints or our app crashes. We cannot just figure out why certain situation occurs out of thin air. We need a point of reference to debug our application. In the rust ecosystem, tracing and its extension tracing-subscriber is widely used for this. Telemetry is what it's called.






Step 2: Build out the project's skeleton



Inside the src folder, issue the following commands to create some files and folders:




CODE
~/rust-auth/backend$ touch src/lib.rs src/startup.rs src/settings.rs src/telemetry.rs

~/rust-auth/backend$ mkdir src/routes && touch src/routes/mod.rs src/routes/health.rs






For the created files and folders to be recognized, we need to turn them into modules in lib.rs:




CODE
// src/lib.rs

pub mod routes;
pub mod settings;
pub mod startup;
pub mod telemetry;






Let's start with telemetry.rs. Make it look like this:




CODE
// src/telemetry.rs

use tracing_subscriber::layer::SubscriberExt;

pub fn get_subscriber(debug: bool) -> impl tracing::Subscriber + Send + Sync {
let env_filter = if debug {
"trace".to_string()
} else {
"info".to_string()
};
let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new(env_filter));

let stdout_log = tracing_subscriber::fmt::layer().pretty();
let subscriber = tracing_subscriber::Registry::default()
.with(env_filter)
.with(stdout_log);

let json_log = if !debug {
let json_log = tracing_subscriber::fmt::layer().json();
Some(json_log)
} else {
None
};

let subscriber = subscriber.with(json_log);

subscriber
}

pub fn init_subscriber(subscriber: impl tracing::Subscriber + Send + Sync) {
tracing::subscriber::set_global_default(subscriber).expect("Failed to set subscriber");
}






We are configuring tracing_subscriber's level and format depending on whether or not our app is in production. If debug is true, then we're in development mode. Else, in production. We want JSON output in production since that is easier to parse. Then, we initialized tracing in another function based on the subscriber given.



Next, settings.rs:




CODE
// src/settings.rs

/// Global settings for exposing all preconfigured variables
#[derive(serde::Deserialize, Clone)]
pub struct Settings {
pub application: ApplicationSettings,
pub debug: bool,
}

/// Application's specific settings to expose `port`,
/// `host`, `protocol`, and possible URL of the application
/// during and after development
#[derive(serde::Deserialize, Clone)]
pub struct ApplicationSettings {
pub port: u16,
pub host: String,
pub base_url: String,
pub protocol: String,
}

/// The possible runtime environment for our application.
pub enum Environment {
Development,
Production,
}

impl Environment {
pub fn as_str(&self) -> &'static str {
match self {
Environment::Development => "development",
Environment::Production => "production",
}
}
}

impl TryFrom<String> for Environment {
type Error = String;

fn try_from(s: String) -> Result<Self, Self::Error> {
match s.to_lowercase().as_str() {
"development" => Ok(Self::Development),
"production" => Ok(Self::Production),
other => Err(format!(
"{} is not a supported environment. Use either `development` or `production`.",
other
)),
}
}
}

/// Multipurpose function that helps detect the current environment the application
/// is running using the `APP_ENVIRONMENT` environment variable.
///
/// ```
{% endraw %}

/// APP_ENVIRONMENT = development | production.
///
{% raw %}




///

/// After detection, it loads the appropriate .yaml file

/// then it loads the environment variable that overrides whatever is set in the .yaml file.

/// For this to work, you the environment variable MUST be in uppercase and starts with APP,

/// a _ separator then the category of settings,

/// followed by __ separator, and then the variable, e.g.

/// APP__APPLICATION_PORT=5001 for port to be set as 5001

pub fn get_settings() -> Result {

let base_path = std::env::current_dir().expect("Failed to determine the current directory");

let settings_directory = base_path.join("settings");



CODE
// Detect the running environment.
// Default to `development` if unspecified.
let environment: Environment = std::env::var("APP_ENVIRONMENT")
.unwrap_or_else(|_| "development".into())
.try_into()
.expect("Failed to parse APP_ENVIRONMENT.");
let environment_filename = format!("{}.yaml", environment.as_str());
let settings = config::Config::builder()
.add_source(config::File::from(settings_directory.join("base.yaml")))
.add_source(config::File::from(
settings_directory.join(environment_filename),
))
// Add in settings from environment variables (with a prefix of APP and '__' as separator)
// E.g. `APP_APPLICATION__PORT=5001 would set `Settings.application.port`
.add_source(
config::Environment::with_prefix("APP")
.prefix_separator("_")
.separator("__"),
)
.build()?;

settings.try_deserialize::<Settings>()



}



CODE



We have a couple of structs and an enum. These structs map directly to the `.yaml` files we will create soon. The bulk of the work in this `settings.rs` file is done in the `get_settings` function. Anytime we need some settings variables, we will get them by calling this function. Looking inwards, we first try to get the path of the directory where our `.yaml` files are located. Then we detect whether or not we are in development. By default, we assume that the app is in development. To change it to production, you must set `APP_ENVIRONMENT=production` in your `.env` file or any other way you set your environment variables. Since development and production environments share some variables &mdash; we store those in `base.yaml` &mdash; we use our `config` crate to first load those common configurations before loading environment-specific ones. This is because we are likely to override those common configurations on a per-environment basis. There are some configurations in the `.yaml` files that we may want to change their values using environment variables. Tokens, passwords and secret_key are some examples. We want the ones set via environment variables to take precedence. For example, if we set `debug: true` in `base.yaml` but in production, we want `debug: false`. We can just do `APP_DEBUG=true` in our `.env` file and this will override the one in `base.yaml`. Notice the prefix, `APP_`. It's required for such to be recognized as the setting's variable. You are at liberty to change the prefix as well. We learn more about these nuances as we progress. Now, let's create the `settings/` directory at the root of the project. We will also create `base.yaml`, `development.yaml` and `production.yaml` in it:



```shell
~/rust-auth/backend$ mkdir settings && touch settings/base.yaml settings/development.yaml settings/production.yaml





For now, make settings/base.yaml looks like this:




CODE
# settings/base.yaml
application:
port: 5000






settings/development.yaml:




CODE
# settings/development.yaml

application:
protocol: http
host: 127.0.0.1
base_url: "http://127.0.0.1"

debug: true






And, settings/production.yaml:




CODE
# settings/production.yaml

application:
protocol: https
host: 0.0.0.0
base_url: ""

debug: false






Next is src/startup.rs:




CODE
// src/startup.rs

pub struct Application {
port: u16,
server: actix_web::dev::Server,
}

impl Application {
pub async fn build(settings: crate::settings::Settings) -> Result<Self, std::io::Error> {
let address = format!(
"{}:{}",
settings.application.host, settings.application.port
);

let listener = std::net::TcpListener::bind(&address)?;
let port = listener.local_addr().unwrap().port();
let server = run(listener).await?;

Ok(Self { port, server })
}

pub fn port(&self) -> u16 {
self.port
}

pub async fn run_until_stopped(self) -> Result<(), std::io::Error> {
self.server.await
}
}

async fn run(listener: std::net::TcpListener) -> Result<actix_web::dev::Server, std::io::Error> {
let server = actix_web::HttpServer::new(move || {
actix_web::App::new().service(crate::routes::health_check)
})
.listen(listener)?
.run();

Ok(server)
}






It starts up our entire application and is done in the run_until_stopped method of the Application struct. The motive for writing this way is for easy testing. This is NOT the only way to start actix-web server. The normal practice is way shorter, at least for a start. But this is entirely a design decision which is optional.



Let's enter into src/main.rs:




CODE
// src/main.rs

#[tokio::main]
async fn main() -> std::io::Result<()> {
dotenv::dotenv().ok();

let settings = backend::settings::get_settings().expect("Failed to read settings.");

let subscriber = backend::telemetry::get_subscriber(settings.clone().debug);
backend::telemetry::init_subscriber(subscriber);

let application = backend::startup::Application::build(settings).await?;

tracing::event!(target: "backend", tracing::Level::INFO, "Listening on http://127.0.0.1:{}/", application.port());

application.run_until_stopped().await?;
Ok(())
}






src/main.rs is the entry point for Rust's applications. We opt for #[tokio::main] runtime. You can use #[actix_web::main] instead. Then, we brought dotenv into the game to help load all environment variables in our .env file. We then get our settings as written in src/settings.rs. Telemetry is then initialized. Our entire app was then built and subsequently run but before it was run, we let the developer know the port where our app runs using tracing::event macro. We could get the port here because we made it available in our src/startup.rs. With this, we may not touch this file, src/main.rs, again throughout this series. Our point-of-contact will be src/startup.rs.



If you try to run our skeletal app at this point, it will not compile yet. Let's fix that.



Navigate to src/routes/health.rs and make it look like this:




CODE
// src/routes/health.rs

#[tracing::instrument]
#[actix_web::get("/health-check/")]
pub async fn health_check() -> actix_web::HttpResponse {
tracing::event!(target: "backend", tracing::Level::DEBUG, "Accessing health-check endpoint.");
actix_web::HttpResponse::Ok().json("Application is safe and healthy.")
}






We have a simple endpoint to check whether or not is online. You can see how easy it is to write an API endpoint in actix-web. Apart from the instrumentations, you can wire up a "fully functional" GET request endpoint with just 3 lines of code!!!



Looking into the endpoint, we used #[tracing::instrument] to help keep logs of all requests into this function. That's instrumentation. We then use #[actix_web::get("/health-check/")] to signal that only GET requests are allowed on /health-check/. Any other methods will be rejected. One of the reasons for using . You can also connect with/follow me on . It isn't bad if you help share this article for wider coverage. I will appreciate it...

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
Use custom web fonts in Google Sheets charts
2 Quellen
Introducing the new 1Password App for Google Chat
1 Quelle
Context-aware access controls are available for Gemini Enterprise in the Admin console
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Full-stack authentication system using rust (actix-web) and sveltekit

Thematisch verwandte Begriffe: Fullstack, authentication, system, using · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...