Observable Flutter: Code sharing & Postgres

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
CRAIG LABENZ: Hello, everybody. Welcome to the newest show on the Flutter YouTube channel, Observable Flutter. Starting out, I'm your host, Craig Labenz. And today, we're going to be talking about some server side Dart stuff. But before that, I just want to get into a little bit of the spirit of the show, what we're going to be doing. The first idea that I had many months ago that kind of led to this path was around all the samples that the Flutter team and dev rel team have. And even just looking at those samples myself, I thought they were a little hard to really absorb and use. And I felt like the community could stand to benefit from some videos that walked through some of those samples, and maybe one day Code Labs too. So this is kind of the first product that's coming out of that idea. We're going to walk through a code sharing sample to cover how to use some of your logic, both on the client and the server, and also connect to Postgres at the end, if time allows. So let's get into it. So first up, a little code of conduct for us all. Oh, by the way, yeah, just so everyone knows, I'm going to be trying to watch the chat and interact. So be sure to ask questions. And if I miss one, hopefully the folks behind the scenes will help point it out to me and remind me about it. Yeah. Hello, everybody. So a little bit about the code of conduct, we are all, this whole operation, is under the Google YouTube terms of service and code of conduct, obviously. We are also going to be adhering to, of course, the Flutter code of conduct. So strict requirement for deep respect for each other, for, certainly, all open source contributions from the community toward authors, everybody. We're going to be debating different things on this show. And if one of us doesn't like, prefers a certain package over another package, we're going to always try to talk about that with deep respect for the package, even that we don't prefer. So this is very important to me, and for other technologies as well, things that we don't use because we use Flutter, always deep respect for everybody. And with that, let's start. So how are y'all doing? Today-- I come from a web background. Before I found Flutter, I was a web developer, and actually a backend web developer, not even a primarily frontend developer. I did some JavaScript, certainly. But I was a back end kind of database guy before I found Flutter. And so near and dear to my heart is what goes on the server. And obviously, being enthusiastic Flutter developers as we are, what goes on the client is pretty much a solved problem. We all like to use Flutter for everything that it's applicable for. But that doesn't really imply anything about what you do on the server. You can obviously use Firebase, great technology. You can use other languages. I still, for the most part, in my for fun development, actually still use Python and Django on the server. And that's what I did for a decade before I found Flutter. And I still like those technologies. But boy, I really find myself missing the type system of Dart, and just the general ease of both development and deployment. Deploying a Dart app to the server is actually quite a bit easier than deploying a Python app. So I miss that a lot, honestly. So this is very important to me, even just for my own stuff. So I wanted to talk about where the Dart community is at with that and explore this together. When will we be able to do SEO on Flutter web? That's a really, quite a good question. I know the web team is working very hard on that. SEO for Flutter web is an enormous task. But they are thinking about it all the time. So my fingers are crossed with you. That's definitely, that will be a big win when that arrives, for sure. OK, so this is the code sharing sample. And I worked on this recently with Kevin Moore and Brett Morgan over the last, well, we can see here our last commit was about a month ago. But we were working on this in the fall. And it's a very simple front and back end, obviously both using Dart. Front end is Flutter. And they just share some data models that are generated with freezed and allow for really simple communication back and forth. And what I want to build over this episode and maybe the next episode as well, depending on how quickly we move, is that again. So we can kind of all watch it be done. And then hopefully the sample here will make more sense. But then I want to go a little bit further and start connecting to a Postgres database. And potentially, we'll even wire in Dart Frog, the great server side library from Very Good Ventures, and see where we can get. I'd like to even maybe have a primitive authorization system, where requests are automatically authenticated if they bear the correct password or header or however we wire this up. So that is the goal for this and next episode. So let's start. All right, what window am I on? Here we go. Great. So I'm in my development folder here. And we're just going to make a new directory that isn't going to be generated by Flutter Create or Dart Create, because this is going to be the parent folder for two sibling projects. And actually, it'll end up being three sibling projects by the time we're done, because we'll have our client, our server, and then the shared code. So let's make a directory. And we'll call this, how about code sharing? We don't want an underscore. That'd be crazy. And now, let's open this up. Of course, it opens it in the other window. Come on. I'm literally using this one. Yes, I do trust the authors of this folder. That's important. All right. So we're going to start with a simple-- actually, I think we should start with the shared directory, because that is going to be an immediate dependency of both. So we'll start with a pure Dart package. And yeah, that is where we're going to start. So to remind myself how to do that, because I never remember any of these kind of things, we're going to say, Dart create new package and let's look at what the command is here. Great. Dart create package, package name. Love it. So back in our terminal, we'll say a Dart create package. What word did I just delete? Oh, literally it was package, of course. And then the package name, we will call shared. And now we have our new directory. So I'll open up shared pubspec.yaml. And the first thing that we're going to do here is add freezed and build runner and all those things. So our first dependency will be freezed annotation. And I don't know the latest version of that, so let's grab it. pub freezed. Does this search thing still work? Oh, there we go. Oh, no, we Google searched it. All right, 2.3. And that's probably-- I think he always pins freezed and freezed annotation to be the same version of our-- oh, let's see, annotation. Oh, 2.2. What do you know. All right, so 2.2.0. And we do this. OK, so that is good there. Now, dev dependencies, this is where we actually add freezed. And this was 2.3.0. Looking over at the chat. Good to see you all tuning in. Hopefully you find this helpful. All right, next we're going to need JSON annotation. Actually, I should be using the add dependency extension in vs code. So I'll add a dev dependency of JSON. No, JSON annotation is a regular dependency. So this needs to be JSON serializable, because I typed dev dependency. So this will grab JSON serializable. I'm pretty sure that's right. Let's double check real quick here. JSON serializable. Installing, there we go. It is a regular dependency. Oh, right. I still think it's a dev dependency, but we'll find out in a moment. Add regular for JSON annotation. Pretty sure this is how this goes. And then we're also going to need build runner for dev. And this may be sufficient. Let's see here. If I go now into my lib directory and in source, instead of shared base, we're going to change this to just be our models that we need. Now, the first question is, what is the model that we need? And I mentioned that I want to have an authorization system kind of baked in to Dart Frog that we're eventually building toward. So Dart Frog has this idea of a context, where you can get your hands on global resources. And I want to build that and have it just kind of automatically available, that middleware we write in Dart, Dart Frog middleware, is just going to kind of handle all that for us. So I think a user model is a pretty good place to start. So let's-- I used them in a freezed snippet. Do I still? I seem to not. All right, so we're going to need to import freezed annotation. Oh, I think the reason this isn't helping me at all is I need to run pubget. I need to save the pubspec file. That's critical. And are you doing it? We're in code sharing. And we need to go to shared and dart pub get. All right, much better. So now we can create a freezed class. And we'll call this user. And we'll type the sacred freezed words of user. We add in this mix, and that doesn't exist yet. And then we need to make a const factory constructor. This is-- we're just casting the freeze spell right now. So this is const factory user. And we could name it if we were going to have different types of users, which could potentially be valuable if we had-- we might even have an unauthenticated user that is a type of user. That's actually kind of interesting. Maybe we call this-- I don't know. We'll cross that bridge when we come to it. For now, we're just going to have a user. And this points to another constructor that doesn't exist yet. And in here, we finally get to put the parameters that will exist on our data class. And we're going to need an ID. But I think it should be optional, because the user might not exist yet when we're creating them. And we'll want the database, because remember, we're talking to Postgres, we might want it to generate the ID. So in that case, let's just make the ID optional for now. And then we'll have a user-- should we have a username? Maybe an email. That will be nice and easy. All right, so a string, and this one will be required. OK. Now, we're also going to have to think about our password at some point and how we handle that. But that's kind of downstream. Then you're really getting into building a web framework. So we have a very basic-- oh, and then we need our from JSON thing. We need to actually activate JSON serializable by telling it that we want that constructor. So let me just search for from JSON here in the freeze documentation. Yep, here we go. And this is the line that we want. Wunderbar. Wonderful. Except it's not a response. It's a user. All right. Now, let's run build runner and see if this is working. Someone's asking for the VS Code theme. This is called Synthwave 84. And it is pretty delightful. All right, I typed something crazy. And that was that the email parameter wasn't required, even though it obviously had to be. I think that's what it was. Every Flutter have dash as their wallpaper. That's true. You are not wrong. OK. So what does it say in here? Oh, the part file. Yes, of course. One thing that I always find whenever I'm using freezed, love it as I do, is-- and this is no fault of freezed-- but I'm just continuously forgetting to do one of the things. And that is what I think is going to be so exciting about macros, should the team ever decide that they're definitely the right idea, because they're going to be all this build runner experience without so much tedium and things that are easy to forget. All right, so we need a shared base, we still call this. I thought I renamed it to-- let's rename it now. We'll call it models.freezed.dart. And we'll rename this file to models. Indeed, whatever thing you were just asking about, do it. All right, great, yeah. Love that. OK, now we'll try to generate again. I think three-- oh no, I'm still wrong. I was going to say three is going to be probably the fewest runs I've ever needed, I've ever required to make this work. And I didn't get it, because I forgot to put G here, which is required to actually use JSON serializable, because this is the convention for JSON serializable, and this is the convention for freezed. And freezed, smartly, uses JSON serializable for that functionality. So now we'll build again. And we're not seeing the freezed or G files over here, simply because I've hidden them. But if we search in source, where are we here? I went in lib, right. We see that they do actually exist. I just am very tired of looking at them myself and having them clutter up my sidebar. Oh I said, hey look, they exist. But I'm covering that spot. So here's the proof. Look at it, right in the pudding. OK, get back in the corner, you goof. Can you explain today's topic? Yeah, absolutely. So we are working on some shared logic between the client and server. I was a backend dev for a long time before I found Flutter, backend web developer. And so server side stuff, databases, very near and dear to my heart. And they complete any development story. Obviously, we like to use Flutter on the server, or on the client. But what we use on the server is still a largely open question. There's a ton of great options. And one of the kind of least popular, because it's still so new, is to actually continue to use Dart on the server. And so that's what I'm talking about here today. We're beginning here with some shared logic that we're going to be able to import into our Flutter app and into our backend server app. And then for extra credit, I'm going to try to connect to a Postgres database. And I'm just going to be running that locally right now with Docker, because that's super easy. There was another question I saw here. I know Postgres-- oh yeah, auto increment, yeah, unless they use UUID. I think auto increment is honestly probably fine for users, because there's just only so many people on the planet. Things like likes and different interactions, that there can just be an enormous amount of, then an auto increment can get a little bit wonky. And maybe a UUID is better, or even just UUID ultimately does become a string in the database. And I haven't yet decided which one we're going to use here. But I know that this data type, even if we had a number in the database, we could always just kind of stringify that. And this will mean that whatever, we're set up to separate how our database actually implements its logic from what we do in the client, because this will handle either. Even if we go number that we might think, well, we should have gone number in our user class. But we might one day outgrow that number. Or we might want to consolidate and have the same ID type across all of our models. But other models require that UUID. So we're just going to use a string the whole way through. All right, so we have the simplest class the world has ever seen. But the exciting part is that we're ready to actually use it on the client and the server. So let us now change. Let's go back here, and we'll say flutter create. And this will be, simply, the client. And the one downside of using these generic names, oh goodness, I'm being shamed in front of the whole world for not having updated to the last version of Flutter. Do as I say, not as I do. Let's open the client's pubspec file. I've lost track of whatever I was saying before that. Oh, I need to scroll down here. Why is the ID optional? Great question, Adam. Because I can imagine we might have some user objects that I need to create, either on the server or on the client before they're actually saved. And if the database is going to be producing, is going to be giving us this ID, then we just won't know it yet. Now, the other way to get around this would be to have an authenticated subtype of user and an unauthenticated subtype of user. And that may end up being distinctly superior. And if we do that, then the ID won't need to be optional in the authenticated user variant, because it will obviously already have gotten its ID from the database if it's authenticated. So this is still very much a work in progress. But I just kind of want to, once I get to basically the server side part, then I think it'll be more clear what is actually better. All right. We're back. So the first thing that we need to do in the client is actually import this shared library. And actually, that's the second thing we need to do. The first thing we need to do is delete all of these comments. Now, some of them are important. I'm not going to delete them all, like the one that tells me not to remove publish to dot none. That one's good. All right, but we'll get rid of the other comments. Doo, doo, doo, doo, doo. All right, holy smokes. Look at how well-documented this file is. It goes on forever. There we go. OK, so now back in our dependencies, this is where we can add the shared code. And if my memory serves me, we have to give it a path, which involves leaving the current directory, so out of our client folder and into our shared folder. And I think this is going to be sufficient. But let's find out. Oh, there's still an example folder in the shared directory. We'll have to delete that. I suppose I could do that now while I talk about it. Here we go. I'm so used to looking for Move to Trash on Mac OS, that now that some things actually say Delete again, the obvious thing, I can't find it. We need to create project no comments. Use regex to remove comments. Yep. I always delete those comments. Roman, hey. I set a reminder for your stream yesterday. And I got that reminder when you were done. And I tuned in, and I was like, why can't I chat? Why can't I say hi? And it was because you had just finished, so that was very disappointing for me. But congrats on launching your game. That's incredible. Well done, Roman. Really, really, really cool. I removed all the comments, simply because they were kind of cluttering it up. And I didn't want that. So we have our-- let's see here. Where is-- going to hide or collapse some of these folders here. Client, here we go. And the Lib directory, this is the one that we want. Great. So now, let's maybe make a user and just print their name to the screen. And then we'll kind of already be ready to switch over to the-- switch over to the server. I think I said server there at one point when I meant client. We'll switch over to the server and actually think about authenticating that user correctly. So let's just-- do we have any stateful widgets in here? Stateless. Yeah, of course we do. It's a counter app. Oh, here we could use the regex, yes. So we want to get rid of slash slash, and then any character and any amount of those characters. And we have to turn on regular expressions. And we see now that it highlights all the things. And we also will get rid of the new lines at the end. Otherwise, we'll just have a bunch of blank space. And we'll replace all of that with nothing. And we run the regular expression, and they're gone. And then our file is poorly formatted, so we press Save and let the computer do the work. Roman, you have 1,000 times more experience than me on live streaming. So whatever notes that you take, as soon as you're done, grab your lighter, set that piece of paper on fire, and watch the ashes blow to the wind. I am the beginner. You are the sensei. All right, so we're going to make-- yeah, here we go. Here's our stateful widget. And let's just create a little user object here. User user, and we, of course, have to import this user. And it should work. And it isn't. Wonderful. I love it. Let's see if we can type shared and shared. I can't. All right, good stuff. So in our client pubspec.yaml, it thinks that we have shared here. I wonder if this is because my VS Code window is open to a higher level, and it's confused about the project scope. I don't want this to be true, but it might be. So we're to open a new code window in the client. And we'll see if it thinks that this is going to work. Why do I like VS Code more than Android Studio? For me, that is just because I kind came up on the old text mate Sublime Text route. And this just feels the most similar to those. Oh no, this still doesn't like it. OK, this is good, actually, because it means we're going to be able to just use one. We'll just put print the user ID here, I think. We're going to be able to use one window, once we get it working at all, and then we don't need this. All right, so we just have to figure out why this doesn't work. So the URI doesn't exist. It feels like it does, because the pub spec file-- lets look at our pub spec lock, and let's look for shared. Oh, it's not here. Did it not run? Hmm. Flutter, pub get. It didn't run. I thought it ran. Oh, we're still-- yeah, we're in the client. And this is adding-- wait a minute. I didn't add freezed annotation or JSON annotation. Oh, it just copied them in, I think they're transitive, from shared. OK, this makes sense. Now we go back here. Do you work now? You do, great. See? Never in doubt. Yeah, we just have that build error, of course. Well, we can close this window and just go back to our single window. And we will actually create our app or our user in initState. They'll be late, because most users are late. Most people are late, honestly, if you think about it. So they are going to have an ID. And it's going to be ASTF. And they're going to have an email that is someuser.com. And that can be const. This is highly unrealistic, but the linter will just bark at us. And it wants us to call initState, the super. You're rarely going to have const users in your actual app. But the linter is pushy. All right, let's run this. This is literally the lamest thing anyone has ever made. But it's going to be good. I'm excited. And then we haven't pushed the button. So that's not a thing. Anyway, let's go into the client directory. And we'll say Flutter run, and we'll use Mac OS, because it is so much lighter weight than any of the emulators. Which ORM to use for Dart on the server? What a good question. Well, as I was researching, I found a package called storm berry that I have not played with at all. So in the spirit of The Boring Show, where we go in blind and we just hope for the best, I'm going to try to use storm berry. And I don't know how well it's going to work. We're going to find out together. All right, folks, this is the simplest app anyone's ever made. But we're on the way. So I will now leave it running and create a new terminal window. And now it is time to get started with Dart Frog, the installation-- well, I've installed it, but the bootstrapping instructions of which I never remember. So let's look at Dart Frog. Here we go. And I've already activated the CLI. And we create a project. Those fellows [INAUDIBLE],, they made it so easy that only I could forget. So Dart, what was it? Oh, it is just dart underscore frog. Gosh, so good. dart frog create server. Can't load kernel binary. Well, it seems to be working. RIP to the kernel binary. Oh, OK, well we can update. I wonder if that's the problem. Here's your problem. Can someone say Dart Frog? Someone did. Yeah, someone did. And we need to find that person, ask them what they're up to. OK, so now let's open. Actually, let's go back to the things and see. We run the thing. So this is kind of all we need. This is the equivalent of Flutter Create for Dart Frog. This gives you the kind of most basic stuff on the server. And then Dart Frog dev is kind of the equivalent to Flutter Run. And Dart Frog Build would be the equivalent of Flutter Build. I know you didn't see that coming. So we've run this one. And now we're going to run Dart Frog dev and actually see it working. So Dart Frog dev. Good, already broken. The current version of the CLI-- right, I shouldn't have generated the project off of stale CLI. And luckily, I've done no work. So this is very easy to rectify by pretending that I never did it in the first place. Did I update Dart Frog? I did. Wait, I wrote dart frog update to update. Was that what it told me to run? Did this work? Is that a coherent command? Oh, it is, amazing. I'm interested in how this syntax came to be. Maybe update is a magic word for latest, and this is the command, and this is joiner word. OK, VGV is here. So they may be able to let us know what was going on there. So we're going to run dart frog create again. And it's called server still. And this time, it should run. Dart Frog runs every other time I use it. So I think it's going to work. Server and dot frog run-- no, dev. I have the memory of a goldfish. I look at the documentation. I switch back to my terminal. I go, what was I supposed to type? All right, yes, allow incoming connections. This is a web server, after all. All right. And then, oh, it probably tells us in here. 81, 81, no that's the-- oh, here we go, 80, 80. Ah hah. OK, it opened it in the wrong window of course, because always. All right, we're in with Dart Frog, and we're ready to begin. So the next thing that Dart Frog will need is our shared library, of course. So we'll open server pubspec.yaml. And we will add, as a dependency, shared, which comes from the path of up one level, and shared. All path dependencies must be in the project. External dependencies have been detected. All right. Well, we can live with this. We can fix this. So we need to go back and simply move. Or actually, so what should we do? Let's say back into server. Let's look at our lib directory here. It's not called lib. What's it called? Ah right, because we have-- I guess we'll just have a top level package here. I think that'll work. Let's try it. mkdir packages, and then we can move from up a level shared into the packages directory. Oh, it didn't put a space for me, into packages. OK. This, I hope, will work. We're about to find out. So you are now in, not up a level, but in packages slash shared. Add a symlink, someone says. That's a good idea. A symlink is a fair idea. Let's try it. That would be nicer. Good thinking. You really ate your Wheaties this morning, or afternoon, whatever time of day it is for you. So let's reverse what we just did. And we're going to move packages up a level. No, that's insane. We're going to move packages slash shared up a level. That's good stuff. All right, so this, models dot dart, is really just having a heck of a day here. Let's close some files. The shared pub spec can go away. The models file can go away. We're not thinking about the client right now. And we're definitely not thinking about the main dot dart file right now. OK, shared packages. Or packages shared, right. Yes, because we're going on symlink now. OK, looks good. All right. So now we'll create a symlink. Now, for anyone who's not familiar with a symlink, this is basically a special little file. Actually, you know what? Symlinks don't work on Windows if I recall. Pretty sure that's true. I don't really develop on Windows, so I don't know this firsthand. But there's a little birdie in my head that looks and sounds like Brett Morgan, who's on the Flutter dev team. And he says, symlinks don't work on Windows. So if you're on Windows, keep this in mind, that what I'm doing right now isn't going to work. But let's test, because I'm curious whether or not the symlink will work, regardless of that plan's viability on Windows. So I've just moved it back. Now, we're going to create the symlink. The command for this is ln link. And then we give it a dash s flag for soft. And now I can never remember the order of the parameters. I think it's-- well, let's run help and see. It's, oh, man ln is what I want, for the manual. And its source first, then target. You'd think that would be easy to remember. So link soft, it was source first, so that's up a level into shared. And then the destination is going to be in packages. And I don't know if I have to type shared. Let's try it, I think so. OK, looks good. Now, this would make the path local again. Oh right, I changed what I typed to this. This was never going to work, did not need to flip that back. So here, we return to shared or packages shared, because we're inside the server code now. And it thinks it doesn't exist. So that's not a good start. Let's double check ourselves in the server. We have the packages directory. And in packages, we have shared, and it points to up a level. It needs to point to up two levels, because it needs to leave packages, and it needs to leave the server folder. So this, I think, is just not going to work. So if we were to cd into packages and then try to cd into shared, yes. It knows that that doesn't exist. So my soft link command did not work. So we're going to remove packages shared and try again. We're in server. Yeah, it feels like this is going to work. Let's do a list on shared, just to make sure I'm actually typing the correct thing. This is the shared folder. Symlinks don't work within the build context of Docker, which Dart Frog tends to use for prod builds. Indeed. That is correct, and I suppose, means that this is simply not a path that we should continue. And also, Windows developers can't do it anyway. Thank you for that hot tip. All right, this means that we are, once and for all, going to move the code into the server. So we'll go back out of here. And we will move the shared directory into server packages. Great. And we know that we can kind of arbitrarily sneak around on our machine from the Flutter side. And this is all in one Git repository. So even if we were to put this in a nice CI pipeline, Code Magic, any of the others, it'll check out all the code together. And it will still be able to find the shared code when it runs Flutter Build and runs all our tests and submits and whatnot. So this, I think, is the way that we'll want to go. Of course, this now means that our client pub spec file will need an update, because it now has to go out one level, and then not just into shared, but into server, and then into packages, and into shared. So we'll first go into our client and run a Flutter pub get. Dmitry, I still appreciate the good idea. Sometimes good ideas run into a wall. Many of mine do. So now, we'll return to the server, and once again, run Dart Frog dev. And I think this will work. There's a chance it'll work, at least. The server code here thinks it has some issue. Oh, the test is probably broken. Oh, this is-- oh, in shared. Right, of course. We actually don't even care about this file. Let's get rid of you. Be gone. OK, now, let's prove to ourselves that the user class is actually being imported by opening up, in our generated Dart Frog code, the index route and instantiating a user. So here, we'll have a final user equals. That's not how any of this works, is it? Again, we'll say the ID here is asdf. And the email is someuser.com. And it doesn't know who the user is, because it doesn't love us. All right, let's try again here. Oh, I was actually expecting this to work. OK, shared. Let's see here. Did you try Serverpod? Ali asks. I have not. Serverpod looks quite promising, and you should. The URI doesn't exist. Well, why not? So in our server pub spec, I see it. Maybe we have to run-- maybe Flutter dev, Dart Frog dev, that might not run pub get. Let's find out. I kind of thought it was going to. This seems to be the issue. All right, now we'll run the dev command again. We return to index. This works, really good stuff. And we'll not just welcome anybody to Dart From, because we're exclusive. We're going to welcome our user to Dart Frog. So we've hit a milestone here. And admittedly, it doesn't seem that exciting. But what it proves is that if you think about Dart on the server, you can share all of your business logic, as much of your business logic as you want, in fact. Doesn't even just have to be data models. You can run the same, you can run the exact same code on the server and client for, maybe, really opportunistic updating of rights. You can, obviously, your JSON serialization and whatnot is going to be a lot easier now. And you're not going to be mapping fields or worrying about keeping everything in sync when you add a field just on the server, and you forgot it on the client. Don't ask me how I know about that headache. Or maybe you use another language that uses a different camel casing or a different capitalization scheme, camel casing instead of snake case or something or other way around. And you're constantly being like, oh, the field doesn't exist, because it's got an underscore in it or something. And in Dart, we don't use underscores in variable names very often. So this is a huge win already. We've got Dart Frog on the server, which gives us really great hot reload as well, which honestly, we're all pretty spoiled by and pretty much require it or we throw hissy fit. So Dart Frog coming in delivering that same experience on the server. And our shared logic spanning the gap between the front and back end, which I find very exciting as someone who, to this day, still uses Python, mostly on the backend, specifically for ORMs, which brings us to the great question that we got earlier of whether or not-- what ORM we're going to use. And snow berry was my answer. So it is now time to think about talking to a Postgres database. And even before I want to get into that, there's-- and I want to talk a little bit about relational databases and what value they bring, which from a historical perspective, is a fairly insane statement. It's like, relational databases are the OG databases. So you might think that their value would be the most obvious. But document databases have really caught on of late and have the a much lower barrier to entry. So a lot of developers who don't any kind of database find document databases to be the most easy to wrap our brains around, and for good reason. They have a lot of strengths. So anything like a MongoDB, Cloud Firestore, Realtime Database, tons of others of course, open source ones out there galore, they're just a great fit for a lot of applications. But relational databases are much older. And it's not so simple as to say they're more performant, because like everything in software development, it depends. But they're extremely performant. And they're very flexible. I think that's the trade off that you get with relational databases. They're a little bit more involved to operate and to use. But once you really get the relational database concept in your brain, it's extremely flexible and extremely cost effective, for sure. So that is-- those two things combined with me just having used relational databases almost exclusively in my development career, I think it's important for Flutter developers to be introduced to them. And if you already know them, then of course I'm not introducing you to anything. But that is kind of my thinking about relational databases. And Postgres is the relational database of choice for me. And humbly, I submit that it should be for you as well. There's other more popular ones like MySQL. But Craig's opinion, Postgres is the best. So that's what we're going to use. Another fun thing about Postgres is that it actually has a very high quality JSON field data types. And you can actually query on keys in some of those fields. And you can write indices on those fields that are very specific. So you can also get some flexibility. You can get some of that kind of scheme-less feel, even with a highly-structured relational database like Postgres. So it depends. Yes, the count has moved to 1, nice. It always depends. All right. Oh, there's some starred comments in here. How did I hide the G and freeze files? Great question. First of all, I got really frustrated and hated everything for a while. And then I remembered that I should Google it. And then I was still annoyed for a minute. But I eventually-- oh, files.exclude. So I simply-- these ones, I did not add these. Or maybe I did. I don't remember adding these. I do remember adding freezed and G. And it's a pretty interesting syntax here that they've chosen, this map. Like, why would I add a file type and then set it to false? It seems weird. So obviously, they're all to true. But these are all the things that just simply do not appear over here. And they make my life a lot easier. I'm going to quickly look at some of these starred comments. For managing state, I use StreamBuilder and controller. Is that fine? Of course, yeah. StreamBuilders are-- pretty much every state management solution out there has streams under the hood somewhere. And a lot of the widgets that they offer, this is not strictly true, don't quote me on this, not everyone does. But many, many have a StreamBuilder under the hood. What they often offer, like block for example, definitely heavily uses streams. And what it offers is lots of de-duplication of events. So if you emit the same state multiple times, block will catch that and prevent your app from just repainting the room in the same way that it's already painted. It's like, I like these red walls. What do you say we repaint them red? Block doesn't let that happen, doesn't waste your user's battery and whatnot. But stream controllers, they're the bread and butter of so many things, for sure. Why do you use a factory in the model class? Someone asked. Well, because the freezed documentation told me to. It is genuinely as simple as that. So if we go back to our models file, this is just the syntax that the creator of freezed, [? Remi Rousle, ?] settled on for the first domino that his code generation magic examines and builds out all the other stuff. And one of the downsides of hiding the freezed in the G files is that you can't as easily look at those files, because they're just not here anymore for me to click on. But we can still type, and we're in our client right now, so we'll go out into server, and then packages, and then into-- why is it putting that space there? I don't want that space. What are you doing? And then into lib, was it? And then source. Goodness, this is really turning into quite a scavenger hunt. Models.g.dart. This should actually open the file. Here we go. That was so long, I kind of forgot what I was going to say. Oh yeah, this is just the code that is generated by freezed from that starting invocation. So yep, documentation said. Thanks Majid. I hope I don't kill it too much. I want to live. I'm trying to build a social media app for text mp3 video on AWS. What are your best resources? Well, if you're building a social media app with all of those things, you are biting off probably the biggest single thing to chew on known to man. I mean, I guess you could also make an operating system or something or an MMO. You could try to make the next World Of Warcraft. But no, all kidding aside, that is an enormous task. AWS has a ton of great resources. If you're using AWS, Amplify has some pretty good server side technology that works well with Flutter. But I think you may need every CS book under the sun, because you are swinging for the fences, my friend. Good luck. You can do it though. You can do it. Does Firebase ever support an open source database? Interesting question. Yes and no. I mean, Firebase has a lot of different products. And its database products are Cloud Firestore and Realtime Database. And they're both pretty good, but they're not perfect for everything, because nothing is. So if you have a different open source database in mind that you want to use, you can use Cloud Functions. And those functions, Cloud Functions, Firebase functions, kind of the same thing, those functions can connect to the other database that you use and read and write and execute queries or whatever the schema is of the database that you're considering. So that can work. But if you're making a giant apparatus of Firebase, Firebase functions, and then having them talk to a Postgres database for example, you can also just kind of go in this route and make your own service and deploy it to Cloud Run, which is where what I'm building would ultimately be deployed to. Another thing to consider, you might be thinking about the high value of Firebase Auth, in particular, which is absolutely incredible. And Firebase Auth can also be connected to other services, again, by just adding a function that is alerted on every new user and pings your other system. And then that system can sync users into this other database that you're thinking about. That happens to be exactly what I personally do and recommend. Is Dart Frog the Django of Dart? I would currently call it more the Flask of Dart. I think Dart doesn't have a Django. And in large part, that's because Django is just one of the largest web efforts ever undertaken. And as a Dart community, our server side existence is all still pretty new. So we don't yet have a Django. But we do have a Flask. And Flask is also very good. Flask is also, it's another Python web framework. And it is the default of Python functions on Google Cloud. If you spin up a Google Cloud Python function and set it to the HTTP function type, it just gives you an empty Flask bit of boilerplate to write. So I personally love Django. It's what I always use. But Flask is also great. And as a Dart community, I think we have our Flask. Any solution to make a Flutter app SEO friendly? Well, that's a tough one. There is a solution, and it probably involves having HTML files for the pages that you want Google to crawl. My street is being cleaned right now, wonderful timing. Hopefully it's not too loud. HTML files that you want a search engine to crawl, and then use Flutter web for your logged in pages. It's not the best answer, I know. I apologize. When to use Android on Flutter, and what requirement? Can you please explain? Well, you don't really use Android on Flutter. You mostly use Flutter on Android. And so I think I don't fully understand the question. But Android, of course, is the phone runtime that runs quadrillions of apps. Most of them are native Android apps, meaning they're written with Java or Kotlin. And they use the default Android technology. And Flutter is able to graft itself on top of that. So every Flutter app that chips to Android is also an Android app. And then there's only one thing happening in the Android app, and it's Flutter taking up the whole screen. Flutter is like, I'm here, and draws every single pixel through Android. So when to use Flutter on Android, I think, would just be when you want to use Flutter and you want people with Android phones to use your app. How do you pass an object instance to a new go route or route? Oh, that's a good question. There is documentation for that. I'm going to punt on that for now. But we might use go router on the client in this project. And in that case, we'll do that. So stay tuned. And what's your take on block versus get? Oh, I think my take on that is right now, I'm not going to take the bait. There's a lot of great state management solutions in Flutter. I, personally, pretty much always use block or Riverpod. And that doesn't mean the other ones aren't good. MobX, I know people love MobX. I just haven't used it. But I don't have a bad thing to say about it. I'm sure it's wonderful. So these are all-- there's so many good state management solutions in Flutter. It's an embarrassment of riches that we have, honestly. The Flutter community often is like, oh, why don't we have just one? Why isn't there one state management solution that I know I can use? And it's like, oh, it's actually better than that. There's like 5. And you can comfortably know that you can use any of them, because they're all very, very good. They're not all the same. They have different guardrails. They help you in different ways. They have different opinions. But I've used enough of them to know that your app will run. Your app will be fast. Your app can be bug-free. There's not any one state management solution that has perfectly figured it out in a way that, oh, but if only I'd used that state management app, it would load faster. And I'd get my data from the server faster. And I'd be caching things better. And I'd use less memory. That's not really how it is. They'll all make your app good, is the exciting conclusion. All right. I think I've exhausted the starred comments. Great. We're back. So it's Postgres time. And this is, we're going to start using some tech that is just outside of the Flutter and Dart world. And specifically, I'm actually just going to reference the example, because I don't really want to write it all again from scratch. So in the code sharing example, if we click on the server directory, we should see a Docker Compose file. Oh, I think I didn't include the Docker Compose. No, I don't know why I didn't include the Docker Compose file. I was really excited to see a Docker Compose file. Well, maybe it's at the top level. Hey, there it is. That's right. OK, so we are going to start running. Oh, this was only running the server. Right, right, right. All right, we're starting from scratch a little bit, a little anti-climatic. Into the server we go. cd, into the server we go. And we're going to make a new file, touch docker-compose.yml. And I'm actually just going to Google a Docker Compose file, compose for postgres, because that's what I want to use. Creating and-- oh, I remember why. Postgres had a bug, or it was just not working on my machine or something. Maybe I had to restart it. But the day that I wrote this code sample, Docker just wouldn't run a Postgres database. So I was using a Postgres app to run Postgres, which I'm actually going to have to stop to free up the port. Yeah, I remember now. OK, so I want a-- this is not going to give me a Docker Compose YAML. Here we go. All right. So let's grab some of these things to start. Oh, I have to actually open Docker Compose, very helpful. OK, so the container name is, now we're just going to use the Postgres image. And we are going to definitely need a volume. This is an actual place on our computer, where the data from Postgres is stored. Turns out, very important. We'll then move down to the bottom and grab that volume, make sure that actually exists. And what else do we need here? So PG admin, not going to use that. We do want this port. And oh, that restart unless stopped, yeah, that seems valuable. Let's use that. This is why you look up, whenever you do things with Docker, look up what someone else wrote down. If you write down your own Docker files and they're complete garbage and you have to delete them immediately, because they're probably wrong. But if you look on the internet, then you'll find way better ideas. That's at least true for me. If you're good at Docker, then don't listen to what I just said. And now, these environment variables. Yeah. OK, great. OK. So Postgres user. This is interesting. I actually don't know what this is going to mean. Let's see if we can read something about this. Compose Postgres. Oh, it is the, Postgres user is the default value. So this would be Postgres. And then it's-- maybe this is like-- I honestly just don't know this syntax. This must be Docker Compose or YAML syntax or something with a default. Oh, change me. OK. I'm not going to change it. I'm just going to make the password, change me, because I don't want to change it. All right, let's see. Let's run this. So I'm going to run docker-compose up slash d to demonize what it runs. That just means it'll-- oh, I have to say this file. That means it'll keep running whatever we spin up, even after-- actually, it'll give me the terminal back, essentially. It won't hold on to the terminal. All right, let's run and see if this works. Oh, up. We do have to say up. I figured if it goes before or after the d, good stuff. Apparently it goes before. Ports must be in mapping. Thought it was. I did change some indentation or something. Ports. Yeah, it definitely feels like I copied what was in here. Oh boy, do I have egg on my face after talking about how-- oh, it is because these are in the wrong spot. These volumes need to go down here. Does this all go in? Is that the idea? Yeah. Yeah, yeah, yeah, these all just get indented. Here we go. OK. And Docker needs to be running. OK, fine. You want me to launch you before you do any work. All right, Docker is starting, I have on good authority. Here we go, here we go again. Oh, why not alpine tag? Alpine is pretty good. Someone asked. And Alpine is just a very minimalist image type. And we could use the Alpine tag. We certainly could. Let's do it. If we go back, I think I had the actual Postgres image here. So we could use, yeah, 15 Alpine right there. Is there an Alpine latest? I guess we'd just do 15 Alpine. OK. So then that's a colon that we use for that. I think so. Colon alpine, alpine 15. I think that's right. It could be wrong. Hey, Simon. What's up? Randall lamenting YAML indentation with me. Yeah, I really fall back to using JSON in YAML. I can't get the spacing right. Yeah. Boy, do I. Oh, Postgres error, OK. All right, I appear to be typing this incorrectly. And so I'm just going to scrap it for now. But Alpine is really good. OK, we have created our database. So now we can say Docker container. And look at the ones that are running. And we see that we have Postgres running. And the name of the container is server Postgres 1. So we can look at the logs by writing docker log, don't remember if it's log or logs, flip a coin. OK, we'll go with the log, and the server name. No, not a command. RIP. Logs. And all right, database is ready to accept connections, which is an exciting final entry to see in our new Docker container. It is immediately on the heels of the system was shut down. So that one's less good. But it's ready to accept connections. So that's what we want. Now, let's connect to our Postgres database. And let's call this-- what are you even trying to connect to? Maybe this is just working. Incorrect, leave me alone. I didn't mean to click that. Stop it. All right, here we go. Local host, that is good. The database is probably just going to be Postgres. The user, we said, was going to be Postgres. And we'll call this, by the way, oh, up here. This is where we'll write Docker Compose. And then the password was change me. Test connection. Great, it worked. Connect. All right, we're in. So we are now talking to a Postgres database. Not in our code yet, but we're ready to do that. And we're also ready to see our changes here. So why not PgAdmin? I just kind of, I like Postico. There's nothing wrong with PG admin. But I just really like Postico. I think it's a wonderful app. I have a paid version on my personal computer. I think this one is the freeware, the nag-ware, as they call it. Postgres is advanced SQL or relational database. Indeed it is, yes. You can even use package drift to share database code between client and server. Oh. I need to look into this. People have been talking to me about Drift for this. And I didn't, I was unaware. And I'm still mostly unaware. But for reading this evocative sentence right now, that Drift could be used for databases other than SQLite. This is news to me. Frog, who are you? Unmask yourself. Identify yourself. Let's look at Drift. I still kind of want to use storm berry. But I'm really curious about this as well. So let's go to pub dot dev, packages, and Drift. You can use databases other than SQLite? What? Built on top of SQLite. Interesting. You know what, Frog, whoever you are. The more package changed its name to Drift. Yeah, that's right. But I still didn't know that more could be used with same same. Didn't know either could be used with anything other than SQLite. This is huge news to me. SQLite, please. Oh. Randall, always the pedant. Did you burn incense and pray to the live coding gods before starting this? I am not a superstitious person. And I also am comfortable looking like a fool. So nope, no incense was harmed in the preparation of this live stream. But let's go to storm berry for now, because this, I really do want to play with. And we can all collectively file away in our brains that Drift can be used with Postgres, which is also extremely exciting. OK, so storm berry is an ORM, which stands for object relational mapper, something like that. It basically gives you one class in your application code per table in your database, kind of as simple as that. If you've used Firestore before, and you had one, you obviously, well, maybe not obviously, but you'll almost always have one class per collection in your Firebase database, then this is very similar. The only difference is because this is based on SQL. SQL, which is a string-based communication protocol, you can't as obviously just work with JSON like you can when you're talking to Firebase, because it will just take a map of fields and values and be like, sure, in the database you go. And it's happy to work with that. But if you try to just throw a map of values at Postgres, it's going to be like, I literally have no idea what you're talking about. So we need something to change our data classes into these different types of queries that we run. And that is the role that an ORM plays. And it even uses a user class here. So this is pretty great. I can already imagine myself getting extremely confused between this user class and our freezed user class. So we'll see how that goes. But for now, I'm just going to copy this code. And we also have to add, we add storm berry and build runner. So we'll go to our server, pub spec dot yaml file, which is right here. And we'll add a dev dependency of BuildRunner [INAUDIBLE].. Why did that happen? I reject what just happened. Server pub spec dot yaml, not lock. Server. Did I delete it? pubspec.yml in server. Did I delete it? I think I deleted it. Where did it go? Oh, goodness. Well, you know, we've done very little. All we have to do is move our packages folder out of this. And then we'll move ourselves out. And we'll remove server. Right? Yep. And then we'll recreate the server by running dart frog create server. Seems like I should have burned some incense, Scott. Huh? Lesson learned. And I will move packages back into server. Oh, right. I didn't type that even remotely correctly. There we go. Move packages back into server, looks good. And we'll return to server ourselves. We'll open that brand new shiny pub spec file, not delete it this time. What did we have? We had our shared code, which came from, where the hell did it come from? Came from packages shared. And now, we get to add BuildRunner. Actually, I'm going to let the tool pick the latest version for me. Goodness gracious. And dev dependency, there we go. BuildRunner, there it is. And now, we'll also add the regular dependency of snow berry. We did it, folks. Made it look incredibly hard. Could have just copied these lines here and run those. No, had to be fancy. Had to break it first. Craig, how do you include Postgres inside Flutter app? If we want to use it instead of SQLite. There may be sufficient black magic that you can use to embed Postgres with FFI or something. I'm just talking, pulling things out of thin air right now. In general, that's not going to be a very common requirement for an app, because if you think about why you would want Postgres embedded in an app, I mean, I love Postgres a lot more than I love SQLite. Though SQLite is also very cool. But SQLite is often used for, when you're talking about putting a database in your app, then this is basically going to be used for the most aggressive, in a good way, on-device caching of data known to man or woman or beast. And you can't-- you have to think about how much data are you going to be downloading from your server that you need to save on the device and store in relational databases, where you can run queries with joins, that you kind of need to do all of these things before you need a database on your user's phones. Otherwise, you can just load from the server, right? But maybe you have all these requirements, and you need to be offline first. So this is really important. That's totally understandable. Then to further graduate from SQLite to Postgres, you'd need, well I guess, if you got that far, then you just needed good geo data or those flexible data types. You could need Postgres on the device. At that point, I think I would encourage exploration of other solutions. Like, how else might we get this cached data on the phone? Have we really outgrown SQLite? Can Postgres on the server be our actual source of truth? And then on the device, we just have some kind of normalized caches or whatnot that we filter on, maybe without joins. We just have some flat lists. I'm unaware of a way to get Postgres on a phone in an app. It may be possible, though. Postgres backend is experimental, but it works decently, Frog says. Oh, that's for Drift. Amazing. I think I'm going to have to play with this at some point, Frog. Whoever you are, mysterious man or woman or animal. Scott says, I will become superstitious. Maybe. Actually, just this morning, OBS decided it didn't want to save any settings. And every time I would change one of the scenes, it would be like, cool, cool, cool. How about these settings now? Then I just had to restart OBS. But I was feeling a little cursed for a minute there. Is there any direct Dart connection with server db without API? This is, so I think what you're saying is, can your client send queries directly to the database and not use a server? And I'm unaware of anything that would prevent that from happening, other than, you would put your database credentials in your app. So you can do this. I don't think there's any technical limitation. But it's probably not a great idea, because once your database password is in your app, it could be reversed. Someone can crack the binary and pull that value out. And then you will have the worst day of your development career ever. This is kind of why there's generally a back end. So it can just get HTTP requests, make sense of them, authenticate the user. And your backend is the only person-- that's not really a person, now, is it? Your backend is the only anything that knows how to actually talk to your database. Better to give Django as an example for ORM. Well, I do love the Django ORM. It's one of my favorite pieces of software I've ever used. So I generally agree. I'm not sure better than what. I don't remember what I was saying at 1:14, 6 minutes ago when you wrote that. But I love the Django ORM. So in general, full endorsement from me. Comparison of Dart Frog and Node.js, please. Well, I've not used Node.js a ton. Node.js is a runtime for JavaScript outside of the browser. And Dart provides its own runtime. So they are slightly different there. But often, when people say Node.js, they're really, they kind of mean express the framework written on top of Node.js. And in that sense, I've also not used express much, which can be deduced from the previous statement of me not having used Node.js much. But my understanding is that they're pretty similar. I think they're in that Flask, Dart Frog, Node.js kind of level of pluggable extra resources. You know, Django's big line is that it has batteries included, where obviously, there's still a trillion extensions for Django that people install. But it really brings a lot of the stuff. You can get quite far without adding anything else. And I think Express in the node world is more similar to Flask. That's my understanding. You an just add berry instead of snow berry. What? How would this be a thing? What are you talking about? Berry. Oh. Wait a minute. Oh, I can add it. That's true. But this looks like abandoned ware, Roman. Are you, do you use this instead of snow berry? Do the relationship between the two? Because this one is from an unverified uploader, whereas snowberry is from the author of other popular packages, including Jasper, which I actually discovered yesterday. Andrew Brogdon pointed that out. I didn't even know. And Simon points out that Isar is another great option for an on-device database. Yes, shame on me for not mentioning Isar. I love Hive, the package, the first package that the author of Isar wrote. And then he moved on to Isar. And I played with this quite a while ago. And it was still pretty-- wow, look at it. The community has spoken. Yeah, Isar, very good. And you can also, my understanding is that this has joins, which really moves it into that direction of being a very strong SQLite, very robust on device cache. So a great point, Simon. Is Dart Frog or still need more development or improvement. Everything needs more improvement. But no, Dart Frog is good. Very Good Ventures has labeled it stable. And my experience with it, it has also felt extremely stable. So I think if you are wondering like, oh, is it too early to jump on the Dart Frog train? It is not. Jump on the train. All right, we are going to get back to a little code here. We've been going for an hour and a half. So we'll probably wrap up soon. Maybe I'll make a connection to the Postgres database, and then we'll call it. How about that? So to do that, before we even get going with stormberry here, I actually need to go back to this tab and grab the Postgres package. This, pay no attention to the red failing signal. We actually need to get started with this Postgres connection. So we're going to look at its installation instructions. And this time, instead of deleting my pubspec.yml file, I'm just going to grab this. And we're on the server. You're in that directory. So we run the command. Oh, it added berry. Was is it listening to me? When did it add berry? I just talked about adding berry. Oh, is that what the person said? Wait, let me scroll up. Did they say I added berry or that I should add berry? Craig, you just added berry instead of stormberry. Brilliant, thank you. Roman, I misunderstood you this whole time. Incredible. All right, berry, out of here. Although, it's very interesting. It looks similar to stormberry. It looks like an ORM of some such, but highly, highly abandoned, it seems. Hey, [INAUDIBLE],, how's it going? Scott, you guys-- Scott's pointing out they did almost three hours today. You're marathon runners. All right, so Postgres, that's what we need. Add dependency, Postgres. I can't believe I typed berry. That's a great show, by the way, "Barry" on HBO, spelled differently, also a very different vibe than a relational database, but excellent nonetheless. All right. Pub run, why am I blanking on how to get the things? Dart pub get, there we go. Typing pub run get. And part of my brain's going, woop, woop, woop, mayday, that won't work. All right, so now, we are ready to get started here. And let's put this in some middleware. So the next thing that we're going to do is look at Dart Frog's middleware. And I always do that by going to its homepage. And I want to learn more about middleware. So didn't it generate a file for us, a middleware file? Where's the routes folder. Routes, nope. So what do we have to make here? We make an underscore middleware dot dart file. I can do that. I can do that. New file, middleware.dart. All right. And then of course, we copy in all the code. Great. And now we are going to-- this middleware is interesting. Oh, I want middleware that provides something, dependency injection. Yeah, this is what I want. So we get the context. Oh, the handler, this has changed. This middleware, these shenanigans have changed since the last time I looked at this. I'm going to actually have to read what's going on here. Oh, here is a handler. Let's understand what's going on as opposed to-- middleware can be used to inject dependencies into a request context via a provider. I love it. Provider is a type of middleware that can create and provide an instance of type T to the request context. I've used that before, but it looked different. The create callback is called lazily. Love that. And the injected request context can be used to perform additional lookups to access values from the stream. Let's go back to the middleware thing and see what it says. Middleware, blah, blah, blah. And Dart Frog, a piece of middleware consists of a middleware function exported from an underscore middleware.dart file. Then the thing. There can only ever be one piece of middleware per route directory within routes, or with routes middleware, being middleware that is executed for all [INAUDIBLE], I see. So this is our base middleware, everything gets it. Yeah, naturally. You can chain middleware, such as the request logger, the middleware via the use API handler.use request logger. Interesting. So this handler is passed into the middleware. And it basically decorates the actual process. I think what's interesting to me-- so this is execute code before it is handled. And then forward the request on to the actual handler Oh, so in the middleware, we have to remember to call the actual handler. I do not remember that being a thing. But I guess it was a thing. So it sounds like we will basically do Postgres things here. Postgres things here. I need good sleep today. We all need good sleep every day, Aman. But like every other day, I do need good sleep today. That's true, you're right. All right, we're back here. So we're in the middleware again. And the provider thingamathing. Let's just grab this. Oh, handler.use. And in this case, we actually don't have to call the thing. Aha, this is what we want, excellent. So we're going to get rid of a lot of this. Handler.use. OK, I love it. And now we get to add Postgres to this file. And in actuality, this Postgres connection should go somewhere else. As this gets more advanced, the Postgres connection will definitely not be instantiated in this file. It's absolute madness. But for now, that's just what we're going to do. So back to the Postgres. And we'll look at its readme. And we'll open a connection like this. And great, local host, that's correct. Dart test, no, we called it Postgres was the database name. The username that we connected with was Postgres. And the password is still change me, which has got to be one of the most epic. I wonder how many passwords in the wild are just change me or admin or something like that. This is definitely an anti pattern. All right, why are you not formatting, dear friend? Probably because that is incoherent syntax in that place. So we're going to await the connection to be opened, which means this needs to be asynchronous. And then our provider is going to add a Postgres QL. I can't type. It's going to add a connection of the thing that we're dealing with. And the value is, what did this snippet call it? A connection, wonderful. OK. Why is this handler marked as async? So it needs to be a future of type handler, yes, very good, well done. And these apparently should be single quotes. All right. I think we are ready to now read that handler. And if we return to-- where did the window go? Oh, here we go. If we return to the Dart Frog documentation, we can see that it will do this. So we'll turn to our index file, server bounce index, and grab our database connection, except this is of type this long thing. And that requires, I require your finest accommodations. That requires the Postgres package to be imported. And this is the connection. And now, we are going to say, welcome to Dart Frog. And the username, oh yeah, we have to add the username, because I deleted that, shared. And so let's make it user. And the ID was asdf. And the email was someuser. All right, so we're going to say, welcome to Dart Frog, so and so, user.email. The time is, and I think we can ask Postgres for the current time. That's what's going to be exciting. So let's, first of all, see Postgres query select current time, the now functions. So I think if we just write, select now, great. And we can verify this for ourselves by going into our Postgres Explorer and typing select now, which philosophically is an extremely interesting concept. It's kind of seizing the day, I think, yeah. This is, you know what? I'm settled on this. All developers need to wake up and run the query select now. That's how you seize the day. This is how champions start their mornings. So we are going to run that query. So connection dot execute, I believe, is what we'll run this. And let's just dip into this execute function. Takes a string, which is the format string. This is essentially the query. We are not going to have any substitution values, because we have a hardcoded query. And I'm not going to-- I'm just going to use whatever the default timeout is. So we will select now. And that is going to probably return a future of something, future int, the number of, I have no idea. Let's just find out. So final result equals wait that. And then you need to become asynchronous. We're moving now, folks. Oh, I see some more starred thingamathings. I'll get to those in a moment. All right, so we have a result. And I have no idea what the result is. Let's just print it. And then we'll run this. And we're not telling the current time yet. So we'll come back to that. All right, all right. Dart Frog dev, let's do it. Oh good. What have I done? Cannot access parent directories. I wonder if this is because this server is stale, or this terminal window is stale, and still is in the old deleted directory. I just made that up. But it could be the case. Server dart frog dev, yep. Even a blind squirrel finds a nut sometimes. Ooh, functions marked async must have a return type assignable to future. I agree with that. That just makes good sense, sensible stuff. OK, we're building. Oh, there's other problems. I should really look at the page with the problems, shouldn't I? These are the tests. Let's just leave that, because I don't want to be distracted by those. Oh, well, it doesn't see any problems now. So what is this? Future is from Dart async. That is extremely surprising. It wants me to return to import. What? The argument of type future or future or response. How is this thing-- can't be assigned to future or response. Yeah, indeed. A future of-- those are different types. Oh, what am I doing wrong? I'm doing something wrong. Select now, universe implodes. Hopefully not. OK, what am I doing wrong? This is obviously a very easy thing that I am just screwing up. Tutorials. One of these tutorials will have a thing. To dos. We need an async view in one of these. And then we will win. OK, so they're also making data classes, love that. Yep, yep, yep, more things. Here we go, a data store. See, this is better. They actually have a separate thing to access the data. We'll get to that, it is important. Creating middleware. We already created the middleware. What was all this? Un-request. Oh, it's just future or response. That's what we need to return. OK. I was unaware of-- what? Oh, and then this, I have to probably bring in async, OK. Dart async. I literally never use future or, honestly ever. Simon says, check your middleware return value. I think it's not wrapped in a future. It wasn't for a while, and then it barked at me. So yeah, I think we're good here now. This should be good, unless you're talking about something else. Nope, still broken. Maybe this is what Simon's talking about. We're making excellent television here. Hear, the argument type, blah, blah, blah, same exact error. But where is it coming from? Oh, in this middleware. Oh, Simon was on to something. So this has to be future or, I think is what Simon was trying to tell me. But I'm just dense. So this gets to be a Dart. And this becomes async. All right. Once more with feeling, folks. No better. All right, so the area-- it still says it's on the middleware thing. I guess I'm just not sure what it's talking about. Must be handled-- OK, Felix in the chat, amazing. Middleware signature can't return a Future. There it is. OK. Thank you, Felix. Amazing. So it sounds like, Felix, should I just be providing a Future of this connection type? Is that what I should do? And then I think I'd maybe just not await this. Oh, this middleware-- oh, right. This middleware is in just a terrible place, if we think about it. We really are going to need a class to handle this, because this is going to-- the connection is going to be opened on every single request. So this is absolute nonsense, what I'm writing. And we're only going to leave it here, because I'm going to pick up in the next episode and do better. So we're going to have a class that actually returns a real-- no. Yeah, well everything-- we're just going to return the connection. We'll open it in the route. Just as bad, just as terrible, because this is still now going to be opening the connection every request completely incoherent, just an absolutely terrible idea. But I think it'll work for one request. That's all I need. And now, this is a future or. And then back in our middleware, I did get rid of that. Oh, but then you're not async. Yes. One thing that would be nice is you get the errors on generation here. And then the generated code runs and either is or isn't incoherent. But you have to run it to find that out. That's interesting. All right, where was I? We come back here. We should have opened this connection. And we should have printed. We didn't get a thing here. What's going on? I refreshed my browser. And I was expecting to see a log here. Oh, it did print one. OK. So what? That's not helpful. You literally just return-- all right, let's look at this connection. Maybe the execute method is not what I actually wanted to use, because that seems to have returned the number of rows return by the query. And I want the time, not the number of rows. Open, no, we don't want that. That opens a connection. Close, of course, add message. Transaction, those are valuable, but not for us today. Upgrade SSLm interesting. Did the documentation not tell me how to do this? Oh, it's literally right here. Oh, it's dot query. OK. So we'll go with that. And this is a list of list of dynamic, great. It really does help to read the documentation. So query is what we want. And if we refresh, the application reloaded, very good. Let's refresh the page, internal server error, because I opened a connection that was probably still opened, yep. And this is where we have one of the worst pieces of software anyone has ever written. OK. Aha, so we have a list of lists that just show the time. I can do this. I know how to do this. So the current time is, and this is going to be the result, sub-zero, an index zero, I believe. And now we have to kill the whole server again, because I'm doing things in the worst way that they've ever been done. And folks, if this prints the time, we have talked to a Postgres database. Whew. All right, I think that will wrap for the first episode. I'll hit the rest of the starred questions. And then we'll pick up next time and actually open this database connection in a responsible way and start thinking about authorization. We'll need a user table. We'll need to actually run some specific queries. We might want to think about how those queries will be testable. There will be a lot to get at in the next episode. But the starred-- things for now, why use Dart to write a backend? Well, that's a good question. You can use Dart to write a backend, because you're using Dart to write a frontend. If that doesn't jump out at you as a good reason, then you probably shouldn't feel-- you're not missing out. If you are very happy with your Python backend or your JavaScript backend or your Go backend or Java, or whatever language you're using, if you are feeling very comfortable with how that's all working, then continue, for sure. Using the same language on the client and server offers a unique opportunity to share a bunch of code. I personally, in my for-funsies development, use Python on the server and Flutter on the front end. And so even though I'm extremely comfortable in both of those spaces, there is just, there is a tax to having two different ideas about how to do everything in these applications that I make. And I miss the development experience of Dart. You know, Python is way older, it has a trillion more users. But the Dart development experience is second to none. And when I'm writing Python code in those side projects, man, I really miss Dart. So for me, that's the reason why. But if that kind of doesn't-- if that doesn't land, then don't feel bad about it, for sure. Why not? Someone proposes. Yeah, indeed, great point. OK, am I using Apple Silicon? Someone asked. I am. Yep, I sure was. Apple Silicon is this computer. That will only really matter insofar as if you make a Docker container on your computer and try to upload it to GCP, it will explode. So you just need to use the GCloud build command to actually build your production Docker images on GCP itself. That's basically the only issue that I'm aware of these days developing with Apple Silicon. Simon says, Craig, you can pass the connection value to the middleware function. Yeah, I'm going to be making something else outside and pass-- well, I'm going to be using it in the function. When you say pass it to the middleware function-- oh, because the middleware-- I think I know what you're saying, Simon, I think. If we go back to middleware in the docs here, we see that it returns a function. And when you're talking about passing it a function, or passing the middleware a value, I think you're talking about using this. But yeah, we'll get into that in the next episode. We can work with MySQL and Oracle 2, right? That is almost certainly a yes, and pretty much just depends on whether or not this URL loads. OK, maybe there's something else. MySQL, MySQL won. The permanent camping of names on pub.dev continues to bedevil us all. Someone's like, I'm going to make a MySQL library. And then 1 minute later, they're like, no actually, I don't want to do that. And then no one else can use the MySQL name for the rest of the time. Anyway, yes, you can use MySQL and Oracle. You just need a package that knows how to turn your queries into a request on the wire. Maybe close the connection before opening it will help the problem. That is solving the problem in the wrong way. It would prevent me from needing to kill the server and then restart it every time. But database connections are meant to stay open. So you are right in some ways. And Simon says the middleware returns, A, the request handler. There's a typo there, I'm certain of it. But anyway, the middleware returns to the request handler, something. Anyway, that's excitement for the next episode. If you too are wondering how these developers are going to get out of this middleware database connection pickle, be sure to tune in next week, where we get out of that pickle and ask ourselves, how did we get in a pickle in the first place? You're, like, in a pickle. There are whole cucumbers. How do you get in them? Doesn't make any sense. Can we do multithreading on the server side with Dart? Yeah, you can make isolates, for sure. It's probably not a good idea, honestly, because if you need to do something outside of your request response flow, and you know it's going to take a while, then in backend technology, it's way easier to just offload that work to another computer entirely. So there's often worker queues, asynchronous tasks. You can send a message to Pub/Sub, and then it can route that message back to somewhere else that actually does the work in question. So in general, the conventional wisdom in server side development is you want to have very thin HTTP servers that don't really have a ton of resources. And they scale really effectively. So like, Cloud Run is so good for this. And you just have requests coming in and out, little bits of JSON. Maybe they're uploading a file every now and then. But in general, hopefully you're having fairly lightweight requests. The server is running some queries. It's turning those into JSON and sending them back on the wire. But then let's say you do get one of those images uploaded. And you're like, I need to optimize this. I need to crop it for the thumbnail and whatnot. And I know I don't want to make my users wait before they get the response back on all of that work completing. Isolates are how we do that on the client, for sure. But on the server, I would recommend using worker queues or Pub/Sub or a different Cloud Run instance that maybe has more memory and is, thus, kind of more able to process images. And so you could send a message to Pub/Sub. And then it could wake up that other beefier Cloud Run service and says, I'm here, and processes the image and whatnot, or whatever the work is you need to do. Shelf has a shelf-worker model to use isolates for multi-processing. All right. Depending on what you're going to do, though, it still can possibly impact the nature of how you configure your Cloud Run service. And there's, like everything, trade offs. So if you have-- you want a simpler setup, and so you're OK just having a little beefier Cloud Run HTTP service that also processes your images and whatnot. But the benefit of that is you only have one of those, and you just have less headache to manage. That's totally valid. Conduit is more serious than Server Frog, you think. Well, I think Server Frog is pretty serious. Conduit may also be serious. There's no Conduit hate from me. I'll check it out. Thank you for the recommendation. But I can tell you that I think Dart Frog it's pretty serious as well. The connection problem will be fixed by having init state and dispose state. Yeah, we've got to handle that correctly. You're totally right. I just did that in the sloppiest way possible, because I was ready to be done livestreaming. Hey, say something about Compose versus Flutter. Well, Compose is an all-Android technology. And Compose is our, I say our from the Flutter perspective, I obviously work on the Flutter team. Compose is just kind of around the corner, just down the road from us, also Google Product got similar ideas. And it's all Android. So if you are using Jetpack Compose-- I appear to need to drink water. Oh, I have a green cup. Is it invisible? It is invisible. Let's go. Yeah. Anyway, I haven't used Compose myself, simply because the only mobile development I've ever done is Flutter. And before that, I was a web developer. I don't really feel like I've lost my voice, but it sure sounds like it. Firestore plus Flutter, good? Yep. Yeah, I'm pretty good. If you use those, you're going to be happy. You'll never have an easier way to live update your app than if you use those things. How are things with Fuchsia, someone asks? Pretty good. Fuchsia and Flutter are less coupled than they were a few years ago. Flutter is still the primary UI toolkit of Fuchsia. But that's kind of a solved problem at this point. There's a lot less movement there. And so the Flutter and Fuchsia teams interact less than they used to. I started at Google on the Fuchsia team and have fallen out of touch with the latest of what they're up to. But they're still going strong. React Native versus Flutter, just kidding. Well, I think you might be able to guess the answer you're going to get from me. React Native is high quality technology. And if you're using React in the browser, and you're using JavaScript on the server, and you're just React experts all around, React Native might just be the best choice. Absolutely. All right. Well, I'm at the bottom of the chat. And I think we have done it, folks. What about ServerPod as a Dart backend? I have not used ServerPod myself. But I believe that it is very good. So it may warrant some future exploration here. It's come up in the chat a few times, as has Conduit. So these may all be things that we kind of have to dive into and explore ourselves and see what good ideas, different packages they are coming up with. But for now, I'm clearing my throat every other sentence. So I think it wants me to stop talking. Thanks for tuning in, everybody. It'll be the same time next week. Ultimately, we're going to go with a schedule of most Thursdays. I don't want to commit to every Thursday. Sometimes, like in January, there are events I have to travel to. So most Thursdays, you'll find me here rambling to myself and you all. And I'm looking forward to it. So hopefully we'll learn some stuff together. And also, if you're also live streaming, definitely hit me up on Twitter and whatnot. Make sure I know, because I'd love to tune in. I tried to watch Roman's stream last night. And somehow, I got the reminder when he ended, which was really unfortunate. But that'll do it for now, folks. So have a great weekend, and I'll see you next Thursday.
Info
Channel: Flutter
Views: 56,993
Rating: undefined out of 5
Keywords: code sharing, postgres, Observable flutter, what is observable flutter, live coding, code tutorial, coding tutorial, how to code, live code, flutter livestream, flutter stream, flutter latest, flutter updates, what’s new in flutter, flutter tutorial, introduction to flutter, how to use flutter, google stream, google livestream, flutter developer, flutter developers, google developer, google developers, flutter, google, Craig Labenz
Id: WE-CYXE1xug
Channel Id: undefined
Length: 115min 2sec (6902 seconds)
Published: Thu Dec 01 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.