This is the only concrete evidence that I can find on the internet that actually shows people putting routes in something other than the main.rs file, THANK YOU
as a person who did few hundred apis in different languages and peeking into rust, i have to say this is top notch, up to the point, no bs tutorial covering exactly the things i was curious about, amazing work! thanks
Hi Jeremy, great tutorial. I have one suggestion: it would be helpful if, at the start of each step, you stated more explicitly what you want to achieve by the end of the step. You do this to some extent, but sometimes things only make sense in hindsight. Again, great tutorial!
Feeling really lucky to see this went out just as I decided to learn how to use Axum, especially with this level of quality! It's all very new to me but your explanations are perfectly clear and concise. Thanks a lot!
Thank you so much for this great tutorial. The teaching pace is quite fast and it was no piece of cake to understand all the methods you use as a rust noob (I've only read "the book", so sometimes, I was wondering "why does he uses as_ref() instead of the borrow checker, etc.)), but it was very interesting and informative. Big up to you!
it took me like 2 days almost following you line by line, I will have to revisit the code now by myself. This was lots of info in one video considering im totally new in rust let alone its eco-system pakcges etc
Incredible tutorial. I am coming from the embedded world of C with a tiny bit of Rust experience, so the Rust things are a bit more familiar to me than the web development things. As I am going through this tutorial (and the follow-along), I am starting to really understand the intricacies of the backend. It is so clear that you have put tons of effort into preparation -- when I rewind and watch over, it's not because your explanations are confusing, but because you have managed to distill a lot of knowledge into a little bit of time! Instant subscribe here, this is amazing stuff.
Great, thank you for the detailed feedback. I'd appreciate your feedback on the web-app production coding session as well-it's 3 hours and 50 minutes long, so take your time. I'm also working on the second episode of the web-app production coding series, which should be informative too.
This has been amazingly helpful, thank you. I really appreciate how you have taken the many concepts that are documented but only to a shallow simplistic level, and shown how to put them together, in a way that is more similar to how you'd want in production. It bridges a knowledge gap that is very real for me.
Thanks for this video! I'm really digging the casual code-along style combined with really excellent preparation from your side, it's clear in everything you say that you deeply understand the subject matter.
Regarding the delete implementation in 'CRUD - REST API', I’ve often found it easier to consume from the client if the server returns '204 NoContent', even if the item isn’t found. This makes the call idempotent, which allows the client the ability to retry in case of the prior call failing for any number of reasons. 32:39
One problem you might face if you decide to implement login in a different route (e.g. "/auth/login" instead of "/api/login"): Make sure to set the auth cookie's path to "/".
Fantastic video Jeremy. I learned so much in this video, thank you 🙏🙏🙏. One small tip: you can use ULIDs instead of UUIDs to get timestamped and sortable IDs, which are certainly useful when using IDs inside logs!
Thanks for the kind words! Also, thank you for the note about ULIDs; they can indeed be used. In our case, we prefer timestamps (UTC ISO8601) and UUIDs because their conversions back and forth to numbers are well-known and widely supported. For log analytics, having these two as unsigned numbers can make a significant difference when querying large data sets (e.g., with dataframe tools).
This is a very well done tutorial. Actually useful and well beyond the standard "hello world" nonsense you usually see. This guy does very good videos.
I have a question / thought. By making the context extractor clone the context result you force the crate's Error type to implement Clone. This cascades into an issue with a real world app using 3rd party db's like sqlx. Because we would probably want to use thiserror and #[from] to convert sqlx errors into your crate's custom error type. If you use several external libraries and want to convert several external errors this can be a headache. I handled by implementing From and using a String as the error date.
Even it's hard for me to keep up with video learning because my english not good enough but I still love his format of tutorial, that's rarely seen on vid turtorial flooding us everyday. Keep up the good work and thank you.
Very Good Video, Thanks a lot for this. :) At about 1 hour, the pace got a bit too fast for me. But it definetely helped me in understand rust and axum etc.
Thanks for the kind words. Yes, sometimes the editing might be too fast. I'll make sure to keep it in check for the next ones. Feel free to check out the latest videos about production coding. Here is the playlist: th-cam.com/play/PL7r-PXl6ZPcCTTxjmsb9bFZB9i01fAtI7.html
On the "Path Param" part, you could have shown as well how to use HelloParams with hello2. For me it was very intuitive, even thought your example is also nice (and probably should be kept). Really enjoying this vid!
I loved the idea of request logging and having a uuid per request. It will make life so much easier when tracing down bug reports if we can get the request uuid from the user! I just implemented your version and did some tweaking so that a uuid is created in the ctx middleware so that it can be used in my tracing layer and set as a tag /attribute on spans for distributed traces! This is a game changer!
I have been slowly watching the series and building along! I’m going to continue watching and learning. But once I push my code I will send a link to my repo so you can check out the open telemetry integrations with the tracing if you are interested! Probably going to be after work tomorrow :)
Cool. If you take a look, you'll see a ReqStamp with a mw_req_stamp middleware. This stamps the "request" with the initial time_in and UUID at the appropriate place, and then exposes it as an Axum extractor. This way, all handlers and middleware can access it. The mw_response_mapper utilizes that information for the request UUID and even includes time_in for computing the duration. So, everything fits together nicely.
Great content, thank you. I am quite impressed at your presentation technique and how your editor and terminal orchestration works well together. Are those special tools you are using or is this just clever video editing? Also, what editor are you using? The multi line editing seems like a neat feature.
Thanks for the kind words. I am using VSCode with Google Material theme. ScreenBrush for the line Davinci Resolve for the editing. And a custom Mac screencapture CLI wrapper. Unfortunately, no magic tools, a lot of editing.
Btw, I finally took some time to create the Rust10x VSCode Extension and a webpage with information about my VSCode setup. Everything can be found here: rust10x.com/vscode
This is such an awesome "quickstart" guide for newbies into rust like me. Question: how do you handle token claims validation (possibly in the middleware) assuming the token is not stored in a db? To what do we compare the token (or the claims it contains) stored in a cookie at log-in? I suspect I might be missing something though.
In the First Extractor - Ctx section, I wonder, why did you do impl of FromRequestPart for Ctx within the mq_require_auth. It could have been done outside of it as an independent thing too? Would that work?
Yes, there seems to be a problem on Windows. You can move the quick_dev.rs in examples/ and run it from there (cargo watch -x "run --example quick_dev")
Yes, it's intentionally designed to be primitive. You can refer to the follow-up video on web-app production coding; it marks the beginning of an actual app. For more details, visit: rust10x.com/web-app More to come…
Hi. I have question. I cannot run cargo watch -q -c -w tests/ -x "test quick_dev -- --nocapture" because file.exe is already blocked by first terminal and it cannot rebuild it. Is there any option how to overcome this ? Thank you for your help
Hey, I think we've found the solution. Move `quick_dev.rs` to the `examples` folder, rename the function to `#[tokio::main]`, and it should allow you to do the following: - In Terminal 1: `cargo watch -q -c -w src/ -x run` - In Terminal 2: `cargo watch -q -c -w examples/ -x 'run --example quick_dev'`
@@mumk Yes, and actually, the quick_dev is not about tests or test reports. So, it's better this way. In the next video, we will use tests, but for real unit tests. Everything will fall into place.
@@irlshrek Note that the ModelController pattern to scale needs to be split between ModelManager (that hold the db pool)and ModelController (that hold the access per entity, e.g., TaskBmc, for Task Backend Model Controller).
Really amazing tutorial : D. Was wondering if you could elaborate on the model controller architecture you used and what it's called? Is it a derivation of MVC?
Thank you. Your timing is great, as I am currently constructing the follow-up course, which focuses on best practices for production code. We'll be dividing the ModelManager (which maintains the model-related application states, such as db_pool) and the ModelController, drawing inspiration from the Data Access Object (DAO) pattern. In a project management data model, for instance, you might have: A ProjectBmc (Backend Model Controller) that handles Create, Read, Update, and Delete (CRUD) operations, and possibly more, for Project entities. Similarly, a TaskBmc would manage the CRUD operations, and potentially additional ones, for Task entities. This approach gives you complete control over the data structures and access methods in your Application Domain, while still allowing for code reuse and normalization. Next course is schedule for June 4th.
Great Video! I am wondering how to utiliize snippets like "region" and function definitions that you are so quickly creating? Do you have custom Snippets and hotkeys for VS Code or just utilizing what's in place from rust-analyzer plugin?
Btw, I finally took some time to create the Rust10x VSCode Extension and a webpage with information about my VSCode setup. Everything can be found here: rust10x.com/vscode
Good point. This might come later. In the next tutorial, we will refactor the model layer while still using a mock-store (a better one). This will demonstrate how to implement multi-scheme password encryption, secure web tokens, and RPC (JSON-RPC).
Whenever you use code assist to import a symbol (add use statement), you should say "Attention! Importing!" out loud. The code assist UI disappears after a split second and one must rewind to see which symbol was imported (if there are multiple suggestions).
Typically, I take a moment to comment when a decision is needed (though I may have missed a few). For more trivial matters, such as import decisions, I try not to interrupt the flow too much, as these should be straightforward to handle.
I've been following along this awesome course and the first time I stumbled was at 54:35 where you say "and now we just do a 'ctx-question mark'." It seemed weird because ctx isn't passed anywhere, no method is called on it and as I am new to Rust it took me moment to understand what that means. A Rust-beginner-friendly comment would be that this has the effect of evaluating whether ctx (which is a Result) contains an error and raising ie, propagating it.
@@JeremyChone and by the way, your videos are really excellent and fantastically crammed full of well thought details. Merci beaucoup pour ces vidéos! Oh, and I have of course subscribed and joined your patreon ;)
Hi, thank you for the note and joined the patreon. This makes a big difference. New cool video coming about building a AI Buddy command line with OpenAI assistant API.
That's a good question. Originally, I didn't aim to delve too deeply into rebuilding Reqwest on top of Reqwest. Instead, I focused more on "browser" use cases, where authentication is typically done with cookies. However, now I agree that it would be beneficial to broaden the scope and allow the setting of headers. The route I'm keen to avoid is the full reconstruction of a Reqwest API using the Reqwest library. My proposed approach is to expose an API like http_client.exec(reqwest_builder), perhaps coupled with some utilities for building these builders. This approach allows developers to use the simple httpc-test API/Prints, while simultaneously harnessing the full power of the Reqwest request builder. Does that make sense?
Jeremy ty so much. I am having problem with test environment. Whenever I use cargo watch for tests/ it says, it cant remove the exe file of server.(failed to remove file.....) Why does such a thing happen?
I think this is a windows thing. Try to move the quick_dev.ts as an examples/ and run it as cargo run -example quick_dev Or cargo watch -q -c -w examples/ -x “run -example quick_dev”
Hi, thanks for the great video. One question tho, how come the Ctx extractor gets called twice between mw_ctx_resolver and mw_require_auth after we implement the server log line? I know one of the calls is to extract the Ctx for mw_require_auth, but what about the other one?
The mw_ctx_resolver resolves the CTX (essentially performing the authentication) and then stores it in req.extensions, so it doesn't need to be computed again. The mw_ctx_require is an optional middleware that ensures anything following it has a CTX, even if they don't explicitly require it. This is currently just an optional safeguard because downstream handlers that request CTX will fail if there isn't one. However, it's good to have.
All my rust webdev thus far has been messing around with rocket - this does feel significantly lower level (rocket feels closer to a rails experience), but this does look interesting.
Hi jeremy, for next videos you could consider the server sent events implementation with axum, I had problems with the "try_stream!" macro of async_stream when passing custom data to it. Greetings from Colombia, your videos are very good.
@@JeremyChone No, actually it was a simpler project. I was using it to display real-time CPU and graphics card information with a browser interface. But you just gave me more ideas for other projects.
@@jorgeosorio1613 ha, cool, yes, make sense. By the way, for files in the .mp4 or .mp3/.wav/... formats, I recommend using a cloud CDN such as AWS CloudFront to S3. This approach allows you to sign the URL to ensure security and keep the app server out of the way, which is always preferable. However, when the data is being dynamically processed, you must stream it from the app server.
Thanks! The next video will be about leveling up and building the Rust Axum base code for a real application, featuring password encryption (multi-scheme), secure tokens, PostgreSQL/SQLx, and a scalable Model layer. So, not the deployment to cloud, which will come later with Kubernetes when we will learn how to do multi-service cloud app.
@@JeremyChone thank you! Looking forward for it. I'm a newbie in Rust and somehow deploying to a cloud is a sort of "hello world" for me just to get the feel on how I can use it in a production environment.
Ho, just println! with the ->> personal notation. No special tools. Next video, we will be using tracing for those. However, I still use the "debug print" in between commits to debug quickly. Tracing gets committed and for things that might be useful in production.
I'm in the very beginning of the video writing the first test function but I'm getting an error with my #[tokio::test] and all of the subsequent uses of the "?" operator. Why could this be? the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`) the trait `FromResidual` is not implemented for `()`
Hello, sir. We are using a mutex in asynchronous programming. Will it not create a deadlock if we await after locking, and another request comes in? Since we are working with a single main thread, the lock will not be able to unlock, and the request will not be able to process.
@sakuraharun00 By the way, I mentioned that "tokio::main" is multithreaded by default, but actually, it requires the rt-multi-thread feature in the tokio dependency. But because we have the `features = ["full"]` then we had the multithread by default.
Hi There, It was a very comprehensive tutorial on axum, but I am stuck at one point, the function where we create mw_require_auth at timestamp: 41:43. I am getting an error *struct takes 0 generic arguments but 1 generic argument was supplied expected 0 generic arguments*. axum::middleware::Next does not any generic arguments, and when I following the video, this was giving me trouble.
It seems you are using Axum 0.7, I am must finishing a video to show how to update. In the meantime, remove on text, and put Request from axum::body::Body, and you can remove the generic on the mw function. Should work.
@@legends_assemble4938 Awesome! I need to add a card/link to the 0.6 to 0.7 video. Eventually, I might update this course with Axum 0.7 and updated techniques.
Axum is one level below the given protocol. You can use REST, JSON-RPC, GraphQL, or even gRPC with Tonic. This is the approach I prefer: a web framework that does not try to do too much and stays at its level. Axum is awesome.
12:58 This seems to be currently broken in the latest axum version. At least for me this fails at runtime due to a false assertion in axum code (version 0.6.15), so I'm pretty sure, it's a bug. I tried axum 0.6.0 and it just worked, even without wrapping "SaveDir.." into "get_service". Edit: 0.6.12 seems to be the latest version where it works. In all versions newer than that, it fails.
Yes, see comment in the description. There was a bug introduced in `0.6.15`. In the cargo.toml put a `=0.6.12` for now. Here is the Axum ticket I filed: github.com/tokio-rs/axum/issues/1931, should be released soon.
@@JeremyChone Haha okay thanks, I guess I should have read the description before searching the error myself. Maybe you could add some kind of annotation to the video at that timestamp (If that's easily possible with youtube)? That would probably save a lot of people some time ;-)
This would be a Link Card, but unfortunately, I can't add them. To do so, you need to be part of the TH-cam Partner Program. However, for some strange reason, Google AdSense revoked my AdSense account, which removed me from the YPP. It's a bummer, as I didn't realize I would lose this feature.
@JeremyChone what extension are you using to quickly add those region comments in your videos? BTW, I really love the way you go deep into explaining Rust concepts and design patterns you use. Please keep it up. The community needs more of these type of tutorials to get more engineers to learn and use Rust. Thanks you so much!
Thanks @JeremyChone for the like. I'm going through your videos just now when I saw your feedback came in. Again, what extension are you using for those quick snippets? I really like your style of partitioning your code into regions but can't seem to find the right vscode extension to do it as quickly as you are doing in the videos.
Thanks again @@JeremyChone . Added below in rust.json (ctrl-shift-p) and now I can create regions just like you do in your videos: "Region comment": { "prefix": "reg", "body": [ "// region: ---> ${1:reg}", "// endregion: ---> ${1:reg}", ] }
@@benbolivar7954 "comment-section": { "prefix": "cc", "body": [ "// region: --- ${1:Section}", "${2}$TM_SELECTED_TEXT", "// endregion: --- ${1:Section}" ], "description": "Comment Section" }, Here is what I have. I aligned the "--- section_name} with white space. And then, in my keybindings.json I have { "key": "ctrl+c", "command": "editor.action.insertSnippet", "when": "editorTextFocus", "args": { "name": "comment-section" } },
Wonderful tutorial! Something im noticing is that the returning status code is not the same as error IntoResponse status code. For a failed login it should return 403 but it's returning 500, did I miss something?
Thanks for the kind words. It does send back a 403 Forbidden if the wrong login. (with the full final code). Here is how it works: - The impl IntoResponse for Error creates a placeholder for the real error. - The main_response_mapper will get the error and call the client_status_and_error, which will return the appropriate status code and send that back to the client. If you go to the quick_dev, and change the password on the login, you will see the login and tickets response with the following status: Status : 403 Forbidden Hope this helps. I just tested the code, and works as expected. Let me know if you have any further question.
Hello ! Thanks for this amazing vid too :) I also have a question with errors i got while implementing my own version of your vid :) Is there anyway for the error type to get a " from implementation from all types implementing errors " (well, its not clear, lemme explain ahah) I like to use ? operator, but it often does not work because MyError does not implement From (like parsing a number for example) and i dont want to have MyError filled with `ParseError(#[from] std::num::ParseIntError)`/... because they are not project's scoped. I'd like to get somewhat default but still allowing me to see original error Is there anyway to achieve this ?
BTW, I finally took some time to create the Rust10x VSCode Extension and a webpage with information about my VSCode setup. Everything can be found here: rust10x.com/vscode
Hey Jeremy, I'm following along, but I'm stuck at about 21 minutes, adding the main_response_mapper, the Response parameter and return val, need 1 generic param? according to my compiler.
Thanks for the video! Been helpful getting me up to speed on axum. I'm fairly new to Rust still and I'm trying to figure out why (at the 8:10 mark) when you use the format! macro my response body in the terminal for the test doesn't show the "Hello {name}" but instead shows "==== ." I can see my {name} printing on the server watch terminal but in the test's response body I'm just getting the equals and period (im guessing this isn't something in my code causing those symbols but rather how it's trying to parse it?). Edit: I figured it out. Not sure the full explanation here but by defining my string as a variable like let name = format!("Hello {name}"); then passing "name" into the Html like Html(name) I got the expected result. I'm curious why it was an issue for me but not you?
@@JeremyChone I think Ill try to save the output to a txt file to see if it exists there or if it's just part of the response. It's really strange haha. I got it working and my server log only shows the name I pass into the path but now I'm curious!
Hello Jeremy, Excellent training. thank you! Can diesel be integrated to Axum an Tokio for a backend API? Can actix-web be integrated to Axum? or not necessary? What is your opinion of actix-web vs Axum? or they are for different purposes! What would be the best set of Rust tools / frameworks for a backend project? Thank you!
1. Diesel - Definitely, you can use any db connectivity. Personally, I like lower db lib, like SQLx, but anything will go. 2. Actix-Web v.s. Axum - They solve the same problem, so, it would not make sense to use both. 3. Actix-Web v.s. Axum - I prefer Axum, as it is a newer tech, has a very clean API and app model, and is very well maintained. Actix-Web API looks similar, but I have not played too much with it, as I am very happy with Axum. 4. I have a new video showing pwd/token encryption, database connectivity (with SQLx), and clean architecture.
This is the only concrete evidence that I can find on the internet that actually shows people putting routes in something other than the main.rs file, THANK YOU
hahhahaha fr...
as a person who did few hundred apis in different languages and peeking into rust, i have to say this is top notch, up to the point, no bs tutorial covering exactly the things i was curious about, amazing work! thanks
+100
Hi Jeremy, great tutorial. I have one suggestion: it would be helpful if, at the start of each step, you stated more explicitly what you want to achieve by the end of the step. You do this to some extent, but sometimes things only make sense in hindsight. Again, great tutorial!
Good feedback, I will keep that in mind.
The work you do to teach us Rust with good practices is incredible. Thank you very much Jeremy.👍👌
GOAT OF ALL RUST CHANNELS OUT THERE ❤
Feeling really lucky to see this went out just as I decided to learn how to use Axum, especially with this level of quality! It's all very new to me but your explanations are perfectly clear and concise. Thanks a lot!
the quality of the production of this video is A++
Thank you so much for this great tutorial. The teaching pace is quite fast and it was no piece of cake to understand all the methods you use as a rust noob (I've only read "the book", so sometimes, I was wondering "why does he uses as_ref() instead of the borrow checker, etc.)), but it was very interesting and informative. Big up to you!
it took me like 2 days almost following you line by line, I will have to revisit the code now by myself. This was lots of info in one video considering im totally new in rust let alone its eco-system pakcges etc
I cannot even express how good this content is. Thank you! Can't believe this is free.
Incredible tutorial. I am coming from the embedded world of C with a tiny bit of Rust experience, so the Rust things are a bit more familiar to me than the web development things. As I am going through this tutorial (and the follow-along), I am starting to really understand the intricacies of the backend. It is so clear that you have put tons of effort into preparation -- when I rewind and watch over, it's not because your explanations are confusing, but because you have managed to distill a lot of knowledge into a little bit of time! Instant subscribe here, this is amazing stuff.
Great, thank you for the detailed feedback.
I'd appreciate your feedback on the web-app production coding session as well-it's 3 hours and 50 minutes long, so take your time.
I'm also working on the second episode of the web-app production coding series, which should be informative too.
This has been amazingly helpful, thank you. I really appreciate how you have taken the many concepts that are documented but only to a shallow simplistic level, and shown how to put them together, in a way that is more similar to how you'd want in production. It bridges a knowledge gap that is very real for me.
Thanks for this video! I'm really digging the casual code-along style combined with really excellent preparation from your side, it's clear in everything you say that you deeply understand the subject matter.
Thank you so much!! I'm just learning Rust, comming from scala, and this kind of resources is hard to find there.
Great video, one hour long, will take me 7-8 hours to digest.
Regarding the delete implementation in 'CRUD - REST API', I’ve often found it easier to consume from the client if the server returns '204 NoContent', even if the item isn’t found. This makes the call idempotent, which allows the client the ability to retry in case of the prior call failing for any number of reasons. 32:39
I just picked up learning rust again and this video was quiet a summary of a lot of rust features I had forgotten already. Great work!
One problem you might face if you decide to implement login in a different route (e.g. "/auth/login" instead of "/api/login"): Make sure to set the auth cookie's path to "/".
@@sampathsris yes, very important to set the path to /. Something I forgot to mention in this video.
I just can't believe this is FREE content. Thank you so much for sharing your knowledge!
Fantastic video Jeremy. I learned so much in this video, thank you 🙏🙏🙏. One small tip: you can use ULIDs instead of UUIDs to get timestamped and sortable IDs, which are certainly useful when using IDs inside logs!
Thanks for the kind words! Also, thank you for the note about ULIDs; they can indeed be used.
In our case, we prefer timestamps (UTC ISO8601) and UUIDs because their conversions back and forth to numbers are well-known and widely supported. For log analytics, having these two as unsigned numbers can make a significant difference when querying large data sets (e.g., with dataframe tools).
very nice sir, thank you, may god always bless you with health
Awesome Tutorial, Covering everything with right examples. Thanks man. Keep it up
I have been waiting for a tutorial from axum for so long. Thank you very much! Keep up the good work! :)
Omg... The amount of useful information in this video is incredible!
This is a very well done tutorial. Actually useful and well beyond the standard "hello world" nonsense you usually see. This guy does very good videos.
I have a question / thought. By making the context extractor clone the context result you force the crate's Error type to implement Clone. This cascades into an issue with a real world app using 3rd party db's like sqlx. Because we would probably want to use thiserror and #[from] to convert sqlx errors into your crate's custom error type. If you use several external libraries and want to convert several external errors this can be a headache. I handled by implementing From and using a String as the error date.
Been waiting for this! Thank you!
❤❤❤❤❤❤❤❤ raw content with so much to learn. I have axum servers, but the best practices you show in your videos make you unique
You make your videos with a very good quality, 15 Minutes in and I am subscribed :)
Even it's hard for me to keep up with video learning because my english not good enough but I still love his format of tutorial, that's rarely seen on vid turtorial flooding us everyday.
Keep up the good work and thank you.
The right tutorial at the right time. I'm even watching for the second time 👍👍
Awesome!!! Thanks for the note.
wow. thanks for such a great introduction to Axum. I am moving on to the 3.5hr next.
Thanks for putting putting this together, really appreciate it. I'm struggling with understanding these frameworks and this helps immensely
Perfect tutorial Jeremy! Happy Coding 🎉
cant thank you enough for these vids, you have amazing skills!
Very Good Video, Thanks a lot for this. :) At about 1 hour, the pace got a bit too fast for me. But it definetely helped me in understand rust and axum etc.
Thanks for the kind words. Yes, sometimes the editing might be too fast. I'll make sure to keep it in check for the next ones.
Feel free to check out the latest videos about production coding. Here is the playlist: th-cam.com/play/PL7r-PXl6ZPcCTTxjmsb9bFZB9i01fAtI7.html
On the "Path Param" part, you could have shown as well how to use HelloParams with hello2. For me it was very intuitive, even thought your example is also nice (and probably should be kept). Really enjoying this vid!
Thank you Jeremy for your great guides. I am learning so much from you, and your videos are SUPER well made
Great intro! Thanks a lot for putting in the effort to create this tutorial. I learned a lot and enjoyed the process!
This tutorial is fantastic, you are the best
I loved the idea of request logging and having a uuid per request. It will make life so much easier when tracing down bug reports if we can get the request uuid from the user!
I just implemented your version and did some tweaking so that a uuid is created in the ctx middleware so that it can be used in my tracing layer and set as a tag /attribute on spans for distributed traces! This is a game changer!
Awesome!
Btw, look at the latest rust web app production blueprint. There are lot of other stuff.
You can find the GitHub repository at rust10x.com
I have been slowly watching the series and building along! I’m going to continue watching and learning. But once I push my code I will send a link to my repo so you can check out the open telemetry integrations with the tracing if you are interested! Probably going to be after work tomorrow :)
Cool. If you take a look, you'll see a ReqStamp with a mw_req_stamp middleware. This stamps the "request" with the initial time_in and UUID at the appropriate place, and then exposes it as an Axum extractor. This way, all handlers and middleware can access it.
The mw_response_mapper utilizes that information for the request UUID and even includes time_in for computing the duration. So, everything fits together nicely.
I will probably add open telemetry at some point, but so much to add before that. For us, the RequestLogLine was the higher priority.
Thank God I found you looking forward to learn awesome stuff from you.
Just finished coding along. I learned a lot. Thanks! 👍
Great content, thank you. I am quite impressed at your presentation technique and how your editor and terminal orchestration works well together. Are those special tools you are using or is this just clever video editing? Also, what editor are you using? The multi line editing seems like a neat feature.
Thanks for the kind words.
I am using VSCode with Google Material theme.
ScreenBrush for the line
Davinci Resolve for the editing.
And a custom Mac screencapture CLI wrapper.
Unfortunately, no magic tools, a lot of editing.
@@JeremyChone Nicely done.
Btw, I finally took some time to create the Rust10x VSCode Extension and a webpage with information about my VSCode setup.
Everything can be found here: rust10x.com/vscode
This is such an awesome "quickstart" guide for newbies into rust like me. Question: how do you handle token claims validation (possibly in the middleware) assuming the token is not stored in a db? To what do we compare the token (or the claims it contains) stored in a cookie at log-in? I suspect I might be missing something though.
Next video, June 11, will show full login, pwd encryption, token encryption/validation.
@@JeremyChone Thanks for your kind reply the work you do!
Thanks, amazing lesson. Waiting for the next parts
Thank you so much, this is exactly what I was looking for! :D
In the First Extractor - Ctx section, I wonder, why did you do impl of FromRequestPart for Ctx within the mq_require_auth. It could have been done outside of it as an independent thing too? Would that work?
Jeremy, this is gold! Thank you!
before staring video i want to say thanks
wish best for you ❤
OMG the best video of Axum👍🏻👍🏻
Wow, the timing! How did you know I needed this right now?
Hi, I'm not able to run server and quick-dev on the project side by side. It is giving error Access denied on the exe program of the project.
Yes, there seems to be a problem on Windows. You can move the quick_dev.rs in examples/ and run it from there (cargo watch -x "run --example quick_dev")
Thank you for the suggestion and the course. Looking forward to learn more from you. @@JeremyChone
Excellent!! 🔥🔥🔥
But also very simple and primitive to be honest:)
Would be very interesting to see building chat server with Axum and WebSockets
Yes, it's intentionally designed to be primitive. You can refer to the follow-up video on web-app production coding; it marks the beginning of an actual app.
For more details, visit: rust10x.com/web-app
More to come…
Hi.
I have question.
I cannot run cargo watch -q -c -w tests/ -x "test quick_dev -- --nocapture" because file.exe is already blocked by first terminal and it cannot rebuild it.
Is there any option how to overcome this ?
Thank you for your help
Yes, I heard this and did not hear about a solution. I am on Mac.
Hope a windows user can give some tip here.
Hey, I think we've found the solution. Move `quick_dev.rs` to the `examples` folder, rename the function to `#[tokio::main]`, and it should allow you to do the following:
- In Terminal 1: `cargo watch -q -c -w src/ -x run`
- In Terminal 2: `cargo watch -q -c -w examples/ -x 'run --example quick_dev'`
I am having the same issue here
@@JeremyChone Thanks for the follow up, appreciate it, it works but without the test report, but it's better than not being able to compiled, cheers
@@mumk Yes, and actually, the quick_dev is not about tests or test reports. So, it's better this way. In the next video, we will use tests, but for real unit tests. Everything will fall into place.
Thanks for the course! Terse and all important is covered! I even learn some tricks apart from Axum itself!
This is a pure Gold!!! Thank you very much J!!!
Very good course, greetings from Ecuador
omg im so excited. this is my favorite rust channel!
A big big one coming Sunday June 11th.
@@JeremyChone can't wait!!
@@irlshrek me too... btw, I need one more week. It's going to be a good one, and want to make sure I give the best. (so, Sunday June 18th)
@@JeremyChone take your time! ill wait. in the mean time practicing doing everything you show in this video!
@@irlshrek Note that the ModelController pattern to scale needs to be split between ModelManager (that hold the db pool)and ModelController (that hold the access per entity, e.g., TaskBmc, for Task Backend Model Controller).
Thanks for the course! 8:40 It's a good idea 🙂 Like!!!
Really amazing tutorial : D. Was wondering if you could elaborate on the model controller architecture you used and what it's called? Is it a derivation of MVC?
Thank you. Your timing is great, as I am currently constructing the follow-up course, which focuses on best practices for production code. We'll be dividing the ModelManager (which maintains the model-related application states, such as db_pool) and the ModelController, drawing inspiration from the Data Access Object (DAO) pattern.
In a project management data model, for instance, you might have:
A ProjectBmc (Backend Model Controller) that handles Create, Read, Update, and Delete (CRUD) operations, and possibly more, for Project entities.
Similarly, a TaskBmc would manage the CRUD operations, and potentially additional ones, for Task entities.
This approach gives you complete control over the data structures and access methods in your Application Domain, while still allowing for code reuse and normalization.
Next course is schedule for June 4th.
@@JeremyChone Sounds amazing. Can't wait. Thank you :))
great tutorial like always ,keep up the good work and thank you.
Great Video!
I am wondering how to utiliize snippets like "region" and function definitions that you are so quickly creating? Do you have custom Snippets and hotkeys for VS Code or just utilizing what's in place from rust-analyzer plugin?
Just a VSCode Snippet that I bound to 'ctr+c'
"comment-section": {
"prefix": "cc",
"body": [
"// region: --- ${1:Section}",
"${2}$TM_SELECTED_TEXT",
"// endregion: --- ${1:Section}"
],
"description": "Comment Section"
},
Btw, I finally took some time to create the Rust10x VSCode Extension and a webpage with information about my VSCode setup.
Everything can be found here: rust10x.com/vscode
So glad this video is here. Lets go!
Very good explanations on how to implement Axum and its topology!
This is fantastic. Thank you for this. But could you do a video of Axum with surreal db.
Good point. This might come later.
In the next tutorial, we will refactor the model layer while still using a mock-store (a better one). This will demonstrate how to implement multi-scheme password encryption, secure web tokens, and RPC (JSON-RPC).
Thank you for this so much. It is complex but you make it understandable. Superb Rust content 😊😊
Thank you for a great resource Jeremy! Question: how do you open this "Quick fix" panel for quick imports?
I think they comes with VSCode and Rust analyzer. Then, cmd + .
Whenever you use code assist to import a symbol (add use statement), you should say "Attention! Importing!" out loud. The code assist UI disappears after a split second and one must rewind to see which symbol was imported (if there are multiple suggestions).
Typically, I take a moment to comment when a decision is needed (though I may have missed a few).
For more trivial matters, such as import decisions, I try not to interrupt the flow too much, as these should be straightforward to handle.
I've been following along this awesome course and the first time I stumbled was at 54:35 where you say "and now we just do a 'ctx-question mark'." It seemed weird because ctx isn't passed anywhere, no method is called on it and as I am new to Rust it took me moment to understand what that means.
A Rust-beginner-friendly comment would be that this has the effect of evaluating whether ctx (which is a Result) contains an error and raising ie, propagating it.
Very good point. You're right, a bit more explanation on this one would have been useful.
@@JeremyChone and by the way, your videos are really excellent and fantastically crammed full of well thought details. Merci beaucoup pour ces vidéos!
Oh, and I have of course subscribed and joined your patreon ;)
Hi, thank you for the note and joined the patreon. This makes a big difference.
New cool video coming about building a AI Buddy command line with OpenAI assistant API.
Thank you, great tutorial as always
@Jeremy Chone Why do you not provide a means to set headers in your testing client?
That's a good question. Originally, I didn't aim to delve too deeply into rebuilding Reqwest on top of Reqwest. Instead, I focused more on "browser" use cases, where authentication is typically done with cookies.
However, now I agree that it would be beneficial to broaden the scope and allow the setting of headers.
The route I'm keen to avoid is the full reconstruction of a Reqwest API using the Reqwest library.
My proposed approach is to expose an API like http_client.exec(reqwest_builder), perhaps coupled with some utilities for building these builders.
This approach allows developers to use the simple httpc-test API/Prints, while simultaneously harnessing the full power of the Reqwest request builder.
Does that make sense?
@@JeremyChone Indeed
Jeremy ty so much. I am having problem with test environment. Whenever I use cargo watch for tests/ it says, it cant remove the exe file of server.(failed to remove file.....) Why does such a thing happen?
I think this is a windows thing.
Try to move the quick_dev.ts as an examples/ and run it as
cargo run -example quick_dev
Or
cargo watch -q -c -w examples/ -x “run -example quick_dev”
Hi, thanks for the great video. One question tho, how come the Ctx extractor gets called twice between mw_ctx_resolver and mw_require_auth after we implement the server log line? I know one of the calls is to extract the Ctx for mw_require_auth, but what about the other one?
The mw_ctx_resolver resolves the CTX (essentially performing the authentication) and then stores it in req.extensions, so it doesn't need to be computed again.
The mw_ctx_require is an optional middleware that ensures anything following it has a CTX, even if they don't explicitly require it. This is currently just an optional safeguard because downstream handlers that request CTX will fail if there isn't one. However, it's good to have.
All my rust webdev thus far has been messing around with rocket - this does feel significantly lower level (rocket feels closer to a rails experience), but this does look interesting.
Awesome! Thank you for tutorial
Very good video! almost, such as good this video is not in japan. Appreciate!
Hi jeremy, for next videos you could consider the server sent events implementation with axum, I had problems with the "try_stream!" macro of async_stream when passing custom data to it. Greetings from Colombia, your videos are very good.
Ha, that's a good suggestion. Might not be in the next one but at some point.
Question: Is your use case for Video or Audio streaming?
@@JeremyChone No, actually it was a simpler project. I was using it to display real-time CPU and graphics card information with a browser interface. But you just gave me more ideas for other projects.
@@jorgeosorio1613 ha, cool, yes, make sense.
By the way, for files in the .mp4 or .mp3/.wav/... formats, I recommend using a cloud CDN such as AWS CloudFront to S3. This approach allows you to sign the URL to ensure security and keep the app server out of the way, which is always preferable.
However, when the data is being dynamically processed, you must stream it from the app server.
subscribed! Appreciate the lesson. Is it possible to have a tutorial on deploying Rust apps to a web server? That would make the lesson "complete" 😊
Thanks! The next video will be about leveling up and building the Rust Axum base code for a real application, featuring password encryption (multi-scheme), secure tokens, PostgreSQL/SQLx, and a scalable Model layer.
So, not the deployment to cloud, which will come later with Kubernetes when we will learn how to do multi-service cloud app.
@@JeremyChone thank you! Looking forward for it. I'm a newbie in Rust and somehow deploying to a cloud is a sort of "hello world" for me just to get the feel on how I can use it in a production environment.
also, whats that debugprint tool that youre using?
Ho, just println! with the ->> personal notation. No special tools. Next video, we will be using tracing for those.
However, I still use the "debug print" in between commits to debug quickly. Tracing gets committed and for things that might be useful in production.
I'm in the very beginning of the video writing the first test function but I'm getting an error with my #[tokio::test] and all of the subsequent uses of the "?" operator. Why could this be?
the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`)
the trait `FromResidual` is not implemented for `()`
Do you have -> Result in your test function returns ?
I did not see this error.
amazing course Jeremy!!
Hello, sir. We are using a mutex in asynchronous programming. Will it not create a deadlock if we await after locking, and another request comes in? Since we are working with a single main thread, the lock will not be able to unlock, and the request will not be able to process.
First, I'm not certain if it's a single thread. By default, the `tokio::main` uses multiple threads to run its tasks.
@@JeremyChone okay sir, thanks for the reply.
@sakuraharun00 By the way, I mentioned that "tokio::main" is multithreaded by default, but actually, it requires the rt-multi-thread feature in the tokio dependency.
But because we have the `features = ["full"]` then we had the multithread by default.
Thank you very much for the video! How do you do quick #region?
Code snippet
"comment-section": {
"prefix": "cc",
"body": [
"// region: --- ${1:Section}",
"${2}$TM_SELECTED_TEXT",
"// endregion: --- ${1:Section}"
],
"description": "Comment Section"
},
Nice, it would have been nice to have a branch with the starting state so we follow along
Yes, I could do that. Will think about it for next full course.
Very helpful, thanks a lot for your amazing work 🙏
Hi There, It was a very comprehensive tutorial on axum, but I am stuck at one point, the function where we create mw_require_auth at timestamp: 41:43. I am getting an error
*struct takes 0 generic arguments but 1 generic argument was supplied
expected 0 generic arguments*.
axum::middleware::Next does not any generic arguments, and when I following the video, this was giving me trouble.
Which Axum version you use ?
It seems you are using Axum 0.7, I am must finishing a video to show how to update.
In the meantime, remove on text, and put Request from axum::body::Body, and you can remove the generic on the mw function. Should work.
@@JeremyChone I am using 0.7
@@legends_assemble4938 Awesome! I need to add a card/link to the 0.6 to 0.7 video. Eventually, I might update this course with Axum 0.7 and updated techniques.
Excellent tutorial! Thank You!
I'm new to learning Rust. Does Axum translate well into having a GraphQL (not REST) backend? I definitely strongly prefer GraphQL backends.
Axum is one level below the given protocol. You can use REST, JSON-RPC, GraphQL, or even gRPC with Tonic.
This is the approach I prefer: a web framework that does not try to do too much and stays at its level.
Axum is awesome.
12:58 This seems to be currently broken in the latest axum version. At least for me this fails at runtime due to a false assertion in axum code (version 0.6.15), so I'm pretty sure, it's a bug. I tried axum 0.6.0 and it just worked, even without wrapping "SaveDir.." into "get_service".
Edit: 0.6.12 seems to be the latest version where it works. In all versions newer than that, it fails.
Yes, see comment in the description. There was a bug introduced in `0.6.15`. In the cargo.toml put a `=0.6.12` for now.
Here is the Axum ticket I filed: github.com/tokio-rs/axum/issues/1931, should be released soon.
@@JeremyChone Haha okay thanks, I guess I should have read the description before searching the error myself. Maybe you could add some kind of annotation to the video at that timestamp (If that's easily possible with youtube)? That would probably save a lot of people some time ;-)
This would be a Link Card, but unfortunately, I can't add them. To do so, you need to be part of the TH-cam Partner Program. However, for some strange reason, Google AdSense revoked my AdSense account, which removed me from the YPP. It's a bummer, as I didn't realize I would lose this feature.
@JeremyChone what extension are you using to quickly add those region comments in your videos? BTW, I really love the way you go deep into explaining Rust concepts and design patterns you use. Please keep it up. The community needs more of these type of tutorials to get more engineers to learn and use Rust. Thanks you so much!
Thanks @JeremyChone for the like. I'm going through your videos just now when I saw your feedback came in. Again, what extension are you using for those quick snippets? I really like your style of partitioning your code into regions but can't seem to find the right vscode extension to do it as quickly as you are doing in the videos.
Thank you for the note. I just a VSCode code snippet.
@@JeremyChone thanks again. That's what I thought
Thanks again @@JeremyChone . Added below in rust.json (ctrl-shift-p) and now I can create regions just like you do in your videos:
"Region comment": {
"prefix": "reg",
"body": [
"// region: ---> ${1:reg}",
"// endregion: ---> ${1:reg}",
]
}
@@benbolivar7954
"comment-section": {
"prefix": "cc",
"body": [
"// region: --- ${1:Section}",
"${2}$TM_SELECTED_TEXT",
"// endregion: --- ${1:Section}"
],
"description": "Comment Section"
},
Here is what I have. I aligned the "--- section_name} with white space.
And then, in my keybindings.json I have
{
"key": "ctrl+c",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"name": "comment-section"
}
},
Thank you very much for this tutorial, and how about upload files using axum?
Yes, file upload will be the topic of another video.
@@JeremyChone thank you
Wonderful tutorial!
Something im noticing is that the returning status code is not the same as error IntoResponse status code.
For a failed login it should return 403 but it's returning 500, did I miss something?
Thanks for the kind words.
It does send back a 403 Forbidden if the wrong login. (with the full final code).
Here is how it works:
- The impl IntoResponse for Error creates a placeholder for the real error. - The main_response_mapper will get the error and call the client_status_and_error, which will return the appropriate status code and send that back to the client.
If you go to the quick_dev, and change the password on the login, you will see the login and tickets response with the following status:
Status : 403 Forbidden
Hope this helps. I just tested the code, and works as expected. Let me know if you have any further question.
@@JeremyChone Just pulled the repo and I didnt notice the "error_response.unwrap_or(res)" at the end 😅That makes a lot more sense, thank you!
Hello ! Thanks for this amazing vid too :)
I also have a question with errors i got while implementing my own version of your vid :)
Is there anyway for the error type to get a " from implementation from all types implementing errors " (well, its not clear, lemme explain ahah)
I like to use ? operator, but it often does not work because MyError does not implement From
(like parsing a number for example) and i dont want to have MyError filled with `ParseError(#[from] std::num::ParseIntError)`/... because they are not project's scoped.
I'd like to get somewhat default but still allowing me to see original error
Is there anyway to achieve this ?
Did you try anyhow? It does kind of what you want I think.
High quality video. Thanks
Thank you for this great tutorial. In this vedio, I wonder your vscode plugin setting, especially multi region annotation part..
I put some of my VSCode settings at:
rust10x.com/vscode
There is the comment-section there, and the key binding for it.
BTW, I finally took some time to create the Rust10x VSCode Extension and a webpage with information about my VSCode setup.
Everything can be found here: rust10x.com/vscode
@@JeremyChone thx bro:)
It is very helpful course! Thank you very much for your work!
You're awesome dude!
Hey Jeremy, I'm following along, but I'm stuck at about 21 minutes, adding the main_response_mapper, the Response parameter and return val, need 1 generic param? according to my compiler.
changing Response to Response fixes the issue.
@@popplestones886 Yes, check the latest main branch. I updated it to Axum 0.7.
Thanks for the video! Been helpful getting me up to speed on axum. I'm fairly new to Rust still and I'm trying to figure out why (at the 8:10 mark) when you use the format! macro my response body in the terminal for the test doesn't show the "Hello {name}" but instead shows "==== ." I can see my {name} printing on the server watch terminal but in the test's response body I'm just getting the equals and period (im guessing this isn't something in my code causing those symbols but rather how it's trying to parse it?).
Edit: I figured it out. Not sure the full explanation here but by defining my string as a variable like let name = format!("Hello {name}"); then passing "name" into the Html like Html(name) I got the expected result. I'm curious why it was an issue for me but not you?
I am not sure, the code seems to work well, I see no reason for a ==== popping up.
@@JeremyChone I think Ill try to save the output to a txt file to see if it exists there or if it's just part of the response. It's really strange haha. I got it working and my server log only shows the name I pass into the path but now I'm curious!
Hello Jeremy,
Excellent training. thank you!
Can diesel be integrated to Axum an Tokio for a backend API?
Can actix-web be integrated to Axum? or not necessary?
What is your opinion of actix-web vs Axum? or they are for different purposes!
What would be the best set of Rust tools / frameworks for a backend project?
Thank you!
1. Diesel - Definitely, you can use any db connectivity. Personally, I like lower db lib, like SQLx, but anything will go.
2. Actix-Web v.s. Axum - They solve the same problem, so, it would not make sense to use both.
3. Actix-Web v.s. Axum - I prefer Axum, as it is a newer tech, has a very clean API and app model, and is very well maintained. Actix-Web API looks similar, but I have not played too much with it, as I am very happy with Axum.
4. I have a new video showing pwd/token encryption, database connectivity (with SQLx), and clean architecture.