
Rust Web Programming
Description
Alles über E-Books | Antworten auf Fragen rund um E-Books, Kopierschutz und Dateiformate finden Sie in unserem Info- & Hilfebereich.
- Explore the exciting evolution of Rust in recent years with WebAssembly, Axum, native TLS, and SurrealDB
- Build code in a scalable way with microservice and nanoservice design patterns
Book DescriptionRust is no longer just for systems programming. This book will show you why this safe and performant language is a crucial up-and-coming option for developing web applications, and get you on your way to building fully functional Rust web apps. You don't need any experience with Rust to get started, and this new edition also comes with a shallower learning curve. You'll get hands-on with emerging Rust web frameworks including Actix, Axum, Rocket, and Hyper. You'll look at injecting Rust into the frontend with WebAssembly and HTTPS configuration with NGINX. Later, you'll move on to more advanced async topics, exploring TCP and framing, and implementing async systems. As you work through the book, you'll build a to-do application with authentication using a microservice architecture that compiles into one Rust binary, including the embedding of a frontend JavaScript application in the same binary. The application will have end-to-end atomic testing and a deployment pipeline. By the end of this book, you'll fully understand the significance of Rust for web development. You'll also have the confidence to build robust, functional, and scalable Rust web applications from scratch.What you will learn - Build scalable Rust web applications as monoliths or microservices
- Develop a deeper understanding of async Rust
- Get to grips with Rust language features such as traits and the borrow checker
- Manage authentication and databases in Rust web apps
- Build app infrastructure on AWS using Terraform
- Learn how to package and deploy Rust servers
- Build unit tests and end-to-end tests for your Rust web apps with Python
Who this book is forThis book is for web developers who are looking to learn or adopt Rust to build safe and performant web applications. This includes developers familiar with languages such as Python, Ruby, and JavaScript. You don't need any prior experience in Rust to start this book. However, you'll need a solid understanding of web development principles, along with basic knowledge of HTML, CSS, and JavaScript to get the most out of it.
All prices
More details
Other editions
New editions

Content
- Cover
- Copyright Page
- Contributors
- Tabel of Contents
- Preface
- Free benefits with your book
- Chapter 1: A Quick Introduction to Rust
- Technical requirements
- What is Rust?
- Why is Rust revolutionary?
- Reviewing data types and variables in Rust
- Using strings in Rust
- Using integers and floats
- 8-byte integers
- 16-byte integers
- Introducing floats
- Storing data in arrays
- Storing data in vectors
- Mapping data with enums
- Mapping data with HashMaps
- Handling results and errors
- Controlling variable ownership
- Copying variables
- Moving variables
- Immutable borrowing of variables
- Mutable borrowing of variables
- Scopes
- Running through lifetimes
- Building Structs
- Summary
- Questions
- Answers
- Further reading
- Chapter 2: Useful Rust Patterns for Web Programming
- Technical requirements
- Verifying with traits
- Metaprogramming with macros
- Mapping messages with macros
- Defining our contracts and contract handler
- Defining functions to handle contracts
- Building a register contract macro
- Using our macro in a program
- Configuring our functions with traits
- Defining the trait signature
- Implementing our trait for the database handle
- Calling our macro with our trait
- Checking struct state with the compiler
- Defining the state of a struct
- Changing the state of a struct
- Summary
- Questions
- Answers
- Chapter 3: Designing Your Web Application in Rust
- Technical requirements
- Managing a software project with Cargo
- Basic Rust compilation
- Building with Cargo
- Shipping crates with Cargo
- Documenting with Cargo
- Interacting with Cargo
- Structuring code through nanoservices
- Building to-do structs
- Configuring our file structure
- Defining the task status
- Building our to-do structs
- Managing structs with an API
- Storing tasks with our data access layer
- Defining our module layout
- Building our JSON file handle
- Building storage API functions
- Creating a task using our DAL
- Summary
- Questions
- Answers
- Chapter 4: Async Rust
- Technical requirements
- Understanding threads and processes
- Understanding async and await with Rust
- Implementing our own async task queue
- Building a task-spawning function
- Building an async sleep future
- Running our async sleep future
- Exploring high-level concepts of tokio
- Implementing an HTTP server in Hyper
- Setting up the Hyper server environment
- Building the Hyper request handle function
- Building the main function for running the Hyper server
- Implementing HTTP2 for our Hyper server
- Summary
- Questions
- Answers
- Chapter 5: Handling HTTP Requests
- Technical requirements
- Launching a basic web server
- Defining our file layout
- Writing our server code
- Running our server
- Connecting the core to the server
- Defining our core file layout
- Coding our networking layer
- Refactoring our to-do items
- Serving our to-do items
- Defining the JSON body
- Defining the get all API
- Connecting get_all to our server
- Handling errors in API endpoints
- Defining our glue module
- Building our custom error
- Mapping our custom error
- Integrating our glue module into our dal
- Integrating our glue module into our core layer
- Integrating our glue module into our networking layer
- Summary
- Questions
- Answers
- Chapter 6: Processing HTTP Requests
- Technical requirements
- Passing parameters via the URL
- Passing data via POST body
- Defining the core create function
- Wrapping our core in the networking layer
- Configuring our create network route
- Testing our endpoint with cURL
- Deleting resources using the DELETE method
- Defining our delete JSON file method
- Adding delete to our networking layer
- Defining the delete route
- Refactoring our JSON file code
- Automating running our server
- Updating resources using a PATCH method
- Extracting data from HTTP request headers
- Defining our header token modules
- Implementing an Actix header extraction trait
- Extracting headers from requests in the Actix networking layer
- Summary
- Questions
- Answers
- Chapter 7: Displaying Content in the Browser
- Technical requirements
- Building out a development setup
- Connecting our development environment and application
- Running our frontend server
- Serving the frontend from Rust
- Defining our frontend embedding dependencies
- Embedding the HTML file
- Embedding the React application
- Serving the embedded frontend
- Connecting backend API endpoints to the frontend
- Defining our interfaces
- Defining our API utils
- Defining our GET API call
- Integrating our get API call in our main app
- Building out the rest of the API calls
- Creating React components
- Building a create item form
- Injecting our form component into our app
- Testing our create item form
- Rendering multiple React components
- Inserting styles with CSS
- Serving our CSS
- Defining page borders with CSS for different screen sizes
- Styling React components with CSS
- Summary
- Questions
- Answers
- Chapter 8: Injecting Rust in the Frontend with WebAssembly
- Technical requirements
- What is WASM?
- Setting up our WASM build
- Defining our WASM module
- Loading WASM in the frontend
- Inspecting a WASM interface
- Integrating WASM loading into our React component
- Passing our WASM output to subcomponents
- Loading WASM on the local machine
- Building a WASM kernel
- Building a WASM library
- Building a WASM client
- Summary
- Questions
- Answers
- Chapter 9: Data Persistence with PostgreSQL
- Technical requirements
- Building our PostgreSQL database
- Why we should use a proper database
- Why use Docker?
- How to use Docker
- Running a database in Docker
- Exploring routing and ports in Docker
- Running Docker in the background with Bash scripts
- Adding SQLX to the data access layer
- Defining our database transactions
- Connecting our transactions to the core
- Connecting our transactions to the server
- Creating our database migrations
- Refactoring our frontend
- Summary
- Questions
- Answers
- Appendix
- Chapter 10: Managing User Sessions
- Technical requirements
- Building our auth server
- Building our data access layer
- Building our auth core module
- Networking layer
- Defining our user data model
- Storing passwords
- Verifying passwords
- Creating users
- Defining our create user database transactions
- Defining our core create API endpoint
- Defining our networking create API endpoint
- Refactoring our JWT
- Restructuring our JWT for a unique ID
- Creating a get key function for encoding
- Creating an encode function to encode user credentials
- Creating a decode function to extract user credentials
- Building our login API
- Getting users via email in the data access layer
- Creating our core login API
- Mounting our core login function to our server
- Interacting with our auth server
- Adding authentication to our frontend
- Building a login API call
- Adding tokens to our API calls
- Building a login form component
- Connecting the login form to the app
- Summary
- Questions
- Answers
- Appendix
- Chapter 11: Communicating Between Servers
- Technical requirements
- Getting users from auth with the unique ID
- Adding get by unique ID to dal
- Adding get by unique ID to core
- Adding get by unique ID to networking
- Making auth accessible to other servers
- Tethering users to to-do items
- Linking our to-do items to users in the database
- Adding user IDs to data access transactions
- Adding user IDs to core functions
- Adding user IDs to networking functions
- Testing our server-to-server communication with Bash
- Summary
- Questions
- Answers
- Chapter 12: Caching Auth Sessions
- Technical requirements
- What is caching?
- Setting up Redis
- Building our Redis module
- Defining the user session
- Building the login process
- Building the logout process
- Building the update process
- Building our Redis client
- Building the login/logout client
- Building the update client
- Connecting our cache to our servers
- Building our cache kernel
- Calling the kernel from our to-do server
- Calling the kernel from our auth server
- Summary
- Questions
- Answers
- Chapter 13: Observability Through Logging
- Technical requirements
- What are RESTful services?
- Building frontend code on command
- What is logging?
- Logging via the terminal
- Defining a logger
- Creating a logging middleware
- Integrating our logger into our servers
- Logging via a database
- What is an actor?
- Building our logging actor
- Update logging functions
- Configuring our logging database
- Summary
- Questions
- Answers
- Chapter 14: Unit Testing
- Technical requirements
- The importance of unit testing
- Writing our first unit test
- Testing our core
- Unit testing our networking layer
- Defining our test module
- Mocking our database handle
- Mocking our user cache handle
- Defining our mock service
- Writing tests
- Unit testing our data access layer
- Defining a teardown SQL command
- Creating a DAL unit test
- Refactoring our testing script
- Summary
- Questions
- Answers
- Chapter 15: End-to-End Testing
- Technical requirements
- The importance of end-to-end testing
- Building our to-do HTTP kernel
- Building our create item HTTP kernel
- Building our delete item HTTP kernel
- Building our atomic end-to-end tests
- Writing the end-to-end test
- Running our atomic end-to-end test
- Testing workflows with end-to-end tests
- Configuring existing code to accommodate the workflow tests
- Building a kernel API call for creating a user
- Building a kernel API call for logging in
- Creating a workflow test workspace
- Creating a delete workflow test
- Updating our test runner bash script
- Summary
- Questions
- Answers
- Chapter 16: Deploying Our Application on AWS
- Technical requirements
- Setting up our build environment
- Setting up an AWS SSH key for an Amazon EC2 instance
- Setting up our AWS account
- Setting up our Terraform build
- Building a server build pipeline
- Packaging our system with Docker
- Building the base Docker image for all servers
- Defining the build config for our monolith
- Defining the build config for our microservices
- Distributing our system with Docker
- Deploying our system with Docker
- Writing a Terraform script for our server and database
- Writing a Docker Compose system for our server
- Writing a deployment script for our system
- Summary
- Questions
- Answers
- Chapter 17: Configuring HTTPS with NGINX on AWS
- Technical requirements
- What is HTTPS?
- Implementing HTTPS locally with Docker Compose
- Installing SSL
- Attaching a URL to our deployed application on AWS
- Attaching an Elastic IP to our server
- Registering a domain name
- Enforcing HTTPS on our application on AWS
- Getting certificates for our URL
- Prepping our EC2 instances
- Creating a load balancer for our traffic
- Creating security groups to lock down and secure traffic
- Updating our deployment script
- Attaching our URL to the load balancer
- Summary
- Questions
- Answers
- Further reading
- Chapter 18: Practicalities of Using Microservices and Nanoservices
- Technical requirements
- When to use nanoservices, microservices, and monoliths
- The negative effects of microservices
- Container orchestration
- Flying blind
- Network calls are expensive
- The positive effects of microservices
- Rolling out releases
- Isolation of code
- Flexibility to experiment
- Managing private repositories with dockpack
- Integrating Rust nanoservices with other languages using C interfaces
- Creating our to-do Rust library
- Creating our C interface
- Interfacing with our Rust nanoservice with Python
- Summary
- Chapter 19: Low-Level Networking
- Technical requirements
- Building a TCP server
- Building a kernel for key-value messages
- Building a TCP server
- Building a TCP client
- Implementing HTTP on top of TCP
- Defining a basic server
- Defining an HTTP client from scratch
- Implementing HTTPS on top of TCP
- Exploring WebSockets
- Summary
- Questions
- Answers
- Chapter 20: Distributed Computing
- Technical requirements
- Introduction to queuing
- Defining the layout of the queuing system
- Client
- Compute unit and kernel
- Server and worker
- Other configurations
- Building our kernel
- Building our compute unit
- Building our worker
- Building our server
- Building our client
- Running our queuing system
- Summary
- Questions
- Answers
- Chapter 21: Unlock Your Exclusive Benefits
- PacktPage
- Other Books You May Enjoy
- Index
1
A Quick Introduction to Rust
Rust is growing in popularity, but it has a reputation for having a steep learning curve. However, if taught correctly, this learning curve can be reduced. By covering the basic rules around Rust, as well as learning how to manipulate a range of data types and variables, we will be able to write simple programs in the same fashion as dynamically typed languages, using a similar number of lines of code.
Understanding the basics is the key to effective web programming in Rust. I have maintained entire Kubernetes clusters where all the servers are written in Rust. Because I utilized traits, error handling, and generics effectively, it made reusing code in Rust extremely effective. In my experience, it takes fewer lines of code to build out Rust servers than it does Python servers if the basics of Rust are well utilized.
The goal of this chapter is to cover the main differences between Rust and generic dynamic languages and to provide you with a quick understanding of how to utilize Rust.
In this chapter, we will cover the following topics:
- What is Rust?
- Reviewing data types and variables in Rust
- Controlling variable ownership
- Building structs
Once we have covered the main concepts in this chapter, you will be able to code basic programs in Rust that will run. You will also be able to debug your programs and understand the error messages that are thrown by the Rust compiler. As a result, you will have the foundations to be productive in Rust. You will also be able to move on to structuring Rust code over multiple files.
In this chapter and the following chapter, we will cover enough Rust to be productive in web programming. However, we will not be able to do the entire Rust programming language justice in two chapters. If you want to dive deeper into the Rust programming language after reading this chapter, then you can explore the further reading list at the end of the chapter.
Your purchase includes a free PDF copy + code bundle
Your purchase includes a DRM-free PDF copy of this book, the code bundle, and additional exclusive extras. See the Free benefits with your book section in the Preface to unlock them instantly and maximize your learning.
Technical requirements
For this chapter, we only need access to the internet, as we will use the online Rust playground to implement the code. The code examples provided can be run in the online Rust playground at https://play.rust-lang.org/.
You can download the example project and code for this book by following the instructions in the Download the example code files section in the Preface of this book.
This chapter's code files are included in the downloadable code bundle.
What is Rust?
Rust is a cutting-edge systems programming language that has been making waves since Mozilla Research introduced it in 2010. With a focus on safety, concurrency, and performance, Rust is a formidable alternative to traditional languages like C and C++. Its most notable feature, the ownership system, enforces rigorous memory safety rules at compile time. This approach effectively eradicates common pitfalls like null pointer dereferencing and buffer overflows, all without needing a garbage collector.
Designed for high performance, Rust provides granular control over hardware and memory, making it perfect for developing operating systems, game engines, and other performance-critical applications. Its syntax is both modern and expressive, offering features typically seen in higher-level languages, such as pattern matching and algebraic data types, while retaining the efficiency required for system-level programming. Consequently, Rust has rapidly attracted a strong and active community, bolstered by excellent documentation and a burgeoning ecosystem of libraries and tools.
Rust's adoption has been nothing short of remarkable, particularly in tech sectors where safety and performance are crucial. Major players like Microsoft, Amazon, and Dropbox have integrated Rust into their technology stacks to capitalize on its reliability and efficiency. Rust's consistent ranking since 2016 as the most loved programming language in Stack Overflow's annual developer surveys is a testament to its appeal. Developers laud Rust for its unique blend of performance and safety, alongside its supportive and vibrant community.
Moreover, Rust's ecosystem, highlighted by its package manager, Cargo, offers a seamless development experience. Cargo simplifies dependency management, project building, and testing, streamlining workflows that can be cumbersome in other systems programming languages. This robust combination of features positions Rust as a unique and powerful tool for developers aiming to create reliable, high-performance software.
Why is Rust revolutionary?
With programming, there is usually a trade-off between speed and resources and development speed and safety. Low-level languages such as C/C++ can give a developer fine-grained control over a computer, with fast code execution and minimal resource consumption. However, this is not free. Languages such as C/C++ need manual memory management, which can introduce bugs and security vulnerabilities. A simple example of this is a buffer overflow attack.
A buffer overflow attack occurs when the programmer does not allocate enough memory. For instance, if the buffer only has a size of 15 bytes, and 20 bytes are sent, then the excess 5 bytes might be written past the boundary. An attacker can exploit this by passing in more bytes than the buffer can handle. This can potentially overwrite areas that hold executable code with their own code.
There are other ways to exploit a program that does not have correctly managed memory. On top of increased vulnerabilities, it takes more code and time to solve a problem in a low-level language. As a result of this, C++ web frameworks do not take up a large share of web development. Instead, it usually makes sense to go for high-level languages such as Python, Ruby, and JavaScript. Using such languages will generally result in a developer solving problems safely and quickly.
However, it must be noted that this memory safety comes at a cost. These high-level languages generally keep track of all the variables defined and their references to a memory address. When there are no more variables pointing to a memory address, the data in that memory address gets deleted. This process is called garbage collection and consumes extra resources and time, as a program must be stopped to clean up the variables.
With Rust, memory safety is ensured without the costly garbage collection process. Rust ensures memory safety through a set of ownership rules, checked at compile time with a borrow checker. Because of this, Rust enables rapid, safe problem-solving with truly performant code, thus breaking the speed/safety trade-off.
Memory safety is the property of programs having memory pointers that always point to valid memory.
With more data processing, traffic, and complex tasks lifted into the web stack, Rust, with its growing number of web frameworks and libraries, has now become a viable choice for web development. This has led to some truly amazing results in the web space for Rust. In 2020, Shimul Chowdhury ran a series of tests against servers with the same specs but different languages and frameworks. The results can be seen in the following figure (note that the Rust frameworks comprise Actix Web and Rocket):
Figure 1.1 - Results of different frameworks and languages by Shimul Chowdhury (found at https://www.shimul.dev/en/blog/2020/benchmarking-flask-falcon-actix-web-rocket-nestjs/)
In the preceding figure, we can see that there are some variations in the languages and frameworks. These Rust servers are in a completely different league when it comes to total requests handled and data transferred. Other languages, such as Golang, have come onto the scene, but the lack of garbage collection in Rust has managed to outshine Golang. This was demonstrated in Jesse Howarth's 2020 blog post Why Discord is switching from Go to Rust (https://discord.com/blog/why-discord-is-switching-from-go-to-rust). In this post, it was clear that Golang servers were producing latency spikes, whereas the Rust servers were not.
The garbage collection that Golang was implementing to keep the memory safe resulted in two-minute spikes. But it does not stop there. In 2022, AWS produced a report called "Sustainability with Rust," wherein they created a ratio of energy consumption, resulting in the following table:
Language
Energy Rating
C
1.00
Rust
1.03
JavaScript
...System requirements
File format: ePUB
Copy protection: Adobe-DRM (Digital Rights Management)
System requirements:
- Computer (Windows; MacOS X; Linux): Install the free reader Adobe Digital Editions prior to download (see eBook Help).
- Tablet/smartphone (Android; iOS): Install the free app Adobe Digital Editions or the app PocketBook before downloading (see eBook Help).
- E-reader: Bookeen, Kobo, Pocketbook, Sony, Tolino and many more (not Kindle).
The file format ePub works well for novels and non-fiction books – i.e., „flowing” text without complex layout. On an e-reader or smartphone, line and page breaks automatically adjust to fit the small displays.
This eBook uses Adobe-DRM, a „hard” copy protection. If the necessary requirements are not met, unfortunately you will not be able to open the eBook. You will therefore need to prepare your reading hardware before downloading.
Please note: We strongly recommend that you authorise using your personal Adobe ID after installation of any reading software.
For more information, see our ebook Help page.
File format: ePUB
Copy protection: without DRM (Digital Rights Management)
System requirements:
- Computer (Windows; MacOS X; Linux): Use a reader that can handle the file format ePUB, such as Adobe Digital Editions or FBReader – both free (see eBook Help).
- Tablet/Smartphone (Android; iOS): Install the free app Adobe Digital Editions or the app PocketBook (see eBook Help).
- E-reader: Bookeen, Kobo, Pocketbook, Sony, Tolino and many more (not Kindle).
The file format ePUB works well for novels and non-fiction books – i.e., 'flowing' text without complex layout. On an e-reader or smartphone, line and page breaks automatically adjust to fit the small displays.
This eBook does not use copy protection or Digital Rights Management
For more information, see our eBook Help page.