Observable Flutter: Dart + Postgres

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
CRAIG LABENZ: Hello, everybody. Welcome back to another episode of Observable Flutter. Let me see how I can remove the overlay here. I don't know. Oh, I'm live. It's already been removed. How great. Today, we're going to pick up where we left off from last week-- and I'm hearing myself and need to mute myself. There we go-- where we last saw our heroes. We worked through some code sharing, and had a shared user model that was available to both our front end Flutter code and our back end server-side dark code. And we also established an extremely primitive connection to a Postgres database and just asked it what time it was. And then finally, using Dart Frog, we displayed that time. We saw that running in a window that I'm going to bring back over here and make much larger. So this said-- oh, I'm on the wrong thing entirely, aren't I. Here we go. I'm back. It said, welcome to Dart Frog, blah, blah, blah. This is the current time. So that's where we're going to pick up from today. And we're going to dive deeper into talking to Postgres from the server to get ourselves set up to complete whatever app it is that we're even building. We haven't decided yet. Maybe we should take suggestions from the chat. So that is today's agenda. But before we dive into that, just a quick reminder, folks, this is Flutter. We are, I think, the friendliest community in tech. And let's keep it that way. So we're operating under the, obviously, the YouTube terms of service, but also the Flutter code of conduct, which is very important to me. So we're going to be debating different things on this show, different packages, different options, different technologies, and we always want to stay respectful of any package or technology that we're not advocating for still. A lot of the authors of those things might be in the chat. So let's just remember, always stay respectful of things. I think we're ready. So I'm going to have a quick tour of the code that we wrote last week, for anyone who either has forgotten, which kind of includes myself, or hasn't been able to watch last week's stream. So to begin, and actually I'll open up the side browser here, in one folder, we have the client subdirectory and a server subdirectory. And this client subdirectory is all Flutter. And the server subdirectory is all Dart Frog. And that client directory, in its pubspec.yaml-- actually, I'm going to start with this part of the story, from the server's perspective. The server has nested in it a packages directory. And the packages directory, this was created by good old dark create. And it's a simple package. And it has in its lib directory some shared code. Actually, the whole package is called shared. And we've got a user model here. And this is kind of the crux of what we're worrying about. I want to have some shared front and back end logic. So that lives in the servers directories package shared subfolder. And it is available to be installed in our top level pubspec.yaml. So if we look at the pubspec.yaml that's directly in server, we'll see that we're including packages shared. So this just brings in the user model. That's all it does. Then, the same trick can be done in the client. And I actually think that I may have moved this around since I looked at the client. But if we go to pubspec.yaml-- oh, no, I did edit it late in the episode last week. So this pulls in that same user file and we're able to seamlessly serialize and deserialize from the server and know that we're not going to have any issues. We've got some starred questions already. Before we get any further, let's go. Someone says, I think MongoDB is better than PostgreSQL. Well MongoDB is very different than PostgreSQL. It's a document store. And so it has all of the document storage strengths and none of the relational database strengths. So those are two-- they're just pretty different databases. If you like document databases, anyone who does, and there are many such developers, will definitely prefer MongoDB. MongoDB also does have a lot of really great-- oh, I accidentally clicked on one-- I'm not sure. You always have questions in your application. MongoDB has its own use cases. But it's hard to beat-- oh, this is a response to the last one. The query system SQL provides out of the box. Well, I agree with that. Yeah, MongoDB is excellent. There's no-- it also has a great offering on Google Cloud. And it has a really nice two-way sync, a product called Realm, which has existed for a long time, and at some point joined forces with Mongo. Or I'm not really sure what the back story is there. But MongoDB has a really nice client for that as well. So more comments on SQL versus NoSQL. They're both great. After a huge, amazing success of the Flutter Dart framework, could there be any possibility about a new server side Dart framework? Well, the community is making some of these. I don't think Google is going to make this thing that you're talking about. I think if I had a crystal ball and it could tell us in 10 years from now, has Google worked on a server side Dart framework, I would bet $1,000 that the crystal ball would say, no. But they're coming from the community, and we're playing with Dart Frog right now. But in my opinion, honestly, the biggest task really is how you talk to whatever database you're using, whether it's Postgres or Mongo or something else, which is why we're going to play with an ORM today. What kind of version of the boring show is this, Verde asks. I don't know-- astute. I have no incense again, but I do have two cups of coffee, so I don't have to get up for my second one. So I burned some coffee beans, and my mouth. Goodness. I think we have somewhat covered what we did last week. Oh, no, there's still one thing I want to get back to. We have this-- nope, that's not the right file. Where did this come from? The middleware situation-- I'm going to make some more space here. To talk to Postgres, we need to have this PostgreSQL connection class. Although, as soon as we get into stormberry this may even just go away. We'll see what we do with that. But we have this raw PostgreSQL connection, and we did not at all last week, figure out an elegant way to make this available to our class. Actually, this actually does look fine. Why am I-- why do I keep thinking that this isn't going to work. Let's refresh the page. Oh, no, it doesn't work. I knew last week why it didn't work. And it still doesn't work. I attempted to reopen-- where do we actually reopen? Oh, in the route, I think. Yes. Here we go. We open the route. We open the connection in the route. And this is just absolute madness. That's what I couldn't remember. So this is the first thing that we're going to think about today, is how to have a sustainable connection to Postgres. But we're actually going to approach that problem from the perspective of this stormberry library. So let's say pub stormberry. And let's take a little peek here. Stormberry is a strongly typed ORM code-generation package. Oh, ORM-like-- interesting. I don't know if there's a firm definition of ORM such that a package like this should be hesitant to don the full mantle of a complete ORM co-generation package, instead of just being an ORM-like co-generation package. Anyway, that provides easy bindings between your dart classes and Postgres database. It supports all kinds of relations. I love that. So I think I added this last week. But I don't know if I did anything with it. Let's remind ourselves. Our server, pubspec.yaml and, no. I thought I did, but I didn't. I really thought I did. Shared pubspec.yaml? This would be a silly place to add it. No, it's not there either. Because there was the whole thing, if I added the wrong package instead. Did I never come back and add the right one? That would be quite funny. So we're in the server directory here, and I'm going to run the command of adding stormberry. And then we're going to add-- we'll build runner and we probably already have that. Yep, we do. And it's a dev dependency as well. So I think we are ready to get started. In your code, specify an abstract class. And they, even, are talking about a user as well. And it should look like this. Oh, one of the questions that we had last week was whether or not the ID should be nullable or not. And I was pointing out in our code right now, it is nullable. Theirs, it is not. And I said, well the database is going to give us users. So it will generate the ID. That's, whether you use a sequence or just an auto increment, the database will give you that primary key. And lo and behold, they've got a different way to handle this. So we'll learn how that goes together. But for now, I am just going to copy this code. And we'll also have to think here. Remember, the whole point of this is code sharing. But this is a database class. So we're obviously not going to use this class in our Flutter front end. And for anyone who's watched any of the videos on clean architecture, I love those videos from ResoCoder, where he talks about having your to domain and you're from domain and you're from data and to data, however they're all called. I think we're going to need some stuff like that. So put on your ResoCoder hats. Anyway, I've got that code in my clipboard. And if I-- let's see, where should we put this. I'll open the sidebar again, which I'm going to try to keep closed today. It wastes a bunch of space. But in the server-- I guess, we'll just make a new folder here. I don't even know what's going on. I'm going to get out of here. That was a fail. So in the server, let's make a new directory here. We have to go into the Lib. No, there is no lib, because this is-- well this is a question on Dart Frog. Where do I just put a random code. What are my options here. All I have is routes, and then this packages thing that I made. I think the idea is that I put this in a subpackage. Let's make a database subpackage. I think that is a good idea, actually. So in packages, let's run Dart create again, which I'm going to hit by typing control R, and then typing Dart create. And editing this, and I'll call it, just DB. So this was how we made the shared directory last time. And now that will be where stormberry goes. This will be nicer. So let's go back to our server pubspec.yaml file, and get rid of stormberry, and save. And then we'll open our DB pubspec.yaml file, which is this one, and we'll add it back in. And now we will also need to bring in build runner, because that was a step that I was able to skip a second ago, by way of it already existing. Felix, in the chat, points out a lib or subpackage, which is how we're going. Felix, I just, I asked myself what would Felix do. And I remembered in all of your Dart Frog tutorials, this is what you do. So great. Welcome, Felix. Great to see you. So we've got stormberry. We've got build runner in this subpackage. And now I think we need to run some pub gets. So let's CD into DB, which, of course, stands for database, and run Dart pub again. And now, we can return to our server pubspec.yaml and we'll add DB, like this. And I guess we'll go full alphabetical sorting. I wonder if we should have all the shared path ones together. Don't know. For now, we'll just do more alphabetical sorting. So we are now in the DB subdirectory package, whatever it's called. And let's open up DB main.dart. No. What's going on? Lib, source, db.dart. What does db.dart have? This just has the export. Yeah. So CD source. Oh, it's probably called db.dart. db base. Right. Right. Right. So let's rename this to DB models. We call the other one models, as well. So we're already kind of colliding with our own naming convention here. But let's rename db base.dart to DB models. This is imperfect. Db models. And now, we don't need any of this code. And my clipboard has been overridden. Does anyone else sometimes-- you save something important in a clipboard. And you're like, this is critical. If I don't paste this, I lose so much work. And then instead of opening text edit or something and pasting it somewhere safe, you just keep it in your clipboard. And you keep doing the work until you're ready to paste it, just like praying that you don't forget and copy something else and then lose all your work. Oh, no? Just me? OK. Well, sorry, guys. I do that. Let's import package stormberry.dart. So returning to the instructions, it now says, you're going to need this build.yaml shenanigans in your root directory of whatever package you're in. So for us, of course, that's db. So I'm going to return to the root of our database directory and run touch build.yaml. And then we'll open build.yaml in the database directory and paste it in. Now lib models is not where our stuff is. We are in lib source db models. Lib source db models. Modify this to match your library file. We did that. Nailed it. Let me pause and look at some questions. Why Dart Frog rather than server pod? Have to start somewhere. Honestly, not any more sophisticated than that. I am pretty sure that server pod is quite good. But I have just used Dart Frog a bit before, so I thought I could move slightly more quickly with Dart Frog. And I like Dart Frog. I'm not going to lie about that. What is the topic? Some shared logic between the front end and back end, and we're also going to be talking to Postgres as well. So what we're imagining here, is folks here, we all know, that we like to build our client UIs with Flutter. But that doesn't necessarily inform what we do on the server. There's a ton of great options. And so we are, if we really zoom out on what the topic is, we are exploring end to end fullstack Dart. How can we put the middleware on routes like get or post? Oh. Whoops. We double clicked. How can we put the middleware on routes like get or post? Well, the middleware, I know, can-- it lives in every specific directory. So if we look back at our-- and Felix is in the chat, so while I guess, he might just be typing the answer here. But if we look at the routes directory, and this is all server pond stuff now. If I wanted to have a URL like /users/ the user ID, I would make a new folder here, called users, and then another file in there, called, depending on my URL scheme, it might be actually kind of a wonky file name. It might be kind of the matcher as the file name. But let's pretend it was called userID.dart. And then I wanted middleware only on that route. So I could put another underscore middleware file in there. Now that doesn't quite answer your question, but it sets us up to look at this handler that comes in. And I bet this has a pointer to the request. And I think it might be as simple as we just check the method. So let's look at the handler here. Oh, this is the context. This is, I think, what we actually are going to request context here. So I think we're going to analyze that, and say, imperatively, not very declaratively, are you get, are you post. If so, apply the appropriate middleware. Good question. Oh, hi. Maintainer of stormberry here. This is so exciting. I hope everything works smoothly. But if not, you're here to help. Killian, I'm so excited that you're here. Yeah, I hope everything goes smoothly, too. This is-- I'm truly flying by the seat of my pants here. But so far, the documentation is strong. So nice. Build.yaml-- we're good there. Closing some files. Index.dart-- this is all so old. So we're focusing on database models on Dart. And I suspect we are ready to build. And we are. So I'll return here. We are in the database directory, which is where we need to run this build command. And I'll just add delete conflicting outputs, because folks suggest that you do that. I'm not going to lie, I've not studied the full difference. Alexander says, oh, and why no conduit? I think they're following up with a previous question. I'm guessing conduit is more backend Dart tech. Again, just, got to start somewhere. But conduit, server pod, any others you can get your hands on. If they feel strong, then they are probably strong. Dexter says, Windows has a clipboard history feature. Get it. Catch up, Mac. Come on. Come on, Tim. Have you heard about Alfred? It's a Dart backend framework. I heard about it right now. Thank you, Nathan. But I have not used it. I love that there's all these options out there. So we have generated our code here. And remember, last week, I said that I had configured VS Code to hide the build files. Well, that's actually kind of biting me in the behind a little bit right now. So let's print out what we have in lib source. And we see that it added this schema.g.dart file. So I'll open that db models.schema. Oh right, I am not in that directory. So I have to type all of these-- models.schema. Let's see what it made. So we have this extension on a database class. And database comes from stormberry core. And then we have a new model registry, which starts with a map. So obviously, this is going to be something that's mutable, which is fine. We're going to register the different models that are there. So now we have our user [INAUDIBLE] man, that tooltip, so aggressive. We have a user repository-- implements all these different things-- insert, update, delete. So we see the different CRUD operations that we can do here. Love that. A couple-- oh, a query for a single. Query for multiple. And this is very interesting. We've got this nice helper, this query params class. I was so excited. I like this. And here's some internal stuff, which we know we don't have to worry about, because it has an underscore. But just some more glue code, I guess, connecting things. So user queryable. See, that does not have an underscore, so we-- I don't know if we're going to get our hands on that or not. And it generated. Look at this. We have our full query here ready to go, generated off the fields in our database. Goodness, I just love it. An update method, a delete method, and then we get down to-- oh, I love this, too-- a specific class that encapsulates the idea of the request. I love doing this. I have-- when I-- like networking code on the front end, I'll have a single class that encapsulates any parameters that I need for every single network request that I make to the server. So it's like, maybe you can optionally filter on something. I'll just have a class that knows how to accept that optional parameter and pass it to the network layer. And it seems like we're going to basically be kind of getting that same very nice usability here. So this-- oh user queryable, we saw this a minute ago. So it extends this other thing. And it's got two types-- user and string. So this is just a lot of database glue code here. This really tells it-- I bet these things are going to fill in the query that we just saw. Update-- does this take a queryable-- Oh, the update request, I think that had a queryable, but I'm not sure. Where was the insert? Yeah, so this takes the database and user insert request-- well, I'm not 100% sure, but this looks really good. I love it. We have one question right now. Can we implement microservices using Dart? I mean, if we build backend server using Dart, is it possible that we do use microservice architecture? Yeah, of course. Just have smaller Dart Frog or conduit or server pod applications that are really targeted and deploy them to different Cloud Run services. Obviously, you can go the Kubernetes route, and it's really sophisticated at coordinating different microservices. But I am allergic to yaml, so I'm not good at Kubernetes. And Felix agrees. He says, good point, definitely. Oh, point is actually part of proto coders name. The protocoders point was the name. Wur AA says, there's possible SQL injection with these queries. We should replace every variable with dollar placeholders and use the args field in the query method. That is quite a good point. Let's remind ourselves-- oh, yeah. Look at this. So this database query method, let's look at this. Because the raw-- who pointed that out? Excellent observation. And I know we have the-- yeah, there it is-- Wur AA-- keen eye. So SQL injection is a nasty little trick where the user types a username like closing quote, semicolon, drop all tables. There's a great XKCD little Bobby Tables about this. And if they kind of land just right in the way you compile your SQL at the end, it can terminate. You just-- you're trying to build SQL with code like this, and a name drops in, and it's actually not a good faith name. It's something very clever. And it ends up terminating your query and starting another query, like delete all tables, or whatever, drop table users, and that's called SQL injection. And the way around that is you use the database bindings. They basically, now, just say, give me your query in one place and all your variables in another place, and I will ensure that they are not ever SQL injected. So let's look at the method that we're using here. This query method comes from stormberry. And this PostgreSQL result class comes from the raw PostgreSQL bindings. So we know that we're using that. And then we have this transaction context query. Oh, we have substitution values here that are coming in with values. But I don't think we were passing this second parameter back in our query. No, we aren't using that method. We are combining the query ourselves here. So I know we have the creator of stormberry in the chat, and I would take this as a big action item that all of these values here that we're kind of flattening ourselves, unless you are just sure that this encode method is going to guard against SQL injection. But even still, I wouldn't take that work on myself, if I was you. I think those values should be passed in to that second parameter, the optional values list here. That is an excellent point. Another viewer that ate their Wheaties this morning. But for now, I'm going to pretend that we don't know that, and that we are just loving everything that we see, and we're continuing with stormberry, because that problem is fixable. We have our user here. Let's return to the documentation and see how we can continue. Set up. So we did that. And this will generate this table, which looks like a nice match to the class that we made. You can also make any field auto increment. Well, I'm not going to worry about that. Relations-- OK, this is a huge part of why anyone would use relational databases versus document databases is, of course relations, and the join queries that you can write. So this is just a wonderful syntax for a join. It's just so simple. I'm very excited about that. Views-- I love a good view. That's, basically, like you can just save a query and pretend that it's a table. And you can do clever things with views. You can chain views. You can have views that depend on views. They can also become a caching layer. You can have the database live update them. So you don't have to run the query every time you access the view. So they're just kind of meta tables that can be a little ephemeral. They can do clever things. And a nice syntax here to add different kinds of views. So this would be the full user, I guess. And it includes the post. That's what we're saying here. So we add in the posts, and we can see that that's a relationship as info. I'm not sure what as info means, but I think we'll find out, probably shortly. And then there's another view, which is like a thinner view. So this doesn't have their address, and it doesn't have their posts, and maybe this one is suitable for other users to see. And this one's called reduced. I don't know-- I'm curious-- so we have the as parameter here, and we don't have it on this other one. So, I think, we'll probably see this shortly here. Oh, info-- no, not sure yet. Anyway, then there's some more views here. This has info. I wonder if these are supposed to match. I'm not sure. The above model would give the following things. Let's kind of work out what's going on with info here. I'm going to use the old Control F to hide all appearances of info. Info post view got connected to the complete user view. So we saw that here. So our complete user view, we're basically adding in posts. So this is, basically-- this is just going to bake in the join, essentially, saying give me all these users and to their posts, which, of course, is actually going to yield some interesting-- you're caring about the posts primarily, here, because you're going to get all that user data repeated. I'm going to come back to this, because I'm not worrying about relationships yet or views. But this seems pretty interesting, and I think it will just take some time to really look at and fully understand exactly what's going on here. And knowing for sure whether or not this name is required to match this name, or if they're actually just incidentally the same. Maybe another action item on the documentation for this. Serialization-- when using views, you may need to use serialization capabilities to send them through the API. While stormberry does not handle serialization by itself-- of course not. That would be out of scope-- it enables you to use your favorite serialization package through custom annotations. I love that, because we were already talking about using that freeze class and sharing that code. So I would love to be able to use that freeze class when I deserialize things from the database. Now this was just on view, so hopefully, there's a similar trick for non views. Indices-- well the primary index is already in-- or the primary key is already in index. But this is where we would add other indices. Great. Type converters-- yep, this makes sense. This is a very familiar thing for anyone who's used freezed or JSON serializable. Usage-- this is what I was looking for. So this looks-- this code looks pretty similar to what we had in the last round. So I'm going to copy this real quick. And I'll return to index.dart. And this was where-- no, it was in the middleware file. In this middleware file, I created a connection object. And this was the raw PostgreSQL library. So we're going to comment this out. and add in its place, the new code. This means stormberry is going to need to exist in the top class as well. I'm going to abstract this away better in the future. I don't want stormberry to be a thing that my top level library knows about. But for now, I'm going to add it so we can just get to a point where we see stormberry replace the raw PostgreSQL bindings. So I'll hop over to a server, pubspec. I'm searching in not the right thing. Server pubspec and add stormberry. So we'll add a library stormberry. While it does this, I'll take a look at some questions. Phaylali-- I hope I said that right-- asks, is Firestore for desktop possible in the near future? Let me click that. I gotta do the old double click again. Is Firestore for desktop possible in the near future? Well, Firestore for desktop exists on Mac OS and not Windows, as you know. And there are folks, including myself, and Eric Windmill, you may know also on the Flutter dev rel team, Kevin Moore, who are pulling all the strings that we can to make this happen. And that's all I can say right now. We're trying our best. What is the Flutter wrapper backend-- what is this, a Flutter app or a backend app using Dart, Lufti asks. Lutfi-- sorry. This is both. We're currently looking at the backend. But we want to share code with the frontend. So we're going to, I think, once this is all set up, have really a truly special setup that very clearly competes with other backends, especially with stormberry, the ORM. OK, nice. Killian has said, totally valid point. It's planned for the 1.0 release. I presume that goes back to the SQL injection and the substitution variables part. Killian is the creator of stormberry, and is kind enough to be joining us today. And Wur AA says, I prefer using clear Postgres, rather than stormberry, but stormberry is a great solution for parsers or any software that loads data in the database. Yeah. So I presume when you say clear Postgres, you basically mean just kind of writing raw SQL yourself. And I certainly-- I think, it's very helpful for anyone to know how to write those kind of raw queries themselves, for sure. Let's continue here. So in the middleware file, we, of course, need to-- well, the magic IP address is often swappable with local hosts, but sometimes not. Sometimes, it isn't. So we went with change me. I know, I remember like MySQL had annoying things about that where you could only use local hosts, not 127. I don't know. I don't remember. We can add stormberry now, because I added the line-- I added that import. So let's go to stormberry. And there we are. I think I will need to save this file, and then, oh, it's running [INAUDIBLE] on its own. Great. We were getting the depend on actual things. Some stuff that we can close right now. So we are now making a database object in our middleware. And we're going to inject that database. And this one is called db now. It's not the connection. So we're no longer making available to our URLs the full-- just the raw Postgres bindings. We're, instead, exposing this heavily wrapped-- this strong wrapper from stormberry. And if I return to index, I can no longer read from the context the PostgreSQL connection. I can, instead, only read the database. So I'll say final dB equals context.read database. And this will, of course, need to be imported. stormberry-- there we go. And we don't have a connection anymore. So there's a lot that we can't do from the old code. We also aren't going to just bother running, like, select now through it. So I'm going to return to the documentation and see how we can actually instantiate this table. We'll put a record in it. And then we'll read it, and show it. So all the parameters are optional. Well, we're going to provide them. Repositories-- a repository exists for each model on which you can do all the things. Great. You can also get a model's repository through its property accessor on the database instance. Oh, OK. For the above example, two views, complete and reduced, we'd have all these methods. Query complete views-- so that returns multiple things. Query reduced, plural again, insert, and those are into the views, as well. No. So we don't get CRUD into the views. That doesn't make sense. You still only write to actual tables. User for reading. And we saw that each method had a single multi variant. I like that. User insert and user update are special generated classes that enable type-safe inserts. Yes, we were talking about this earlier. So wonderful. So nice. With this, stormberry also supports partial updates of models by update one. And then we pass something. So this ID would match a record. And then, we would basically only change the name Tom on the record with ABC as the ID. Queries-- you can specify custom queries, actions, blah, blah, blah, and database migration. This is what we've needed this whole time. So let's do that. Let's run this Command and see what we get. I'm pretty sure that I still have my database running. Let's connect to it. And I launched this via Docker Compose. Yeah, this is where I ran select now last week. So we have the database running. And let's now run the migrate command and see what it does. So we don't need to type up there. That's right. Select a database to update. Oh, interesting. I'd like to be able to configure this so that this can be fully automated. But for now, I'll say Postgres, because that is the default database we were using. Oh, no. [LAUGHS] We've had a-- the feature already completed. Where is that happening? Where's this? We're in some raw stream stuff. Async. I'm not seeing any stormberry code in this stack trace. Oh, Postgres-- upgrade socket to SSL, is where this happened. I think the fact that this is a local host database and is available over not SSL is potentially causing some issues. Killian, if you are still here, does this sound reasonable to you? I don't know if you're still here. I saw someone say, Killian, have a good day. [INAUDIBLE] Someone says, here-- Vijay asks, can I use this-- can I use for backend API with Axios? I don't know. I'm not sure what Axios is. It's a news company, as well. Let's look up what Axios is. Axios-- let's see if it's a database. How do I make an Axios post to a database? Oh, Axios might be a networking thing. What is Axios? NPM-- here we go. Promise based HTTP client for the browser with Node.js. So that question was, can I use for the backend API-- oh, can you use Axios as the backend communication thing? And to that, now that I know what all the things are, I can definitively tell you, I'm not sure. Good question. I'll have to play with it and find out. TheFable asks is the db object secured since it has the password? And should that file be included in gitignore? Good question. This is some very proof of concept stapling together of the code here. I would pull this out of secrets from Google Cloud, for sure. Having the password, along with it even still being change me. Having the password sitting right here in source code is, of course, totally terrible. So I would pull this code out of SQL, or out of a Secrets Manager from Google Cloud. And Stefan asks, are we able to compete with .NET 6, 7 and/or node? Well, I mean, those have-- node has like 12 years of a head start on us. And .NET has, I don't even know, 30 years of head start on us. So I think Dart on the back end is always going to be pretty niche for Flutter developers. It would be a surprise to me if we really ever got to a point where non-Flutter developers even remotely considered Dart on the backend. So, I think, we're only competing with those things in a context of our own projects, the backends that complete our Flutter apps. And we're going to find out together, here, whether or not it's a good enough experience. I think it is likely going to be, but we'll see. So we have this error. And it's coming from the attempt to upgrade the socket to SSL. And, I presume, that this means stormberry thinks it's talking to a production database. But it is, in fact, talking to a local host database. So the first thing I want to do is go to stormberry and-- oh, db socket falls. Db ssl default, true. Where did that go? In order to continue to provide the following environment variables, is what it says. I'd like to do this without worrying about an environment variable. Let's see if there-- there we go. Use SSL. So the environment variable just serves as the default value for that. So I will say, didn't I copy this? Use SSL as false. And now let's try again. So this, we ran the migrate command. Engine Denise asks, can I watch this video later? You're too tired to follow along now. Yes, you can. It's going to be on YouTube after this. By the way, hello, everyone from the future. So we are still updating the Postgres database. And we've had another error. Upgrade socket to SSL did not get any better for us. So was there a socket command? Because there was a-- oh, is Unix socket. I don't know if we're connecting to this local database via Unix socket or not. But I don't know. Let's see here. One day, I'm going to get rid of the pub in that command. Postgres-- there's still an error. Phooey. Upgrade socket to SSL. I wish Killian was still here so I could ask him about this. Let's now go to the GitHub repo and see if there are any issues about it. GitHub stormberry issues. Let's see if there are any closed issues about it that might describe a workaround. Large connection parameters on, problem encoding-- no one else is reporting a problem with a migration, and that it is a surprise to me. Improve migration tool-- oh, here we go. This is promising. Except database arguments. Currently all database parameters must be provided through n vars. Oh. Oh, of course. The code that I write here is not being used in the command. Yes. And Killian was just typing that. Killian says, unfortunately the migration cli does not look at your in-code db variables, since it's an external tool. Of course. Yes. Yes. Yes. Yes. Duh. Well this changes things. By the way, Killian, another action item. The documentation here where you talk about running this command-- oh, I should make this a little bigger, shouldn't I-- where we run this migration-- and you say it right down here, provide these things. But even, potentially, below all of these db-- or all these environment variables, you could have another one that shows the syntax of providing those. That might be helpful. And it would help goofs like me who can't figure anything out. So we've got our same command here. So I'm going to copy this back in. I didn't have it in the clipboard. Copy. Goodbye. We'll go to the front. Let's get rid of pub again. And then we'll say db_SSL equals false. And this is one way we can provide these parameters. And then the db socket, we know that was false as well. So db socket equals false. And then our db-- was it just db_password? It is, db_password. For us, it is change me. And all of the other default values should work. So let's run this. It looks like the database upgraded to Postgres. So I bet we could say db name. Db name and give that Postgres, and it might just know this. We'll see though. Hey, we got a migration. Woo. Do I want to apply these changes? Sure do. Yeah. So now, let's open up Postico-- where you at-- and refresh. And we have a users table. Let's go. And we have a default view. OK. I don't know enough about views to know the value in having a default view versus just hitting the table itself. That's interesting. I would like to know more about that. This is-- oh, man, I'm really excited about this. So now we can go back. And first, let's just add one user ourself. Let's do that. And then we'll read it out and show it. So how do I want to do this? Yeah, in Postico we're going to add a new user. And they're going to have-- so let's say, add a row. And their ID, I think, the database-- the data type that we went with here was an integer, but it also was an auto increment. So I'm going to let the Postgres generate that value of one. And for the name, we'll say Homer Simpson, my favorite test user character. Could not Save Changes. Oh, well that's surprising. I expected-- So if we look at the structure here, it doesn't have-- it just says primary key. It doesn't have an auto increment thing on it. Oh. We saw that something-- there was a way to have tables-- there was a way to specify when something should be an auto increment. And I, falsely, poorly assumed that it was implied by primary key. And actually, now that I think about it, primary keys can be different. So I shouldn't have thought that. So I'm going to grab that auto increment declaration and return to our model class and we'll add it. And we get to really test an interesting flow here, a migration not from scratch, where we just see stormberry's ability to suss out the difference. So we'll now run the build command again. And then we'll run-- oh, OK. It failed to build the database schema. The following field is annotated, but has an unallowed type string. I didn't remember changing this to a string. I do want it to be a string. So then we can't use an auto increment. So it's time to decide, do we want to go auto increment, or do we want to go string and provide UUIDs ourselves. Let's do auto increment. We're going integer. And then maybe we'll have a UUID field separately that our frontend can treat as an ID. Actually, I love that idea. That is what I normally do myself. We'll get to that later. So we're changing a lot of things here. Now we're really putting stormberry in a tough place where we're changing the value of this column. So I almost don't even want to ask stormberry to get this right, but let's see what it does. We're rebuilding. And this table definition, which isn't in this window, this table definition of text here-- so this is just all wonky. Let's find out what's going on when we run migrate again. The migrate code to power this-- oh, yeah, I want to also test-- control A here, db name equals Postgres. Is this going to not ask us the database name. It did. All right, here. Wow. The database code to do this is really-- this is a lot of logic to write. I'm so impressed. So we're migrating this column. That's very good. I do want to apply. And it added next Val here. That is the auto increment. Wow. Very good. Oh, but it finished with an error. Cannot alter the column used by a view. Oh. I didn't even really want the view, I'm not going to lie. So this didn't work because we have the view, and I'm just going to delete the view. And we'll see what happens. OK, let's run it again. Let's see if it notices that the view is gone, and regenerates the view. It is going to do that. Let's see here. Yes. Update successful. We return to the database, and let's see what we have. So users now uses bigint. It has a sequence. So it's going to be auto increment. And the user's view has-- well, exists again. Great. Really nice. stormberry-- woo. This-- I can't even say, as a long time backend developer an old Django fan-- I'm still a Django fan, honestly, specifically for it's ORM. Man, this really just warms my heart. So now we can add that user. Content, and then add a row. This-- Discard Changes, because a lot of things have changed since that UI was correct. So now we'll add a row. I'm not going to type anything to the ID, but we, again, get Homer Simpson. Save Changes. The database gave us our value. We got some starred things here. Tomas asks, could we find your source code somewhere? Yeah, I should post that to GitHub. Last week's was so like immature and bad, that I didn't. But we're going to get to a point where I should post things to GitHub. That's a good point. I'll tweet about that later. ShadRack says, the Dart community is so positive and on fire. Yes, let's go. Killian, also earlier, helping me out of the-- yes, thank you for that. I was realizing that as you were typing it. I do that kind of thing a lot where an external cli-- it's like, of course, it's not-- it doesn't know that the changes that I'm typing deep in my application code, like obviously, that's not going to work. And it just takes me a while to realize it. Minnu says, more about serverpod. Yeah. So folks, when people ask about serverpod, I would encourage you all in the chat to tell them that-- maybe, one day, we'll get to serverpod, but in the interim, you should. That's the official stance of Observable Flutter on serverpod. Do not let me talk you out of trying it out. And Mouaz-- can I get a phonetic pronunciation of your name in the chat, by the way. I don't-- I want to know how to say it again. It's been a minute. But anyway, you're asking, can we deal with objects not as foreign keys-- we deal as objects not as foreign keys with relations? Yes, I think that object under the hood, it's going to make a foreign key. And Ernest asks, I just started learning backend this week with Django and Postgres. And today, finding this-- Earnest, sometimes, serendipity strikes. Django is the gold standard of ORMs, in my opinion. As the Dart community, I look at that as the place to which we can aspire. But what you won't get, what you will never get with Django, is shared logic with your Flutter app. That is exclusive to what we're working on. Antonin says, shouldn't you have a REST interface between PostgreSQL db and the client app? Of course, that's what Dart Frog is going to be. But absolutely, yeah. We're-- I'm just getting the raw bits, like the connection working, and getting a model going. But yeah, absolutely, 100%. So let's return to what we're doing here. We have made-- we inserted a record. And we just had a question about the REST database. For now, we have the simplest-- here's our index. We have the simplest REST app where there's only one URL, and it just gives you a user that's not at all implied by the URL structure. But this is a URL that's going to return us a user, and it will be-- this URL will not survive when we actually become more mature. But let's read a user. And so I'm going to return to the example code. And this was all on generation, indices, type converters, usage, repositories. So we've got our db object, and it has a users attribute on it. And we want like, select one. Query. Oh, we have to-- interesting. So yeah, this is where we need to understand the repositories again. So the repository exists on each model in which you can do these things. And you can get a models repository through its access or property on the database thing. db users is the repository. I'm so excited. So db.users-- Good. We're off to a good start. Let's reload this, because we've done generation, and I think we're just going to have stale intellisense. Or at least that's the thing that happens a lot. db.user-- oh, did they name the class plural-- users-- in the thing? Well, I never do that. No-- singular. Oh, maybe it's just a little typo in the docs. We can live with this. db.user-- there we go. Wait. No, I'm not sure. What is db.user? What are you? It's a string. This is the connection information. That ain't it. That's not helping us. Interesting. Why are we not getting db.users? Nothing. Let's look at our db models. Odd here. So code db schema. Let's open this file again. And we see that we have these repositories extension on database. Do I have to import-- I am importing. Because this is an extension, and they only become available when you import the file, I think, I actually have to import db models into my index file. But I had thought-- oh, no, I'm just not importing db at all. db-- and we'll put you in the correct sorting order. And now, let's remind ourselves what db lib, db.dart says. And it says export db source, which is not correct anymore. We called it db models. So we'll export that. And now in db, it will find that extension, and I think we can say users-- Nope. I still think this is what we had to do, but it's not quite working yet. And that could be because I just updated the export path in a nested file again. So I'm, again, going to reload just to rule that out. No, it still isn't seeing it. db.user only. OK. It's there. We see it. We've got this user repository. Yeah, no, this is the thing. Let's import the file itself directly, which should be redundant, and I do not want to have to do this, but lib, [INAUDIBLE] oh, no, we don't type. Do we do a source? What do we do here? db-- Yes, source. And then db models. Now we're definitely bringing this in, db.users-- man, nothing. Am I missing anything here? Repositories-- no. I don't think so. Query complete-- yeah, we're not getting to those yet. We can't even do this. Let's look at the issues, see if there was anything about that. Custom view, or custom SQL view, or whatever, a generator, more coherent in existing generator packages. Well-- Oh, I see, it would just be more similar. Type degree, virtual views, connection pull-- yeah, connection pull. That will be important. OK. Is there anything closed about this? So our problem here is that we aren't even able to import-- [INAUDIBLE] sense isn't finding-- can't generate schemas from many files. Mark field as auto increment. Escaping variables-- Default view is missing. Oh, this was a thing. So this person wanted a default view. No default view is generated for a model without views. But I guess it shouldn't be a view or a query, in that case. So this person, who's Killian, he knew why he wanted this. For a second, I thought maybe it was an external feature request. But no, Killian, himself wanted that default view. I still don't know why it's a thing. Killian, feel free to, if you're still here, say something in the chat about the value of that, because I honestly don't know. I'm not seeing any open or closed issues about this problem. So I'm really going to have to put on our thinking hats. Let's look at the chat and see if any of you have any ideas. I'm talking about Firebase, which is a great. Great library-- AMR. We are being respectful of other technologies. That's a hard, no. Mouaz, you typed your name again, but it's just the same spelling. So I do want to figure out the correct way to spell your-- to pronounce your name. But I don't-- I'm not sure I'm saying it correctly yet. In the chat, I'm not seeing anyone with-- is db database? Ah, Killian, you're here. Woo hoo. Yes. So the extension is within the generated file. You're importing the wrong one, someone says, but-- oh, is there a no part file? Oh, of course, there's no part file. I didn't add that. I wonder if that's how we should do it. This is absolutely the problem. Calamity 210, you're on it. Thank you. You're smart. So I was coming at this from the freeze, like mindset, where that generated file is always a part of the main file. And let's see if that's how we should handle it. No, because this has its own imports. And it imports db models. Oh, definitely not a part file situation. That is it. Thank you, Calamity. 10 points for house Gryffindor. So we can now go to our db source, db.dart. And we will export db models.schema.dart. There we go. This is why it's great to have backseat help. Don't need you anymore. Now there, we already saw it. It knows what we're talking about. Incredible. Query-- now what did query take? No, query is too low level. I don't think we want query. Update one, update many-- I want like-- Oh, query user, that's what I want. And what do you take? Just an int ID. All right, one. I love it. Oh, wait, and this will be final user equals that. And now user is-- we just get that user object. Amazing. Now we have a conflict between our different users. So I'm going to get rid of the shared directory right now. And then, we'll return to that in a second. So user.email doesn't exist anymore. I think we called it user.name exclamation point. What's the problem here? User.name can't be accessed unconditionally. Oh, user could be nothing. I see. Oh, interesting. That's a good point. So if user equals null, then we will return a different response. So we'll say body not found, and this should be single quotes. Wrong character to type. And then, let's add a status code-- there we go-- of 44. Now we have type promoted our nullable user to a user. And the current time is not a thing we're worrying about anymore. This should say, welcome to Dart Frog, Homer Simpson. TheFable asks, Oh, my gosh, TheFable coming in hot with, literally, the video game Fable as your icon. That's so good. So what was the problem? I think I missed something. This is definitely a good one to go back and hit. So essentially, all of the good code, all the stuff that we care about, is sitting in this schema file. That's what was generated by stormberry, and that's where the extension is. db.users becomes a thing because of this line of code right here. And database extensions-- in Dart, extensions are not visible in calling code, so they're not visible anywhere else, unless you explicitly import the file. They don't magically attach themselves to the class. That's what views will do, which is a feature coming soon in Dart. But for now, until this syntax change is to be views, and thus, it would attach itself and be visible everywhere, I always wonder is that name inspired by database views, and I don't know. But, boy, are they similar things. While it's still an extension, it is not going to be visible to outside code. Now I-- almost all code generation that I do is from the freezed direction, and in the freezed syntax has that main class, include as a part file, the generated code. But this just goes differently. The generated code is actually its own thing, and you have to import or export that directly. So in our export file, in our nested package, we simply had to explicitly export to expose all of the generated code. And that meant that our index file was finally able to read-- here it is-- db.users. Good question. Thank you for asking me to hit that again. So you have to explicitly export the file and explicitly import it as well. Yep. Yeah, oh, I think I see what you're saying. Well, you-- not really. So kind of. I mean, you definitely have to explicitly export it, which we're doing here. And then, of course, you have to explicitly import the file where you export it. So that's the, yes. And we're doing that here by importing db/db.dart. Good question. Oh, there's some starred ones. I'm going to get to those in a second, after we see this work. Let's reload here. We've done a lot. I'm just going to really do a hot restart, as we would call it in Flutter parlance. We are live. And if I refresh, welcome to Dart Frog, Homer Simpson. And now the next question, because we knew that we had a really terrible database setup, where if I hit Refresh right here, it would just immediately error. But I bet stormberry handles that for us. So I'm going to refresh. And it works. Yeah. Let's take a look at some questions. Tozzi says, hello, I'm a web developer. I need desktop applications. Do you advise me to learn Flutter? And, yes. Why, yes, I do. Flutter is great for this. Now if you are a web developer, you may already know JavaScript. And if you need desktop applications, Electron is, of course, a framework to consider. And you should also, especially if you're a JavaScript developer, don't just gloss over Electron VS Code. One of the most important apps in my life is made with Electron, and it is so performant and just great in every way. So don't pass-- don't sleep on Electron, but yeah, Flutter has got incredible desktop support. TOKwe says, probably instead of using the code as a source of truth for the schema, it is better to write the schema in SQL files yourself and use flyway to migrate safely on a new database versions. Yeah, this is definitely-- actually, let me switch to-- let's switch here while we look at questions. How to handle database migrations is a really interesting question, and it's not trivial, by any stretch. And a lot of times, again, I come from that Django Python background, and a lot of times, there, it has actual files that it generates that handle the migrations. And so, kind of run the Django command, generate those files, and then, we'll actually apply those files separately. And you can test them and look at them and make sure you know what's going on. They're version controlled, all that good stuff. And that is definitely very nice. And sometimes, even, really kind of getting to what you're talking about here, sometimes I'll generate in a shared kind of Python Django project, I'll generate those files, but that's actually just for all of my teammates who aren't otherwise going to easily get their local databases to get the changes that I'm introducing. So once they check out my code, they'll get that half of my change, but their database won't know the difference. So I'll use the Django migration kind of pipeline to catch up all of my teammates' databases. But then on the production database, depending-- certainly if it's a high traffic table, or something like that, you visit it with care and really assess, OK, does this query need downtime? Is there-- maybe we can add the concurrent flag to the query, or something for Postgres so that it can alter the schema safely. But that's a great point. Jay asked, can we integrate Python code with Flutter? Well, kind of. You can write Python-- you can write a server app with Python and send it network requests. And in some sense, that integrates the code. Can you integrate it more directly than that? Not easily. I'm not like a C and FFI expert, and whatnot, to know really where or all the boundaries there. But I'll just say, not easily. TOKwe then, adding to his previous comment says, and then use a framework only to map database rows to Dart objects. Yeah, I think that's totally valid. The migration tool for stormberry looks functional, strong, but like there's-- obviously, I don't think Killian considers it robust and totally perfect yet. I think you'll need some kind of pipeline for those database migrations to get those schema changes to your teammates. That need will remain. RogueTravel asks, one of the reasons why I don't use Firebase is the lockin when migrating databases. Is there a happy path for migration to a relational database within GCP? So at a minimum, to migrate off of Firestore, which is not a thing I'm advising everyone do, you will have to pay the read costs for all the records in your database. And you'll have to write the application code that figures out how to translate those document collection-based concepts into relational concepts, which is an enormous task. So if you're already using Firestore, you do have some lockin, in that it's just hard to move off. But the data is always yours, and you have to pay a little bit to read your entire collection. So that's definitely a thing. One thing I'll point out, though, is you have database lockin no matter wherever you go. Now Postgres translates, so you could pg dump your Postgres database and upload it somewhere else. You could have a Postgres database that runs on any Cloud provider. So you're not really locked into who you pay a bill to, but it's like, if you wanted to get out of Postgres, and use something else, it's not going to be much easier than getting out of Cloud Firestore. So now to your specific question about, is there a happy path for migrating to a relational database that's also in GCP. If you have a pretty big project, you might have a contact at Cloud, and I would ask them. I'm not an expert on that. That is a good question. Daniel asks, do you have to use db connect or something along those lines? I think stormberry is doing that under the hood for us. But we did pass it all of the raw credentials, so I mean, it is certainly doing the same thing. All right, Killian, on your way out six minutes ago. Thanks. What was that-- Oh, can we inspect Chrome? Yeah, sure. So this was our source code. And then we can also look at our elements. And this was the HTML that came out. And let me make this a little bigger. Oh, I'm not on the right thing. Here we go. Here is what was returned by Dart Frog. Let's return and think about some of this code a little bit more, because I still want to have-- code sharing is the whole point of this. And we just introduced a new user class that we will definitely not want to bother our frontend UI with. So I'm going to end today's stream by thinking about and, hopefully, solving in a reasonable way how we're going to handle that. So let's think here. Our user class, that's-- let's look at what we generated here, because there's a possibility that there's some user class in here that is actually unaware of databases. And if so, we could use that, maybe, on our frontend. That could be the shared code. And it would be a real shame if I missed that-- oh, user view. But it doesn't have JSON serialization or anything like that. So we would have to add that. But I think this user view will be the class that we access when we think about our bridge between the database aware code and the shared code. So user view is going to be our starting point. But it's not our ending point, because it doesn't know how to call to JSON or from JSON or anything. And we do not want to write those ourselves, so we aren't going to write our own extension class and just write them. We want to use, at least, JSON's serializable and probably freezed, as well, for the whole thing. So let's think about this. We already have our JSON-- we have our freezed class. This lives in shared models.dart. And it's already out of sync, as well. We've got email, whereas in the database class that we made, we called it just name. So that's already a big issue. We are out of sync, which is pretty annoying. We can potentially handle this with tests. But even still, you just don't update the test, and you get false positives on all of your stuff being in sync. We really want a build command to ensure that this is correct. And that going to be an interesting problem to solve. How do we want to do this, because in theory, the frontend could know about-- oh, wait a minute. Wait a minute. This user view class, let's see if we can implement user view with our freezed class. Oh, this might be the trick. Let's try this. So in our models class, let's add implements user view. And we will have to in our shared code-- of course, the shared code will have to know about the database. That's totally expected. So shared pubspec.yaml because it is going to have to translate database objects into the shared log domain space. So here we will add db, which is the path out of the shared folder and into db. And stormberry has a version here. Oh, freeze depends on analyzer. And stormberry depends on analyzer freeze 2.0. Oh. Stormberry is using an old-- stormberry is pointing to an older version of the analyzer. And I am not going to go back. I'll add publish to down here, make that happy. Publish to none. Is that what it wants? Yeah. There's a way to force this, right? How do you do that? dart pub get-- just like, I don't care. Do it anyway. Let's go to here. And in our shared folder, let's say Dart pub get-- I don't know. Oh, dart pub get help. Let's see if this tells us how to force it. Offline-- report what dependencies. No, no, there isn't one. I think we can't force it. Does anyone know how I should get by this? And I'll look at the chat real quick here. A lot of debate about different platforms. We've got a couple of issues that we can file with stormberry, including the version that we need to bump for the analyzer that it depends on. If I go to stormberry-- that I am using the latest version, of course, right. I just added it via the command earlier. And if we go to its GitHub, and we look at its-- let me make this a little bigger. We look at its lib directory, no pubspec.yaml. Where is the analyser? Oh, less than five. Oh, boy. That is a painful one. How old is this commit? The last commit on this file was yesterday. I wonder if this is actually required, as opposed to just a relic that hasn't been looked at. Where's the history? Oh, just a version bump is all that we've had. But these included-- of course, they include the pub spec, because that's where the version is specified on this line. I don't have any great options for this. I'm wondering, should I check out-- oh, dependency overrides. Oh, that's the thing. Yes. Dmitry, great. Let me look that up. Dart Dependency overrides-- didn't have it in my clipboard apparently. I thought there was something. Dependencies override. Ooh, there we go. This should do it. So this is, presumably, just a dev dependency. I don't know. I don't know if there's dev dependency override, so we're just going to do this for now. Dependency overrides and this is the analyzer. And I think it was 521. So in our shared directory, we ran pub get and we didn't have an-- oh, 3.1. Overridden dependencies-- it was 3.1, OK. OK. Cool. Cool. Cool. So now we can get back to having a user class that implements user view, and we can import user view. That wasn't the button I meant to press. We've imported user view. So if I were to run code generation right now-- actually, let's see what our error is. Awesome. It is not defined. Great. Get out of here. OK, next error. Super interfaces don't have a valid override for-- see, these are the kind of errors that I want. And I'm missing implementation for name. Great. Great. Great. Great. And I think this override thing might be a similar problem here. So let's just change this to a required string name. And if I rebuild now, let's see if that makes everything work. And I want it-- Oh, it's showing me more tests. Get out of here. I don't have any tests yet. I will in the future, I promise. In our shared directory, let's build dart pub run, blah, blah, blah. Do I need the pub? Is that what it always barks out? We just run Dart run, right? Dmitry, I appreciate you having the right answer. It's building. It's building. We still have one error. So the name parameter email is not defined. Yeah. This is now just name. OK, that's good. You can go away. Super interfaces don't have a valid override for ID. Yeah, I'm not 100% sure what it is that it-- What is the thing that we're not overriding. It says here-- we'll make it a little bigger for everyone, even. Super interfaces don't have a valid override for ID. Oh, it's nullable. I wonder if that's the problem. Because remember, we had the whole should the ID be nullable or nothing. And ID is an int in interface. There's a string, Dmitry says. Did we make it an int? Oh, we did make it an int. Yes. Thank you. Because I was going to have the UUID be the string. Again, Dimitry, you are like a hawk. How much do I have to pay you to follow me around all the time? OK, there's all the rubble. Oh, it has to be required-- of course. It works. Hey. Hey. All right, we now have Daniel Lutton also catching that it needed to be an int. Great job, Daniel. We have a shared code-- shared file between our frontend and backend. And let's actually switch back to Flutter and make a network request, get the value back, and then show it in our UI. That will be the really cool conclusion. So we've got our server running. So I'm just going to close all these files. And we're going to return to our main.dart file. So we're back in Flutter land. Let's make this a little smaller-- too big. This whole user is silly, of course. We don't need this anymore. What we're going to do instead is open up our clients pub spec file. And let's add a dependency of HTTP, that's what we want. Add dependency, HTTP. 13. Very good. Now we're back in our main file. And let's add HTTP as HTTP. And we alias that just to avoid conflicts on names and whatnot. It's mad that I'm importing it, but not using it yet. That's a short-term situation, because now, in an int state, I'm going to say http.get, and we're going to go to localhost:-- I don't remember the port-- 8080. And that is actually it. Oh, wait. This has to be in another file. Actually what we'll say is, the user is now nullable. And then we'll just use .then. So this is going to get our response here. And what we can now do-- Oh, you know what else we have to do-- index.dart. We're back on the server side, and we are not going to return the raw server. We're going to return-- we have to turn it into domain logic and then serialize it. So our body here-- we've got a couple of things here. I'm kind of all over the place. Let's recollect ourselves. On the server, we have a non-shared object. This is the database user. I also really need to change one of these class names so that it's more clear. Maybe we could call our shared one, literally, like shared user, or something. I don't want to do that. Actually, we'd namespace the database one. We'll call that db user. But I'm also going to do that later. Anyway, this is the database user. We have to turn this into the shared user. So now we're finally going to get to put on that ResoCoder hat we talked about a long time ago. So if I open up shared models.dart, here we're going to add a new class, and this will be from domain, I guess. So from-- or it will be factory-- or from database user.fromdb. And this will take a-- we're going to need-- let's add this as db, just so we're really starting to namespace everything here. So this is going to take a db.user view. And it'll be the user, and we'll call it the db user. And this will return one of its own things. And it will say the ID is the db user.id and the name is the dbuser.name. So now this constructor, of course, will bark at us if we add fields and we forget to add them here. So we're type safe again. We can use a little tighter syntax. We do need that semi-colon. So we have our translation. And now, so this says, db user turn into shared domain user. And we need that for our JSON serialization and our copy with, all those other things that are application level only. The database does not know about copy with, right? This is why we have this distinction still. So we can now turn our-- so we'll say, final. We'll just call this the shared user, even though, again, our naming convention isn't great right now. And this is our database-- no, this is a user .fromdb, and we pass it this. Now the user class-- I think we have to actually run the generator again. And we're importing. And we have to import shared again. Oh, yeah, now we have that conflict. So this will be as db again. OK. Here we go. So user-- it might be a user versus user view thing. Yeah. This doesn't return user views. It returns users. Well, that's OK. This can just be db.user. So now we have our shared user. And now our shared user we can say to JSON. And then we'll have to stringify this. So we'll import encode. Convert. And then it's JSON.encode, or something. I never remember. JSON encode. Are you right? That is right. So we took our database user and we converted it into a application-level user. And then that's our shared user. That goes everywhere. Then, we take that shared user and we convert it into a pure Dart map right here. And then we convert that map into a string so that it can actually go on the wire. And let's add a header, by the way of, content type, JSON. So headers, we'll add content type. And this will be application JSON. This should help our frontend make sense of all of this madness. Now the application is reloaded. So if we return to our app and refresh, it isn't doing what I expected. Did I not save the file? This is a surprise. Let's run this again. Dmitry asks, what's the difference between single quotes and double code strings in Dart? Not much, other than the linter just picked one to favor. And so, I do what it says. Refresh. What's going on here? You can return response on JSON, someone points out. Amazing. Does this take a map, and does it convert it on its own? Oh, it takes the map. Woo. Love it. So we now do this, I guess. Body. Here we go. That's a great tip. This will likely also imply the header. Very good. But why is this still just returning-- Oh. No, it is a JSON. I thought it was still the string. Interesting. We are almost there, I think, folks. Because now, we can return to JSON or to our Dart app and type string. Oh, URI.parse-- yep, I saw someone mention that in the chat. URI.parse. We can't just be throwing around raw URLs anymore. Who was it that said that? Forgot URI.parse-- well, I was looking for it. But oh, there it is-- Matt, Matt Souza, good eye. URI.parse. And our response here, we can now say that our late user, well they're not late anymore, they were just nullable-- user equals user.from JSON. And this is our response-- .body. And we probably have to cast it or something. Oh, we have to decode this. So we'll add convert here. This is Dart convert, because this is a string still, and we have to turn this into an actual map. So this will be JSON decode. There we go. And we're not checking our status codes, and whatnot, because this is-- you don't make network requests in your int state. We're obviously not having a very robust implementation here. We're just connecting the technologies. And then once we have a user, we'll call setState. I guess we'll just do all of that in setState. I accidentally pressed Command up, instead of up. So we'll call setState. Here's an oldie, but goodie. Finally, a familiar face. setState-- gosh, I can't type to save my life. There we go, folks. So we have the user. Now down here, we'll say, if user equals null, then, I guess, we return the spinner. So that's the circular progress indicator.adaptive. And otherwise, I guess, I don't know if you use the l's. I'm not sure. Otherwise, it'll be user-- why is ID-- property ID can't be accessed unconditionally. Oh, got it-- not indented. It's an attribute. It can't-- I see. Const class properties. Con, from the Flutter deverel team had a great decoding Flutter about this, about how you can't type promote out of null ability class attributes. So even though I just said right here, that it's not null, there's nothing to prevent that from having changed. But let's print the name instead. That's going to be way more exciting. We don't need to do this, I guess. We can just do this. Great. Holy smokes, folks, this may work. Holy smokes, folks, also has a pretty nice jingle to it, doesn't it. Out of shared. Out of packages. Out of server. And into client. And we will run Flutter. Run on Mac OS. And big money, big money, big money. If I've been paying attention, this might work, which would be immensely exciting. And while it loads, I will look at the chat. If I suggest Nest JS with Flutter. I'm unfamiliar with Nest JS. OK, it's spinning and iterating. And the problem is that the connection failed. Phooey. Oh, I know why. The connection failed because of entitlements. I've been here before. I've wasted time on this. Gill-- we need to open this directory. So in the client, here, we need to go to Mac OS, open our Workspace. This will actually launch Xcode. Please don't make me install anything right now. Oh, I have-- no, that's built in. So I'm good. I don't need to install anything, I think. I don't know if it's going to install. I really don't want it to. Lord, have mercy. What a nightmare. Please install quickly. The world is watching. Not really. There's just a few of us here. Xcode is turning on. We have to tell Xcode A, this is an app that can make outbound network requests. If you don't do that, you can't do that. So Xcode-- yeah, launch. There's another window on the other screen where it's like, do you want me to load? Yeah, I do. What's going on here? Xcode, open this window. Here we are. And now, we go into runner. No. The top runner, not the bottom one. That'd be silly. Signing and capabilities, outgoing connections. So we've added that entitlement, and now we can build again. Woo hoo. This time, it might work. Team, we did it. We've got shared code in our Flutter app on the frontend and in our Dart Frog server-side app on the backend. They are talking to Postgres. They are loading a record. They first load it into this database object. That database object then gets converted with a little domain-driven development style thinking, gets converted into our shared user. I don't endorse this naming convention. It's quite poor. That shared user is intelligible by both the front and the backend, so we return that. On the frontend, we load it from JSON. And what's critical here, the reason we care about having this shared logic, and the reason it was a win to have our shared user be tied to the implementation of the database user, is that so we can go into our database file and into our models file database folder, models file, we can add something new. We can run the migration command. Migrate. Can't find build yaml. Of course. I am in the wrong folder. packages shared. No. db-- now we run it. And it's going. No changes. Oh, we have to build again, of course. A couple of steps here to show the payoff. We build, then we migrate. Now it should say, I'm going to add a new column. Amazing. That's exactly what I want you to do. Yes. Transaction error-- column email of users contains a nullable value. So this is just a database issue. This is not actually-- this is not a problem of stormberry column. It contains null values. That's right. So we'll just make it nullable, just for now. So I'm going to regenerate. And then we will rerun the migration tool. Basically, I instructed stormberry to do something incoherent there. So hopefully this now works. There we go. We have the nullable column. We applied it. We're good to go. But we can't forget to add that value to our shared user. This is the payoff. We must add email to our shared user. And this will keep everything in sync. And once I add it here, in run build, let's just do, by the way. So this will be string. Let's give it a default. Actually, this will be-- yeah, default of empty string. And then not nullable on this end, email. And I say, aha, I've done it. Now I'll make everyone happy. And I return to shared. Out of db, out of packages-- no, not out of packages. Out of db, into shared. And I say, I'm so smart. Let me build again. And I do. And I'm all snooty. Oh, I made this default. But let's say I didn't make it-- I didn't provide a default value here, then this would have errored. And it would have said, hey, you forgot to include that field in your from db translation method. And then I would have added it there. So we have some excitement here. I'm going to close by hitting some questions. Akilesh, which I hope I'm not totally butchering, says, can we write code through open API and implement in our Flutter app, and will it work or not? I have not used much open API stuff. I know, actually, Brett Morgan was just looking at that. I saw some messages from him earlier today about his exploration on that front. And Brett and I are going to be working together over the next few months on some new entries on the cookbook, on either Flutter.dev or Dart.dev. We haven't really decided where it's going to go yet. And they're going to explore a whole menu of options for this kind of stuff. So you might find some answers when that lands. Good question. Shivam says, can you suggest a structured way to follow the Flutter development path? Sure. For my money, the best way to learn something new, and this is kind of tightly coupled to how I personally learn, but I really learned great from videos. But not videos like what I'm doing right now. This is not the way to have a structured learning experience. Take a course. [? Van don, ?] the Flutter GDE in Stockholm, I believe, has an amazing free course on YouTube. It's like 40 hours or something like that. 37 was the last number I saw, but I wouldn't be surprised if he's continuing to extend it. And he's an excellent teacher. And if you want to have a structured path through learning Dart and Flutter, something like his course. There's also paid ones. ResoCoder has one. There's a ton on Udemy. And the paid ones, they're paid, but they're still very cheap when you consider how much value you're getting for like $20 on Udemy, or wherever you buy the thing. And if you just work through, watch every video, do the exercises, you will come out the other end, not an expert yet, but actually ready to work on your own project. And you'll have an idea of where to go, what to type, how to get started. And, I think, you'll be successful. Yeah, JSON viewer extension in Chrome to get a better experience, Gabriel asks. Yeah, absolutely. I think I just don't have it in my work persona, but I always use it in my personal ones. And that's a Chrome persona. Islomkhuja Akhrorov, which I hope was within in the same galaxy of how your mother would say your name, says, that's a lot of boilerplate code. Serverpod generates the client code for you. Oh, if serverpod generates client code, that is pretty interesting. And, man, the audience-- you guys are all-- there's just a steady drumbeat of, we must look at serverpod, as well. So I think we're going to have to look at serverpod on Observable Flutter. And can I write state management, Mohammad asks? Yep, we're going to have to do that at some point, but not today. So stay tuned. Mouaz asks, and what is the response code from-- and what the response code from localhost? Thanks. So I think you're talking about here, checking the response code, which would be 200. But yeah, I just kind of glossed over that. Muhammad asks, can I share a roadmap for Flutter? Well, there's one published. Tim Sneath publishes a Flutter roadmap every year. And I think for Dart, as well. So the full vision is out there. And I would just have to Google it. I forget where it lives. And Hector-- Hector is heckling by asking, what is the best state management? There's a lot that are great. But start with either, river pod or Flutter block. That's my recommendation, which is purely based on the fact that those are the two that I use. But this is the forever topic. All right, folks, we're coming up on two hours, so I think this is a good place to call it. Thanks, everybody, for tuning in and showing up, asking questions. I think we got some pretty good stuff done today. I'm happy with our progress. And next week, the schedule might be a little different. I have a conflict on Thursday. So stay tuned for info on that. But for the most part, it's going to be a large percentage of Thursday's, this time. So I will see you all in the next one.
Info
Channel: Flutter
Views: 37,561
Rating: undefined out of 5
Keywords: dart, 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: g76H6-MeHHk
Channel Id: undefined
Length: 115min 17sec (6917 seconds)
Published: Thu Dec 08 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.