Modern All Rust Stack - Dioxus, Axum, Warp, SurrealDB

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I wanted to see what it would be like to build a note ticking app using only rest Frameworks and components across the entire stack including the database front end and backend we're going to build this simple browser based note taking application kind of an analog to something like inkdrop or Apple notes you know something that'll help us remember to do important things okay so this is our notetaking app when we first load the page we get this nice little loading spinner once everything's loaded we get a list of notebooks that we can select once we've selected a notebook we can kind of browse through the notes in that notebook and we can create a new note using this button here we'll go to the new note set the title and then we can edit the content here click the save button and our changes are saved we can go ahead and delete that note using this delete button and we can go into an existing note and edit that note we can select all notes and that'll show us every note across all the notebooks and then of course we can add a new notebook by using this plus button right here uh new notebook and then we can start adding notes to our new notebook look let's be real this is not going to be a competitor to obsidian or anything like that but from a developer perspective it'll surface a lot of the patterns that you'd see in a consumer facing app all this is running in the user's web browsers so we need something that's going to be handling the user clicking on stuff creating new notes and notebooks and so on for that I'm going to want a reactive front end framework like reactjs or one of the Thousand other Frameworks that were inspired by it then of course we're going to need some backend apis we need an API to load the list of notebooks on the left and then an API that given a notebook loads a list of note summaries in that notebook an API to load a note and then create update and delete apis for both notes and notebooks I think that covers everything and then the backend needs to store all this stuff somewhere I'm not an expert but I think that's called a database I'm a little rusty at this but luckily in this video we're focused on Rusty choices this video is kindly sponsored by war. so thank you to them for supporting the channel this video isn't about warp specifically but I've been using warp for the past few months and there are definitely some features I found pretty valuable so I'll show those off as we go okay so we want to choose a database that's written in Rust this is a silver platter of some pretty interesting options and surreal DB is definitely one of the most well-known it aims to give developers the flexibility of SQL and graph databases with the scalability of key value stores like redus and Dynamo DB the access patterns of what we're trying to build are well within the realm of what real DB is capable of neon is another interesting rust storage solution it would probably work for what we're trying to build but it's actually not a full database solution it's more of a storage layer that allows postest to scale more easily which means that it's not really entirely in Rust so I decided that it doesn't quite fit the criteria of the video then there's something called Tai KV and tikv is a key value store that aims to provide automatic data migration when database nodes are added and removed to me it sounds like it's aiming to offer a lot of the benefits of ads Dynamo DB but in an open source project entirely written in Rust that sounds pretty compelling then there's Miley search excuse me it's actually called melee search you might have heard of elastic search which is a popular way to get full teex search in a way that's Ultra scalable melee search seems kind of like a rust equivalent of elastic surch but it's not really intended to be a durable data store meaning it's expected that your primary data store is somewhere else as you could lose the entire contents of your melee search store at any time that said it's not my intention to productionize this project so for the purposes of this video that's not necessarily a deal breaker there is a vector database written in Russ called quadrant and Vector databases are useful for what's called semantic search which we're not going to be doing for this project so we'll go ahead and rule that out semantic search is a search where the result results are based on the meaning of the Search terms not necessarily the exact matches of the Search terms if we were to take this thing further semantic search actually might be a nice feature but yeah outside the scope of this video okay after all that I decided on surreal DB you could make the case that it's kind of The Golden Child when it comes to databases built in Rust it has some pretty compelling narratives around both query flexibility and horizontal scalability because usually you have to choose one or the other you usually don't get both at the same time there are definitely some open questions around its performance and at the time this video is being made it's not officially production ready but we can afford to go bleeding edge on this project plus real DB has significant investor backing so we can be relatively confident about its future there's also a pretty nice graphical interface for poking around in your tables and manually executing queries we'll look at that more toward the end of the video now we have to choose a framework for running our HTTP server that's going to expose our backend apis and serve up our static files that contain the front end there are quite a few options here and some of the most popular ones are rocket poem axom and actic web and then we have some choices for our front end framework and those include U leptos dioxis Sycamore and many others leptos and dioxis also happen to be full stack Frameworks so they can come with a backend built-in lately I found it increasingly hard to justify not using a full stack framework so for this project we're going to go with dioxis now most people probably wouldn't normally consider their terminal emulator part of their Tech stack but warp is a fully rust based terminal so in this video I'm including it as part of the stack warp is the only terminal application I know of that forces you to create an account and log in before you can use it look I don't I don't want to have to log in on my terminal application by the way thank you again to warp for sponsoring this video but let me break this down as simple as I can I don't want to have to log into my kitchen refrigerator either that would be terrible but if this refrigerator automatically shops for groceries and does all the cooking for me I'll memorize a 100 character password if you want me to I'll wake up in the middle of the night to re-enter my credentials once you become spoiled with some of the features of warp you start to find it hard to go without those features and we'll cover some of those features throughout this video look nobody really wants to log into their terminal and the warp developers really ize that but it opens the door to some really nice features that you wouldn't be able to have without logging in we'll get to some of those okay to walk through how this thing is built we're going to start with the back end and sort of work our way to the front end one interesting thing about seral DB is that it's really flexible in terms of where your data actually gets stored to start seral DB using an inmemory storage layer which means all of your data is going to be completely wiped when you restart seral DB you can do surreal start memory and you get surreal DB running using an in memory storage layer if you want to use a file as a storage layer you can do surreal start and then file colon and then some name. DB so now my data is going to be durable and it's going to be persisted to this DB file obviously if you're running in production you're probably not going to use a file as your storage layer you can use a tyv cluster as your storage layer so to do that you do sural start and then Ty KV and then IP import that's outside the scope of this video and then if you want to put your data on the blockchain you can do surreal start and then blockchain I'm just kidding you can't do that you can't put your data on the blockchain for now we're just going to do seral start and then we're going to store our data in a file so we'll do notes. DB okay so how do we run this thing well dioxis actually has a dedicated command line tool that you can install with cargo install dioxis CLI to actually run our web application and get hot reloading we have to execute this long verbose command so we'll do DX build and you can see warp actually autoc completes based on my command history so that's really nice so I just do right arrow to fill out the rest of the command and then enter and I'm running the command that came from my history the other cool thing about warp is that the command that you ran actually kind of floats at the top and this is configurable but by default if you didn't want this you could actually disable it and have it work like a traditional terminal but by default the command that you run actually floats kind of on top here so regardless of how long the output of the command is you still see what command you ran I find that kind of useful and I'm hard pressed to think of a scenario where you wouldn't want that it's my personal preference to have this and then this is what's called a block the combination of the command that you ran and the output so I can cycle between blocks by doing command and the arrow keys and each time I go to a different block it shows me both the command that was run and the output again irrespective of how long that output was so in an traditional terminal emulator you might be kind of waiting through tons of out output to find the commands that you actually ran that were associated with that output so this is really nice for easily searching through your command history and correlating which output came from which command the other thing I want to point out because it's really important to me is that warp supports Vim motions at the command line so I can do Escape BBB to go back DW to delete a word u to undo dollar sign to go to the end of the line capital A to go into insert mode at the end of the line so on and so forth this is not unique to warp you can do this in other terminal emulators but I wanted to emphasize that warp does support this because for me it's important and probably some other folks too all right before we start implementing our server functions we do need to establish some models and here we have our note model it's got four Fields ID title content and notebook ID is actually an option because when we're first creating a note we don't want to generate an ID we want to allow seral DB to do that for us so on Note creation we actually pass in none for ID and then subsequently when we retrieve notes that ID should be populated by surreal DB title and content are pretty self-explanatory and then notebook is the ID of the notebook that this note is associated with notebook is pretty similar again it has an ID that's an option of a string and then it has a name and then it has a field called count and count is an option of an unsigned integer and that's what we're going to use to represent this number of notes that is displayed uh next to the notebook name obviously when we're creating a notebook we're not passing account to the server that field is only going to be used when we're calling get notebooks and getting a list of the notebooks and the associated note counts for each of those notebooks so that's why it's an option because it's going to be none every time we submit a notebook to the back end now that we have our model set up let's look at the server functions okay the best feature of rest fullstack Frameworks in my opinion is server functions and I've talked about these a lot in previous videos but long story short this is where we're going to actually make our queries to the real DB instance for retrieving data we have get notebooks get note summaries and get note and then for inserting and updating data we have upsert note and upsert notebook and then of course for deleting we have delete note and delete notebook now let's go ahead and add the implementations now that we have our implementations let's try running this we got an error use of Undeclared create or module surreal DB cool feature of warp we can select a block that we got an error on so we'll select this block here and we can do control shift space and we get this AI prompt with the output of that command pre-populated and then how do I fix this and I can just press enter and it's going to give me a response to fix the issue mentioned the output you can follow these steps I have some warnings here that it's addressing but for number three you can see the error message fail to resolve Undeclared crate or module sural DB indicates the compiler cannot find the sural DB crate this is a little contrived it's from the error message it's pretty obvious what the problem is but you can see the value here you can definitely get a lot of cryptic error messages When developing especially in Rust and using rust full stack Frameworks so this is super handy to have let's talk a little bit about how we're going to add this seral DB dependency little background here here is that with full stack Frameworks like leptos and dioxis there are different features enabled depending on whether we're building for the client side or the server side taking a look at cargo. tomomo we can see we have a feature called SSR and that enables all the features that we specify here in these square brackets so we can enable and disable optional dependencies so when you add a dependency to a project you'll need to think about whether you want it on the client side server side or both if you need it in both places you can just add it with cargo ad so I could do cargo ads real DB but we can't do that because we can't have the seral DB dependency on the front end so what we need to do is add it to the list of dependencies as optional similar to how async once is and then we also need to add it as a feature to our SSR feature so we need to put it in this list of features inside these square brackets this is something that's right for Automation and I'm not 100% sure yet but I think this might be my favorite feature of warp I can actually make what's called a workflow to create a workflow I can do command shift H and I can give it a name and a description and then I can enter the command that I want in this case I'm going to do this crazy command it's cargo ad and then crate name so I have an argument called create name and I put that in two places in my command first this is going to run cargo ad and then add the crate name which again is an argument if that succeeds it's going to call said outside this cope of this video what said is but basically it's going to insert that feature in that list of SSR features that we saw I could give this a name I could do like add SSR or something like that and then I could give it a description but check this out I can have the AI autofill the name and description so I just click this autofill button boom add an optional dependency to a rest project and update cargo. toml the AI is interpreting what it is and giving us a title a description automatically that's pretty cool so I'm going to do save workflow so now to run that workflow we just created we can do command p and then search for it add optional dependency it's going to walk me through the arguments that the workflow requires so we can see create name here I can just do real DB press enter and it's added the dependency and ideally added it to that list of features as well let's see if that happened yep there it is so we have it here as an optional dependency and then we have it in this list of features associated with the SSR feature cool that's workflows now that we have our dependencies set up let's Implement those server functions the first thing we need to do is set up a connection to the database and we need to have this connection sharable across all of our server functions so we don't have to reset it up every time a server function is called we can do this using lazy static which allows us to create a static variable that gets initialized the first time that it's referenced this code won't be run immediately when the program starts it's going to be run the first time this DB variable is accessed the seral DB setup functions are async so to set up this static variable we need to use async once and then we pass it the async logic that needs to be run we create a new surreal connection call a wait on that and then Panic if it doesn't work and then we specify a namespace and DBT use so all in all this gives us the ability to reference this DB variable in our server functions and perform whatever database operations that function needs to perform let's take a look at upsert note upsert note takes a note as a parameter first it's going to get the connection from that DB variable that we looked at earlier if the ID field is sum that means we're doing an update and if it's none that means we're creating a new note so that's what these two if Els branches represent in both cases we need to bind the values of the note that got passed in to tokens in the query string and we do that using the bind function and passing in a tuple the first element of the Tuple is the token that we use to represent that value in the query string and the second value of the Tuple is the value from our note structure and then in the query string to reference one of these tokens we just prefix it with a dollar sign so we can see we we're binding notebook to note. notebook and then in the query string we have dollar sign notebook right here and we are casting it to a thing thing in surreal DB terms is like an ID of something in our note object it's a string or an option of a string so we need to cast that to a thing and then of course we set the title and content fields to whatever was in the note that got passed into the function the create branch is pretty similar but there's no ID to worry about because we don't have an ID yet we just create the note on the back end serial DB assigns an ID to it and the next time that note is retrieved that ID field will be populated get note is pretty similar it takes a note ID as a parameter in the query string we do need to cast that note ID to a thing because that's what surreal expects so you can see that here and also notice that we don't specify a table we just specify an ID because in surreal the table is built into the ID of the record the ID of a note will be note colon and then the random identifier so that's kind of Handy and then because we're deserializing this into a note we do need to cast the ID in the notebook to a string because the note struct has strings not things once you understand those two server functions the rest are very very similar those two cover most of the concepts the main function is where everything kind of starts from and there's not a whole lot here we're just calling this launch Builder new function and passing in uh the app function to it launch Builder new is part of the dioxis framework the app function is actually where application logic resides it takes a scope as a parameter and returns an element all dioxis components kind of follow this pattern an app is just basically a top level component that holds all other components in the beginning of the app function we set up all the state that we're going to have to keep track of and there are four main things once you're understand these 12 lines you'll pretty much understand how the front end works first we always need to maintain a list of notebooks and then two if the user has selected a notebook we need to maintain the list of note summaries for that notebook three we need to track which notebook the user has selected if any and then four we need to track which note the user has selected and that's going to dictate which note actually gets displayed in the main pane if you've ever used reactjs you're probably familiar with used state in dioxis the concept is pretty much the same it allows you to set up some state that will be automatically rendered on the page whenever it gets changed we have two pieces of state that we want to track that don't come from the server one is the currently selected notebook and the other is the currently selected note we need to know both because we want to visually highlight the selected notebook and load the note summaries from that notebook and then obviously we need to know the selected note because that dictates what note gets loaded and displayed on the main pane on the right for the other two pieces of state that we need to maintain the list of notebooks and note summaries some data needs to be retrieved from the back end and for this sort of state where some async operation needs to be executed to get the initial value the used future construct is a good fit the third parameter of used future is a closure that performs the async operation that you want to use to populate the state in the case of notebooks that's just the get notebook server function that we wrote earlier which is pretty nice not summaries is a little bit more involved the second parameter of used future is a tuple of states that the future is dependent on if a dependent value changes the async operation that populates the future needs to be rerun so by specifying the selected notebook as a dependency of the note summary's future dioxis noes to reload the note summaries whenever the user changes a selected notebook we use the state of the notebook's future to kind of determine what to render in the case where the future is completed we'll render that notebook bar because we probably retriev that list of notebooks from the server and then if they've actually selected a notebook we're going to render the column of notes contained in that notebook and we're also going to render the the note view which is where the note is displayed when they click on that if the user has selected a note we'll show that note in the note view pane here if the future is still pending we're going to just display a loading spinner basically we do want to make sure if the user selects a different notebook we want to reset that main note pane because if they if they have this note selected and then they select a notebook book that that note is not in it wouldn't make sense to display a note that's not in the notebook that they've selected that's what this use effect here is for so what this means is whenever selected notebook changes we want to call do set To None on selected note that's all it is those are the sorts of things that use effect comes in handy for all right let's take a look at the notebook bar component you probably saw we referenc other dioxis components in this render macro notebook bar notes bar and notes view let's check out notebook bar dox's components are all an with this component macro um other than that they're just normal rust functions they take a scope as the first parameter and then for subsequent parameters they can take whatever you'd like to pass in from the parent component so in this case in the case of notebook bar that's the notebook's future and the selected notebook both crucial for Notebook bar to have access to what we're going to show in the template is going to depend on the list of notebooks of course we only want to render the list of notebooks if we receive the list back from the server which is what is tracked by the notebooks use future that we saw earlier and if the value of the future is an okay result of a vector of notebook wrapped inside a sum option that means we're clear to render the list of notebooks otherwise something went wrong which is handled by this other match arm down here we can put a for Loop directly inside this div element which is really nice so inside we can iterate over the list of notebooks and create a div for each notebook and then have various things inside that notebook including an onclick attribute which calls selected notebook. set on that particular notebook so you can see that here so each run of this for Loop essentially represents a row in that notebooks bar column so work is an iteration of the for Loop Russ is an iteration of the for Loop and each one of these is a div and in that div you have the name of the notebook and then the counter counter is just a tiny component that displays that number that you saw indicating the number of notes in that notebook this block right here represents the element that needs to be displayed played after the user clicks the create notebook button so if i' go to if I click this plus here we see this input field that's that element so that's only rendered if we're in a creating notebook State we can see we have an onkeydown attribute on that input element if the user hits enter it's going to call this submit notebook function and if you look at this submit notebook there's a lot going on here but you can see these two own functions without going into too much detail it took me a long time to realize that I needed these two own functions here so to show you the error that I was getting let me delete these and see what the build says CX is a reference that is only valid in the function body not the most helpful error message so say I want to get help on the dioxis Discord server to try to figure out what's going on here warp has a really nice feature called shared blocks instead of copying and pasting all this output and the command and everything to and pasting it into Discord I can just do share block or share selected block create link and then I get this nice link that I can paste into the Discord server and they get everything that I saw they get the command that I ran all the output including the original syntax highlighting this would be even more useful if the output that you had to share was just really massive and it wasn't realistic to paste into a Discord chat sural DB also has a pretty good web interface I'm pretty sure it's not created by sural DB themselves I think it's created by a third party anyway so we can browse through the tables uh click on records uh or we can perform arbitrary queries so this is kind of Handy by the way if you're into themes check out all these themes that warp has I personally like the default one which is called cyberwave it's got this nice gradient black to Blue but yeah there's something for everyone here fancy Dracula nice all right that is the draw stack and a deep dive into some of the areas of the code overall I'm pretty happy with how it turned out of course dioxis is still in a pre pre 1.0 State and sural DB does currently have some questions around production Readiness but once these projects gain a bit of maturity this is a stack I could definitely see myself building business critical applications with if I were to productionize this I think the next thing I'd look into is integrating sural DB with a Tai KV cluster I'm guessing maybe that's a topic for a whole video let me know if that's something you'd want to see thank you again to warp for supporting the channel definitely check it out if you want your refrigerator to do your shopping and cooking for you I mean if you want to have a more modern terminal that can make you more productive let me know in the comments what you think of this Tex tack do you like it as is would you swap out some of the layers with other things or would you use something else entirely let me know if you are interested in fullstack Rust Frameworks definitely check out this video where I use the lepos framework to build a chat GPT clone thanks for watching and we'll see you in the next one
Info
Channel: Code to the Moon
Views: 55,612
Rating: undefined out of 5
Keywords: neon, postgres, surrealdb, warp, axum, actix, leptos, dioxus, rust, full stack, qdrant, meilisearch
Id: Pr6T0Phjvgc
Channel Id: undefined
Length: 24min 1sec (1441 seconds)
Published: Tue Feb 27 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.