Observable Flutter: Serverpod

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
CRAIG LABENZ: Hello, everybody. Welcome to another episode of Observable Flutter. My name is Craig Labenz, and I'm your host. And today, we have a pretty exciting episode lined up, if I don't say so myself. I'm joined by Viktor Lidholt, the CEO, CTO, CFO, COO, and probably other things of Serverpod, the backend-- the full stack, sorry, not just backend-- the full stack framework for Dart and Flutter that if, like me, you haven't tried either recently or in a while, it is time to learn about. So Viktor, welcome to the stream, and would you like to say a few words about yourself and maybe the project? Oh, I think you're muted, Viktor. We had a little 'stretch our legs' break before we started. VIKTOR LIDHOLT: Yeah. All right. Now, I am here. Awesome. CRAIG LABENZ: Oh, yeah. A few words? Yeah. VIKTOR LIDHOLT: Yeah. Thanks for having me on the show. It's pretty awesome to be here, and-- yeah, my name is Viktor. I'm the founder of Serverpod, but I am actually not all of those things, because we are building a whole team around Serverpod, so that's pretty cool. CRAIG LABENZ: So you've relinquished some of those titles. VIKTOR LIDHOLT: Yes. CRAIG LABENZ: No longer all the Cs. VIKTOR LIDHOLT: Especially all the financial stuff. I also want to give to someone else. Paperwork is like the dark side of life, I feel like. CRAIG LABENZ: Yes. VIKTOR LIDHOLT: But-- CRAIG LABENZ: That all is life administration, and it's the worst. VIKTOR LIDHOLT: Yeah. So-- yeah, I actually used Flutter for quite a long time. I was on the Flutter team way back, but I used Flutter since then to build my own apps, and I was really missing having a good back end for it, so that's where the idea for Serverpod came from. So I basically made the framework I wanted myself to build my apps, and being able to use Dart for the full stack is pretty nice, and we tried to make it as easy as possible to get started, and really-- Serverpod itself may be pretty complicated on the inside, right? It will analyze your code and build APIs and stuff, but we wanted the APIs to be very, very clean and easy to use. So I guess our aim is to do you know what Flutter does for app development to server development. CRAIG LABENZ: Nice, nice. OK, so part of what you-- part of what Serverpod offers is a really nice-- really nice deployment story to get everything running on Google Cloud. VIKTOR LIDHOLT: Right. CRAIG LABENZ: You also support AWS. And today, we're going to build a little thing. I don't actually know what it's going to do yet. VIKTOR LIDHOLT: We'll figure that out. CRAIG LABENZ: And-- yeah, we'll figure that out-- and then we're going to deploy it to Google Cloud. That's the goal, right? VIKTOR LIDHOLT: That's the goal. CRAIG LABENZ: OK, all right. VIKTOR LIDHOLT: So we actually did some-- yeah, quite a bit of work to get everything, like, the whole story. I think many other frameworks, they're focused on one specific detail. It's like you have maybe a database integration, or you have a server, and then you have to build your APIs that works with another language or something. We wanted to make that whole journey fit together really nicely, and all the way from when you create your project to how you deploy it to the Cloud. So that's one of the goals with Serverpod to have everything pre-packed for you. So it makes the system a little bit opinionated, which some people will like, or some may not like, but I think it will definitely save people a whole lot of time. So you may not need all those bells and whistles from the start, but it's certainly nice to be able to pull them in when you need them, like if we're talking about things like caching data or having support for read this to if-- when you need to scale something up to run on more servers, to have them being able to communicate with each other, and stuff like that. It's just nice to not have to worry about those things that you will have to at some point worry about. And if you haven't thought about those from the beginning, it can be pretty hard to integrate your system. CRAIG LABENZ: Yeah. Yeah, it absolutely can if you-- yeah, it just absolutely can. If you don't think about scaling up front then eventually-- [CHUCKLES] once you get some users, that problem can become a little more complicated to solve. But if you then-- if you think about scaling up front, it can bog you down and slow you down from kind of having something to actually get into users' hands, so of course that's why frameworks like this are so great. OK. Should we start? I don't think Chat is turned on. I'm messaging people about that. OK. All right. So folks, what I'm hearing is that you might have to refresh the stream to see Chat, to see the ability to say anything. So, sorry about that, but it should be working now. OK. So let's do it. [HUMS] All right. I see the first message in, so people can see everything. OK. VIKTOR LIDHOLT: Awesome. CRAIG LABENZ: So, Viktor, I'm going to be driving. That's what we're going to do, and you're going to be my co-pilot. VIKTOR LIDHOLT: Right. CRAIG LABENZ: So I'm going to switch over to this stream-- oh no, this isn't the screen I want. This isn't the screen I want. I need to share my window. Share Screen. The entire screen. And it'll be this one, and then we'll add this. There we go. That's it. This is what we want. VIKTOR LIDHOLT: All right. Awesome. CRAIG LABENZ: So the adventure begins. I'm going to-- I don't even have Serverpod on my computer yet. I tried it on a previous work computer, but have never-- haven't tried it since upgrading. So this is the website for anyone who has not heard of Serverpod, is brand new here-- Serverpod.dev-- and all I've done so far is just go to the docs.serverpod.dev page, and scroll down a tiny bit, and I'm ready to install. So I've copied this, and I have a terminal window that I think I'll move into the same space here, and make this a little bigger. I love how it makes the whole thing bigger, too. Terminals are so archaic. I understand that this is how it needed to be in, like, the 70s or something, but I don't know why we carry forward that kind of silly functionality. Anyway. VIKTOR LIDHOLT: Right. CRAIG LABENZ: OK, so I'm installing. Now there is a little other setup that we've done just to save time. So let me-- [TUTS] I'll get to that in a second. We created a database in Google Cloud, that was the big thing-- got that started because that takes a minute. OK, so now we'll test our installation by running Serverpod, and-- well, it seems to have worked. VIKTOR LIDHOLT: That looks good. CRAIG LABENZ: Great. VIKTOR LIDHOLT: So we're pretty much ready to create a project there. CRAIG LABENZ: OK, so continuing to scroll down, and we're kind of skipping over this-- I did also download this Insights app. This is your companion introspector, database viewer-- whatever. Actually I don't know about database viewer. I see method calls here, like HTTP calls. VIKTOR LIDHOLT: Not yet, but it's coming in the next version, so that's what we were working on right now. CRAIG LABENZ: Queries, though. VIKTOR LIDHOLT: Oh, yeah. Yeah, so it actually-- it's pretty cool how it logs. We can walk through that in a bit, but it will-- you can log every query that happens, and it will actually store a stack trace together with the query, so it's very easy, if something is slow to find-- pinpoint those points. [LABENZ HUMS] Or-- I mean, it can be other reasons too, to find stuff. CRAIG LABENZ: All right. So I'm running the Create command. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Doing a bunch of stuff. VIKTOR LIDHOLT: It does a bunch of stuff. CRAIG LABENZ: Who's the Network Connections that it asked me about? VIKTOR LIDHOLT: Huh? CRAIG LABENZ: It asked me if a Dart app could accept incoming network requests. You know what that was? VIKTOR LIDHOLT: Yeah, yeah. Is it a question from the comments or did it-- CRAIG LABENZ: Oh, no. It just popped up on my screen briefly. I just clicked through it instinctively, and then I was like, oh, wait, I should ask about that. VIKTOR LIDHOLT: OK. I'm not sure what that was. OK, so you have Mypod. It's a great name for an app, by the way. [BOTH CHUCKLE] CRAIG LABENZ: Yeah, I do have my server pod, and it's got the server, and-- all right. So-- oh, this is-- yeah, these are the commands to run everything. VIKTOR LIDHOLT: Yeah, exactly. So if you go into the Mypod server directory there, and-- basically what this does is, first you start Docker, and Docker will run your postgres and Redis for you. Because you can potentially run this-- oh, you're already in there. You can potentially run this, yes, with a local postgres installation, but it's pretty tricky to install postgres. I don't know if you've tried that ever, but it's way, way easier with Docker, so that's why we choose to do it like that. CRAIG LABENZ: On Mac OS, there's a nice companion app called Postgres.app that's really, really nice-- VIKTOR LIDHOLT: Oh yeah. CRAIG LABENZ: But it doesn't beat Docker. It's older than Docker. VIKTOR LIDHOLT: Right. So-- CRAIG LABENZ: OK, so let me try running [INAUDIBLE].. VIKTOR LIDHOLT: Yeah, so let's try that. CRAIG LABENZ: OK. So I've CD'd in, so-- oh, I didn't CD into my server. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: OK. And then we'll have Docker compose. VIKTOR LIDHOLT: Yep. So that would be [INAUDIBLE]. OK, that looks good. And the Detach thing makes it run in the background, so it actually would run until you shut it down, and then you just can run the B the main-- CRAIG LABENZ: Try that server. Yeah. OK. So we have the server running. Shortly. VIKTOR LIDHOLT: Yeah, that looks good. CRAIG LABENZ: OK, there we go. VIKTOR LIDHOLT: Everything worked. So basically, what this does, it will start-- Serverpod runs three different servers, so you have the main server running; and then you have the inside server, which-- the app will let you download it, will connect to; and then it runs a web server too. So the web server is optional if you-- sometimes you want to have a web server. It's pretty cool that you can use the same system to build your whole web app. So I used this for building a news app, and then you also want to have web pages, right? With that you can link to news stories too. So you can build that with actual templates in HTML. That's still a little bit experimental, but-- CRAIG LABENZ: Gone? So does web server use Shelf? VIKTOR LIDHOLT: It doesn't use Shelf, none of this. CRAIG LABENZ: OK. VIKTOR LIDHOLT: So the users-- CRAIG LABENZ: But is it-- it's similar, essentially, you think? VIKTOR LIDHOLT: It's very similar, yeah. So we may change to Shelf when I was-- in the future, it's possible. CRAIG LABENZ: Gotcha. VIKTOR LIDHOLT: I think I may have started this project before Shelf was [? offered. ?] So, simple-- so what you can actually do now to try this out is, if you open another terminal, and you can try to run that Flutter app. So when you run Serverpod Create, it will create three packages for you. So it will create the server, and it also creates an app that is all hooked up. So I would typically run-- I think it may be smarter to do the Web if you do the Chrome. CRAIG LABENZ: Oh, OK. VIKTOR LIDHOLT: Yes, because if you do Mac, you'll need to do a few extra setup steps. CRAIG LABENZ: Oh, VIKTOR LIDHOLT: Because the Mac application-- CRAIG LABENZ: The entitlements. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Yeah. Yeah, that does take an extra-- VIKTOR LIDHOLT: I saw some questions here in this chat also. CRAIG LABENZ: Yeah, so we had one. Will Serverpod-- nope, it's just-- the app launched and covered what I was reading. Will Serverpod support deployment on Render, Railway, Fly, or other Heroku alternatives? VIKTOR LIDHOLT: Right. So right now, we have support for AWS and Google Cloud that we made really smooth, but it basically creates a Docker container for you with the server. So wherever you can run that docu container, wherever you can run Dart, you can run Serverpod. The only thing it will need is a connection to a postgres database, and you will be set. So-- CRAIG LABENZ: Nice. Yeah. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: So you've got-- you have really robust installation, like, easy installation support on AWS and GCP. VIKTOR LIDHOLT: Yep. CRAIG LABENZ: But it's very possible to roll it oneself elsewhere? VIKTOR LIDHOLT: Definitely. Yeah. CRAIG LABENZ: Nice. OK, are we ready to test what this is doing? VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: All right. Well, my name is, in fact, Craig. OK. So how do we see proof that this made it to the server? [BOTH CHUCKLE] VIKTOR LIDHOLT: All right-- if you look at the server log there, I think it will have-- tell you that you made a method call to example.hello. [LABENZ HUMS] So when you set it up, it comes with a single-- basically you build Serverpod around endpoints. So if you go to that server directory and open or un-flip the library folder. So there are a lot of folders in here, so it can look a little bit overwhelming, but it's really just one-- a few directories that you need to look at. If you unfold Source, there should be an endpoints directory. CRAIG LABENZ: OK. All right. VIKTOR LIDHOLT: And you open up the example endpoint. So this is sort of the starting point for the endpoint. And you can see here-- this is the Hello, World method. CRAIG LABENZ: OK. So I'm sure that all of this all of these comments and this documentation explains what we can do here, but where does this endpoint get connected to everything? VIKTOR LIDHOLT: Right. So you can, in the endpoint directory, you can create any number of classes there, and they should extend the endpoint class, and in the endpoint-- so this is, you have the example endpoint, which will then link to example, and then you have the methods that you add. So you have a Hello method there. And the methods you add need to have a specific signature. They need to start-- the first parameter needs to be a session object. CRAIG LABENZ: OK. VIKTOR LIDHOLT: Like Session. And then you can use-- CRAIG LABENZ: Whatever parameter-- VIKTOR LIDHOLT: More primitive types, and then you can use types that are serialized by server port as well. [LABENZ HUMS] So we can look at that in a second, how you do that. CRAIG LABENZ: OK. VIKTOR LIDHOLT: It's super simple. And the same objects you can use for parameters, you can also use for return types. CRAIG LABENZ: Got it, OK. VIKTOR LIDHOLT: So this is like the very simple, most simple example right here. Just return another string. But we can build something a little bit more interesting here. But we can just try to add another method here, if you want to do a, like, Hello two maybe? CRAIG LABENZ: Now, how tricky would it be to return a map? VIKTOR LIDHOLT: A map? CRAIG LABENZ: I think we have that. Yeah. VIKTOR LIDHOLT: Yeah, you can do Future and then the Map, and String. So you cannot use, like, generic objects, unfortunately. CRAIG LABENZ: Oh, so should I use dynamic? Or we have to choose another string? Or-- VIKTOR LIDHOLT: You can only use objects direct you can serialize with Serverpod. CRAIG LABENZ: Oh, OK. VIKTOR LIDHOLT: So that's the restriction here. So you can have another string for instance, or an integer there. CRAIG LABENZ: So how do you handle kind of complex JSON that has mixed types? VIKTOR LIDHOLT: So-- CRAIG LABENZ: Put it in an object and have that be serialize-able? VIKTOR LIDHOLT: Yes, that's how you do it. I think we may add support for dynamic types in the future too. That's-- CRAIG LABENZ: OK. VIKTOR LIDHOLT: It wouldn't be too tricky, but something we haven't done yet. CRAIG LABENZ: Goodbye. OK. All right. Tink-- and then we'll return a map that says Goodbye, and Name. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: All right. VIKTOR LIDHOLT: Cool. So what you need to know-- what you need to do now is, when you-- make sure you save that file, and then in the terminal, you need to run Serverpod Generate. CRAIG LABENZ: OK. [TUTTING] Let's see. Which-- this one, I'm trying to remember-- so this one is Flutter, and this one is the server. VIKTOR LIDHOLT: All right. CRAIG LABENZ: So Serverpod-- and do I run it in the Server folder? VIKTOR LIDHOLT: Yep. CRAIG LABENZ: OK. Serverpod Generate. Great. Very terse and succinct output. [BOTH CHUCKLE] VIKTOR LIDHOLT: Right? So if you now go to your-- let's see, the Flutter app. Yeah. Oh, it's smart-- CRAIG LABENZ: Oh, yeah. VIKTOR LIDHOLT: Run in the server. So if you look at the code for the Flutter app. CRAIG LABENZ: Oh. Oh, sorry. Sorry, sorry. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: So not this one. [TUTTING] So I'll close you. Lib, Main. VIKTOR LIDHOLT: Yeah, that's a good place. So you may need to do-- either restart the analyzer here. We haven't-- maybe that's something, you know, how we can do automatically. Or run hub gets. Otherwise they will not know of the changes. CRAIG LABENZ: This change we just made. Yep. VIKTOR LIDHOLT: So basically, what happens when you run Serverpod Generate, it will look through your server code, so it runs the doc analyzer to analyze your server code and pick out the changes you've made. And then it saves those changes maps everything up in the server, but it also maps it up in the Mypod client. So that's a package that your Flutter app will import. So that contains the whole-- oh, yeah. Exactly. That's-- CRAIG LABENZ: A Mypod client. Nice. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: OK. VIKTOR LIDHOLT: So that's how you refer to the client. So if you do-- I think, is it Command Shift P? CRAIG LABENZ: Yeah. If I reload the window, we should see it. VIKTOR LIDHOLT: OK. CRAIG LABENZ: View later. [TUTTING] VIKTOR LIDHOLT: OK, so we start doc analyzer. CRAIG LABENZ: Maybe that worked. VIKTOR LIDHOLT: So if you check-- yeah, cool. Hello. And-- [LABENZ CHUCKLES] CRAIG LABENZ: Yeah. It's easier. OK, so now we want to call it Goodbye instead, so I'm just going to-- VIKTOR LIDHOLT: So you can see that-- oh, yeah. You're so fast. [CHUCKLES] CRAIG LABENZ: Goodbye. So this result must be a-- what's the issue here? Oh, String. String can't be of type String. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Oh, a result message. Right. VIKTOR LIDHOLT: Yes. You can put that in a string, for instance. Or yeah-- CRAIG LABENZ: Resolve. I will just grab the name. And then it's still going to say Hello, right? In the-- VIKTOR LIDHOLT: Right. CRAIG LABENZ: Where does that get dropped in? VIKTOR LIDHOLT: So this is just a super simple app that will show the results. Yeah. CRAIG LABENZ: Yeah. VIKTOR LIDHOLT: We tried to keep the example, like, as easy as possible, so it's not super fancy. CRAIG LABENZ: Yeah. No, that is reasonable. OK, so I'm going to run it in Flutter again. It's still going to say Hello. It's still going to-- VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Not be visually different, but we should be able to see, I'm guessing-- we saw the method call on the server logs here. VIKTOR LIDHOLT: Right. CRAIG LABENZ: And it said Hello, and this time it'll say Goodbye. All right, we had a good question that I want to pop up while this builds, though even-- it did just finish, but Viktor, do you want to field this one? Nope, wrong one. Wrong one. Though we can come back to that. Oh man, where did I-- did I lose it? Maybe I just saw it, but it wasn't starred. OK, well someone asked-- oh, here we go. This is the question. VIKTOR LIDHOLT: Oh. Yeah. So actually, the name Serverpod, I made that before Riverpod [CHUCKLING] by a couple of months. So I started Serverpod for, like, a long, long, long time ago. So it has nothing to do with Riverpod-- it's just, like, a coincidence. The name is more-- my thinking was that it's like a server, but everything is like contained in-- [OBJECT CLANGS] Oops, sorry-- in a pod that has the whole-- everything you need. And originally, it was more like a placeholder, but it kind of stuck with people, so I figured, why not? It's an OK name. CRAIG LABENZ: We've got our method call here. I have a funny story, by the way that I'm going to try to tell as quickly as I can. In my experience, any temporary name that you use and you hope to discard and swap later will never get replaced, and you'll use it forever. So an old company I worked at-- I made up some algorithm, and I just pulled it out of thin air about how to rank these different objects that we had in our database based on customer popularity. And I didn't know what to call this algorithm, or literally the column in the database, and I was just feeling cheeky, so I called it The Awesomeness Score. And I thought for sure that name was so silly no one would ever use it, and years later, The Awesomeness Score remained a thing that people talked about all the time. Like, very serious people would be in meetings and be like, well, this one has an awesomeness score of blah, blah, blah. [BOTH LAUGH] VIKTOR LIDHOLT: That's about right. CRAIG LABENZ: Yeah, whatever name you pick will be the name that is used. Anyway. VIKTOR LIDHOLT: I guess so. CRAIG LABENZ: OK. So we've added this Goodbye method call, and we are seeing it returned. Or we're seeing it called, and actually because in the server, I just realized, you actually-- the Hello came from here, it didn't come from the UI. That's why, once I didn't include Goodbye in the payload part of it, because I'm only using the Goodbye key unless it's just grabbing the name-- that's why we just saw Test and not Hello Test anymore. VIKTOR LIDHOLT: Right. CRAIG LABENZ: But (SINGSONG) it's working! VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: OK. So what's something cool we can do? VIKTOR LIDHOLT: All right, I figured maybe we can try to build a small to-do app. CRAIG LABENZ: OK. Sounds good. Also, the Insights app. I should-- VIKTOR LIDHOLT: Oh, yeah, let's have a look at that. CRAIG LABENZ: Yeah. Let me find-- let me grab this. Applications. [TUTTING] Insights. I don't know how to scroll through the alphabet. Oh, no. It's Serverpod. That's why it's not showing up in the eyes. Oh. VIKTOR LIDHOLT: That's about right. CRAIG LABENZ: I know I did download it from the internet. OK. All right. So I hope that your terms and conditions are fair and just. VIKTOR LIDHOLT: Yeah. We are not collecting too much data. [CHUCKLES] CRAIG LABENZ: Do I have to fill these out? VIKTOR LIDHOLT: Yeah. I'm sorry about that. [CHUCKLES] CRAIG LABENZ: That's OK, but I'm just going to fill them out off screen. VIKTOR LIDHOLT: Makes sense. CRAIG LABENZ: [INAUDIBLE]. VIKTOR LIDHOLT: We're adding [INAUDIBLE] sign-in for the next version too. CRAIG LABENZ: The adventure continues as I now have to validate my email. OK. There it is. Got the code. Things are looking up. [LIPS POP] Verify email. And we're in. VIKTOR LIDHOLT: Nice. CRAIG LABENZ: OK. VIKTOR LIDHOLT: OK. So you need to add a project here. CRAIG LABENZ: Add Project. OK. I'm going to do this off screen as well. [BOTH LAUGH] Let's see here. I'm going to also-- [LABENZ LAUGHS] How high is The Awesomeness Score for Serverpod? VIKTOR LIDHOLT: 10 out of 10, obviously. CRAIG LABENZ: Yeah. I agree. Now that was actually-- that one was just an un-normalized number where higher was always better. [LIDHOLT LAUGHS] So 10 out of 10 would be a Awesomeness score of 1, which would be extremely low. VIKTOR LIDHOLT: Oh, damn it. [BOTH LAUGH] CRAIG LABENZ: Not a 0.000001. All right, what am I doing here? Oh, yeah. I'm clicking into the project and I'm finding Mypod, it was called, and then I-- should I choose the top level folder? VIKTOR LIDHOLT: Yeah, you can choose either the top level folder or the server folder. Both should work. CRAIG LABENZ: OK. I'll just pick the server folder. Where did that window go? There we are. Here we go. Select. Hey, hey! OK. So-- VIKTOR LIDHOLT: Good. Cool. CRAIG LABENZ: If I click another button, would it show up? VIKTOR LIDHOLT: Not really yet. So the thing is, you're not really logging anything here. By default, it only logs errors, not to bug stuff down. But if you click Log Settings in the top right corner-- CRAIG LABENZ: Oh, OK. Yep. Yep, yep. VIKTOR LIDHOLT: You can say log all sessions there, and save. And if you now send to server and click the reload button up in the top left corner, there is the session. CRAIG LABENZ: Nice. VIKTOR LIDHOLT: What's pretty cool with an inside session log here is that it will group your logs by session, which makes it much, much, much easier to follow your logs, right? Because if you have thousands of users doing stuff simultaneously, and you try to go through a text log where everything is [INAUDIBLE] up, that can be super hard to find. CRAIG LABENZ: Right. VIKTOR LIDHOLT: So how about we try to add something in the server? So we can actually log something that's more than just a session. CRAIG LABENZ: OK. VIKTOR LIDHOLT: That could be a [INAUDIBLE].. So if you go to your example endpoint and look at the Goodbye method that we're calling right now. So the session object will contain lots of things that are-- it's sort of the context of where you are at. So here you have logs, session.log, and you can write a little message here. But through the session, you can also access the database or send messages to other servers, or whatever you want. CRAIG LABENZ: By the way, this is supposed to be read in the Mrs. Doubtfire voice. (ACCENTED) Well, hello! VIKTOR LIDHOLT: All right. CRAIG LABENZ: Let me just log this and see. VIKTOR LIDHOLT: You need to restart the server here. CRAIG LABENZ: Yeah. I'm really struggling to remember which tab is which in the-- OK, that one's Flutter. There we go. VIKTOR LIDHOLT: So that's something we plan to add. It's like hot reload for the server too. It's not there yet, but it would be nice to have. CRAIG LABENZ: OK. So now I'm going to send this to the server. I'm going to come back, and I have to click Refresh again? VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: OK. VIKTOR LIDHOLT: You have a second session there and you can see your log. CRAIG LABENZ: Mrs. Doubtfire is in the house. VIKTOR LIDHOLT: All right. CRAIG LABENZ: OK. So one second here-- there is just a mountain of questions building. VIKTOR LIDHOLT: Yeah. We can work through some of the questions. CRAIG LABENZ: Yeah, let's-- all right. I'm going to bring them up in as rapid fire a way as I can here. MySQL-- yea or nay? VIKTOR LIDHOLT: So-- I mean, you can use anything you can use with Dart, but, like, the built in support is only postgres at this time. CRAIG LABENZ: OK. So I would say that's functionally a no for MySQL. VIKTOR LIDHOLT: Yeah, I guess if you want to have all the niceness, you will not have that, but you can obviously connect to any database from Dart. CRAIG LABENZ: (WHISPERING) Also, postgres is really good. postgres is really, really, really good. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: I highly recommend it. VIKTOR LIDHOLT: Best one. [CHUCKLES] Or the one we like the most. CRAIG LABENZ: Yeah, well, you like it because it's really good. OK. Let's see. Looking at the next one. The docs said we can choose the server roles. Oh. Yeah. OK. This one's a good question to cover. VIKTOR LIDHOLT: Right. So you can run server port in different nodes. So the default node is to run it as a monolith where the server contains the state; but you can also run it serverless, which allows you to use it on Google Cloud Run or-- is it called FarGate on AWS? So basically then you just use it as a container. But the drawback there is, the server cannot keep a state, as those servers are added or removed you don't really know when and where; so you need to save all the data either to Redis or postgres if you use that option. For-- if you do a simple app, that's definitely a good option. If you want to do something like a multiplayer game where you have lots of internal communication, then you probably want to run it as a monolith. CRAIG LABENZ: Gotcha. VIKTOR LIDHOLT: So you got both options, basically. We wanted to make that possible, to run everywhere. CRAIG LABENZ: OK, this is a good one. Does the session equal request response from node? VIKTOR LIDHOLT: Oh, good question. I don't exactly know. So the session-- you can get the HTTP request from there. It's stored with the session, so you have all that information, but you also have the logging, database, caching, inter-server communication. All that stuff is in the session object. Authentication. CRAIG LABENZ: So, yeah. The request response thing. So I think request from node REQ is the sum of all of these things, right? Because-- VIKTOR LIDHOLT: Right. CRAIG LABENZ: Request has all of your session stuff and all your parameters. VIKTOR LIDHOLT: All right. CRAIG LABENZ: So that would be request in node; and then response in node, you write something like rest.send and you put your payload in here. VIKTOR LIDHOLT: OK. CRAIG LABENZ: And-- the function here, or the mechanism here, is to just return. So your later somewhere else Serverpod must be kind of-- it's calling Goodbye, so it gets the result from something.goodbye, right? And it passes in the session that it's figured out, and the other parameters, right? VIKTOR LIDHOLT: Right. CRAIG LABENZ: And you're going to-- Serverpod is doing some other equivalent of rest.send, a result like that. VIKTOR LIDHOLT: Yeah. So basically we streamline the whole bunch of those things that you typically need to do in many other frameworks. We need to form your responses. Here you can just return Dart objects, which makes it-- you save a lot of code, and there's also mistakes that can go into there, in my experience. It's hard to keep everything up to date when you change versions in stuff like that. So it's really nice to have that, yes, to generate it. CRAIG LABENZ: All abstracted away, yeah? VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Just a method to return the goods. OK, another good question here-- is there any need to use state management with Serverpod? VIKTOR LIDHOLT: Not really. I mean, you can save any stateful information wherever in the server you want. It could be, like, in a global variable if you want to, but I would recommend like some sort of singleton object if you store a state in the server. CRAIG LABENZ: Now are you answering this question from a perspective of server side code or client side code? VIKTOR LIDHOLT: OK, for client side code, obviously you probably want to use some sort of state management there. So yeah, you can use whatever. There's no limitations. We don't really care too much about what you do on the front end side. It's so many different preferences, so we just wanted to make sure it works with everything. And we haven't made it work specifically for Riverpod or specifically for setState, or block [INAUDIBLE] or anything like that. It just-- you have to plug that into the structure that you like the most. CRAIG LABENZ: Yeah, and from the-- so I think that's definitely the correct approach that Serverpod should take on the front end, is your data communication layer, but there's solid state management, however, on the front end; and there's just nothing even philosophically that could prevent it from working with anyone's favorite solution. VIKTOR LIDHOLT: No. CRAIG LABENZ: And then on the back end, especially if you're in a monolith and you have a game, for example, then obviously you're highly stateful. So in that case, everything I'm about to say changes; but in a typical-- yeah, I'll put air quotes around typical-- web app, you know, HTTP is considered a stateless protocol. VIKTOR LIDHOLT: Right. CRAIG LABENZ: So every request just wakes up and starts from scratch and has no idea about anything, and then it's technically aligned because cookies and the session bring a little bit of state with it. [CHUCKLES] VIKTOR LIDHOLT: I guess. CRAIG LABENZ: But the server, like, wakes up for a request and then goes back to sleep, and wakes up for a request. So every time a request comes in, it's got to be able to do everything from scratch, whether that's pull some data from the database or pull some data from Redis or whatever is kind of going on for that request. And then that state doesn't outlive the request. It just-- some stuff gets returned to the server, and then everything-- or, sorry, some stuff gets returned to the client-- and then everything else on the server basically just dissipates and gets garbage collected. So that whole concept of state management that we all use on the client, if you do have a stateless app, and that's-- you're just using something like HTTP, then you would not have that kind of state management on the server because that's juggling this kind of long lived bunch of different interactions from the user, and blending in. OK, what did they just do? Versus what did they-- last 10 things they did. Which doesn't happen. VIKTOR LIDHOLT: So there may be cases where you want to do some of that stuff. So, say-- we were building a news app with Serverpod before, and it takes maybe 100 database requests to build a front page because you need to pull comments from different places. And that front page doesn't really update that often, so you can store that in the cache, and you can load it from the cache, and then you can apply specific data that is specific to you in that page also, so it knows you are you. So you may add some extra data for your comments or something like that, and then you can return it to the server. But all the heavy work can potentially be stored for a little while, like a minute or a few minutes, depending on your use case, so that can really save a lot of performance because talking with the database is really the most expensive thing you do in a server. Everything else is like-- people ask, like, what is the performance of the server? It doesn't really-- it's like, compared to the database, it's like nothing. So it doesn't really matter. CRAIG LABENZ: Yeah. VIKTOR LIDHOLT: I mean, it can be cases, for sure, where it matters, right? If you have hundreds of millions of users, I'm sure being able to cut off, like, a percentage of that will make a huge difference. But for most cases, it's like-- yeah, doesn't matter too much compared to everything else. CRAIG LABENZ: Yep. VIKTOR LIDHOLT: All right. CRAIG LABENZ: Got another question, and maybe we'll get to this one later, actually. But how to use relations-- I'm assuming this is like postgres relationships and rules is ambiguous. Do you know what-- do you have a sense of what rules might mean? CRAIG LABENZ: I am not sure. I mean, it can be relations to other tools also. I mean, you can use-- with Serverpod, you can use any sort of tools. Anything you can do with Dart, you can do within Serverpod, so you can connect anything there. For our database, we haven't any explicit support for joins yet, but that's something we're going to look at adding. So you have to do that in a few steps currently, but you can also obviously write your own custom SQL code, so you can do-- if you need it for performance reasons, whatever, you can do whichever queries you want. CRAIG LABENZ: What's your wire transfer protocol? VIKTOR LIDHOLT: So it's-- I guess it's kind of similar to GRPC, but it's not using it. Right now it's using JSON, so we're looking at maybe doing a binary format for performance too. CRAIG LABENZ: OK. VIKTOR LIDHOLT: So GRPC have packed things together a little bit more performant, but it's not that huge of a difference when you apply a SIP compression to it. So by default, Serverpod will see your GCPR. So it's really pretty small, the payload, anyway. CRAIG LABENZ: And is it HTTP, even like 2.0? Or WebSockets? How is it connecting? VIKTOR LIDHOLT: Oh, yeah. So it has support for WebSockets as well. So if you do real time communications, uses WebSockets, and then you can pass, like, any sort of objects that are serializable by Serverpod, to the client or to the server. So that's actually a really smooth experience as well. Right. CRAIG LABENZ: All right. Oh, sorry-- I thought you were going down. VIKTOR LIDHOLT: All good. CRAIG LABENZ: OK, let's-- with questions, honestly getting all the way to a to-do app might be a little challenging. Let's just save one thing to the database. VIKTOR LIDHOLT: Sounds good. CRAIG LABENZ: Oh, which I guess could be like a to-do, but maybe we won't do CRUD and chain updating and whatnot. But let's just save something to the database-- VIKTOR LIDHOLT: Yep. CRAIG LABENZ: And maybe read it from the database and deploy to Google Cloud. VIKTOR LIDHOLT: So I guess like reading and writing stuff to the database is like a one liner, but maybe a little bit more-- a little bit more work to-- [INAUDIBLE] work on the Flutter side. CRAIG LABENZ: Right, true. So how do we start with adding a model? Let's do that. VIKTOR LIDHOLT: So let's start with that. If you flip out the protocol folder. CRAIG LABENZ: OK, closing that stuff. That's in the server. VIKTOR LIDHOLT: Yes. Server and Source. CRAIG LABENZ: Oh, here we go. VIKTOR LIDHOLT: Protocol. So there's an example there. So this is like a YAML file. So basically, this is how you define your objects in Serverpod for serialization. CRAIG LABENZ: (WHISPERING) Whoa. OK. So we already have one. VIKTOR LIDHOLT: We have one. It's called Example. CRAIG LABENZ: We haven't connected to a Postgres database yet, right? Also-- VIKTOR LIDHOLT: No, we have not yet done-- well, it's the service connected to Postgres by-- when you're running it now, but we also need to add a table if we add tables. CRAIG LABENZ: Oh, yeah. Because it generated this Docker Compose file and-- VIKTOR LIDHOLT: Right. CRAIG LABENZ: That's how we launched the database, so it knows the connection. VIKTOR LIDHOLT: Correct. CRAIG LABENZ: Yeah, and then-- VIKTOR LIDHOLT: See how easy that was? CRAIG LABENZ: Yeah, nice. VIKTOR LIDHOLT: You didn't even notice. CRAIG LABENZ: I've forgotten the-- yeah, it's not gonna remind me, but just tiptoed on. [BOTH LAUGH] OK, great. VIKTOR LIDHOLT: Right. CRAIG LABENZ: So-- yeah, have we generated this table yet? VIKTOR LIDHOLT: Yeah. So maybe we can just create a new YAML file there, that we call-- it could be a to-do. So, the protocol folder. CRAIG LABENZ: OK. VIKTOR LIDHOLT: Yeah. That's perfect. And then you need to say a class at the top. So we can go to all that to-do. Perfect. And then you add fields. CRAIG LABENZ: Right. VIKTOR LIDHOLT: So that should be indented to the left there. CRAIG LABENZ: Fields does not get invented. OK. VIKTOR LIDHOLT: Yeah. Right. Correct. CRAIG LABENZ: And then, so I guess we'll just-- Description, Name. VIKTOR LIDHOLT: Yeah. Perfect. And that's the string. And-- CRAIG LABENZ: Let's do the isdone Boolean. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Bool? VIKTOR LIDHOLT: Yeah, just like Dart. CRAIG LABENZ: Oh, nice. VIKTOR LIDHOLT: OK, so if we want to make this into a database table, we also need to connect the tables. So the way we do that is, under Class, you can Add Table, and-- CRAIG LABENZ: So-- oh, right here? VIKTOR LIDHOLT: Table, and then you go to-do with-- perfect. CRAIG LABENZ: OK. VIKTOR LIDHOLT: And then-- CRAIG LABENZ: So how do we run? VIKTOR LIDHOLT: Now you save the file and you do a Serverpod Generate. CRAIG LABENZ: Great. That's easy enough. VIKTOR LIDHOLT: And it says Done. So now we have all the code for this. So it actually generated some SQL code for us here. So if you go up a little bit in my pod server directory there, there's a generated folder that you might want to have a look at. And-- yeah, so these are, like, the actual generated-- the code that's generated by Serverpod on the server side. If you scroll up a little bit more, so this is-- typically you don't need to modify that code, but if you-- CRAIG LABENZ: Sorry, where am I scrolling up to? VIKTOR LIDHOLT: Yeah, there's another generated folder right above your-- CRAIG LABENZ: OhI I see it, I see it. And which one do I have look at here? Table? VIKTOR LIDHOLT: Tables. So this is your to-do table code. [LABENZ HUMS] So, yes, copy that and run that in Postgres, and that will create the table. So what we're doing for the next version, this will actually-- you can do migration, so you'll be able to do Serverpod migrate, and it creates a migration that you can run when you start a server. Or you can actually connect to the server, run the migration while the server's running if you want, like, zero downtime. So that would be a super cool feature, actually. But we're not quite there yet, but we will be. So here you need to figure out-- CRAIG LABENZ: Voted post to go to show-- VIKTOR LIDHOLT: Yeah. Did you connect to the-- CRAIG LABENZ: Database Mypod doesn't exist? Yeah, it does. Does it not? So we need to create the database? VIKTOR LIDHOLT: Yes. CRAIG LABENZ: Oh. OK. VIKTOR LIDHOLT: No, no. No, no. The database is there, but I don't think you're connected to the database from that one. CRAIG LABENZ: Yeah I tried to connect using-- VIKTOR LIDHOLT: So this is on-- so you should connect on, if you can see the settings there? CRAIG LABENZ: Yeah. So localhost, mypod, Postgres. VIKTOR LIDHOLT: Yes. Port-- CRAIG LABENZ: And I type in that password. VIKTOR LIDHOLT: The port should be 8090. CRAIG LABENZ: Whoa. VIKTOR LIDHOLT: I know. CRAIG LABENZ: Why did you change that? [BOTH CHUCKLE] VIKTOR LIDHOLT: OK. Now you're in. So basically, we wanted to pick some ports that are not commonly used for other stuff. OK, so now you have the table. Perfect. CRAIG LABENZ: To do. There we go. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Looking good. VIKTOR LIDHOLT: Nice. CRAIG LABENZ: All right, let's get something in there. VIKTOR LIDHOLT: Let's do it. CRAIG LABENZ: Oh, 8090. Yeah, there it is. Staring me in the face. That a snake, it would have bit me. VIKTOR LIDHOLT: So you can actually find this. There's a Config folder in your server that have all the configuration data that Serverpod produced. You can, if you-- CRAIG LABENZ: Now this isn't very helpful, now is it? [BOTH CHUCKLE] VIKTOR LIDHOLT: It's right on your left. If you scroll up a little bit, there's a blue folder. CRAIG LABENZ: Oh, here we go. Oh, owner. VIKTOR LIDHOLT: And here you have Developments. And you can just change the ports here if you want to. CRAIG LABENZ: Nice. OK. Cool. VIKTOR LIDHOLT: You have the database there, and then there's a passwords file where all your passwords are stored. CRAIG LABENZ: Very cool. [CLEARS THROAT] OK. So we've made the thing. Yeah, now we need to.-- let's make an endpoint-- VIKTOR LIDHOLT: Yep. CRAIG LABENZ: That writes to it. VIKTOR LIDHOLT: Right. CRAIG LABENZ: Instead of Goodbye, maybe this will be Add To-Do. VIKTOR LIDHOLT: Perfect. CRAIG LABENZ: And we only had a name. VIKTOR LIDHOLT: Yeah. So you could potentially pass in a to-do here. CRAIG LABENZ: Yeah. That'd be cooler. [INAUDIBLE] VIKTOR LIDHOLT: Perfect. CRAIG LABENZ: And then-- did I generate that class already? VIKTOR LIDHOLT: Yeah, it should be. Is it giving you a warning? CRAIG LABENZ: Todo.dart, OK. Well, it sees it. VIKTOR LIDHOLT: So maybe it's not imported. If you scroll up in that window. Yeah, generate the protocol. CRAIG LABENZ: All right. VIKTOR LIDHOLT: Nice. CRAIG LABENZ: OK, so I saw a session.db. VIKTOR LIDHOLT: Yeah, so there are shortcuts for the database tables. So what you can do is basically view the static methods on the to do class that will help work with that to do dot. Insert. CRAIG LABENZ: Insert. Could be good. VIKTOR LIDHOLT: And it takes the session, and then you-- CRAIG LABENZ: Row as an-- VIKTOR LIDHOLT: Item. CRAIG LABENZ: Item. [HUMS] VIKTOR LIDHOLT: And you put in a wait in front of that, probably. CRAIG LABENZ: Yeah. Does this return-- VIKTOR LIDHOLT: A future. CRAIG LABENZ: Oh, interesting. So how did we get the ID that this used? VIKTOR LIDHOLT: Oh, yeah. So we're going to return the ID. We're actually changing that. But after you've done the insert, that item will have an ID dataset. CRAIG LABENZ: Oh. Gotcha. OK. So here we could return-- just return the item.id, I guess. No, let's return the whole item. I think that would be cooler. So we just need to change this-- VIKTOR LIDHOLT: Yes. CRAIG LABENZ: To be to do. VIKTOR LIDHOLT: Yeah. Perfect. CRAIG LABENZ: Cool. All right. VIKTOR LIDHOLT: This is that. And now you just need to run the Serverpod Generate. CRAIG LABENZ: Oh, whoops. I clicked the wrong-- VIKTOR LIDHOLT: Command to do sort of update your protocol. CRAIG LABENZ: All right. VIKTOR LIDHOLT: Nice. [LABENZ HUMS TADA] Done. So if you go to your app. CRAIG LABENZ: Oh. Here. But probably the code. VIKTOR LIDHOLT: Oh, the code, yeah, yeah. CRAIG LABENZ: Yeah. All right. So let me close this. Somebody asked, by the way, what did I use to connect to Postgres? That's called Postico, and I don't know if it's available for Windows. I think a Mac specific developer-- VIKTOR LIDHOLT: Yeah, I think that's a Mac one, but there are, like, plenty of other alternatives. CRAIG LABENZ: Yeah. OK. So the client-- well, not the generated client-- Flutter. VIKTOR LIDHOLT: Main file. CRAIG LABENZ: Oh. So we are just-- I see, we are still all in the Lib directory. The other one had another top level directory. Now-- that's because it was next to BIN. That's right. OK. So I guess I should add-- VIKTOR LIDHOLT: I think you-- CRAIG LABENZ: No I don't need to add the to do class. It's already there. VIKTOR LIDHOLT: It's already there. So what you need to do is probably restart that, maybe. Maybe If you have them open at the same time, that will-- CRAIG LABENZ: Restart. VIKTOR LIDHOLT: Let's see if you-- CRAIG LABENZ: Oh, the analysis engine? VIKTOR LIDHOLT: Yeah, possibly. This would be really nice. CRAIG LABENZ: This seems nicer. VIKTOR LIDHOLT: Would be nice if that happens. CRAIG LABENZ: Subtle things that I-- oh, sorry. What was that? VIKTOR LIDHOLT: Yeah. So you can add an insert method there, for instance. CRAIG LABENZ: We'll call this save to do, and it's going to take a string. That'll be the name, and then we're going to make a to do. VIKTOR LIDHOLT: Nice. CRAIG LABENZ: Oh, I guess we can-- maybe we'll just not take a string, and we'll make a to do with the value. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Here, we'll say final unsaved to do equals client.example. Dot save to do. Why is that under-- VIKTOR LIDHOLT: Ah. Yeah, no. OK. CRAIG LABENZ: Oh wait-- because we're really just grabbing this token. VIKTOR LIDHOLT: Ah, OK. CRAIG LABENZ: So I think it just-- I think it didn't update. VIKTOR LIDHOLT: Save to-- CRAIG LABENZ: Yeah. Well, what did we call it? With add to do. VIKTOR LIDHOLT: That's-- and then you just pass in an item there. CRAIG LABENZ: So the name is going to be the text editing controller value, and then the is done is going to be false of course. All right. VIKTOR LIDHOLT: Right. We need a wait there. CRAIG LABENZ: To do. Oh, it's not unsaved anymore. Now it's saved. I was going to make a to do object and then pass it in, but then we made the to-do right in the parameter spot. So again, now we have a saved to do. We don't need this anymore, and so now it's going to be a saved to do, and then-- can we have a to do dot JSON or something? VIKTOR LIDHOLT: Yeah. If you just put it to do in there, the two strings will output the JSON. CRAIG LABENZ: Oh, OK. VIKTOR LIDHOLT: If you want to see that. CRAIG LABENZ: But save-- VIKTOR LIDHOLT: That's save to do. Yeah. CRAIG LABENZ: OK. So save to do is now what we have to put on our button. VIKTOR LIDHOLT: Right. CRAIG LABENZ: Oh man! Is this going to work? VIKTOR LIDHOLT: Let's hope so. Did you restart the server? I think you did, right? CRAIG LABENZ: Uh. I killed it. VIKTOR LIDHOLT: It's good if It's running. [BOTH LAUGH] CRAIG LABENZ: Yeah. That would be really helpful. And so we made the-- so let's recap what we just did. We added a class definition to do dot YAML, then we generated that class by running Serverpod dot Generate. Then we went to this top level generated folder next to lib, and grabbed tables dot PSQL or PG SQL, and nabbed the-- oh. No, I didn't run this query as well. Everything will still work, but-- VIKTOR LIDHOLT: Oh yeah, that would be nice. CRAIG LABENZ: Yeah. Oh, where is this thing again? Every now and then I'm like-- VIKTOR LIDHOLT: The SQL query. Top left. CRAIG LABENZ: Oh, yeah. I click around-- I click in here and I'm like, why are you doing it, man? I have to click exactly this. Anyway. OK. That's done. OK, so we manually did the query migration, and you're adding automatic migrations in the next release, so that'll be very cool. Then-- so we generated, we migrated, we added the table, then we restarted the server; and then on the front end-- remember we don't have to add a to-do-- we don't have to write the to do class. That's generated for us. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: And then on the front end, we-- on the front end, we just called the new method. VIKTOR LIDHOLT: Right. CRAIG LABENZ: Oh, and we added an endpoint, add to do, that actually makes use of the to do class that was generated for us. VIKTOR LIDHOLT: Right. CRAIG LABENZ: So we insert it. That's a method-- a helper method you pointed out is on the class itself. It's a static method, obviously. And then we're calling add to do. And I confusingly am calling that from Save to do. That should have been add to do. All right. Seems like it might work. VIKTOR LIDHOLT: Let's try it. CRAIG LABENZ: I think we're just ready to do it, yeah. So what do we need to do? VIKTOR LIDHOLT: Did you restart the-- CRAIG LABENZ: Oh, no. Yeah. Sorry. I'm being silly. Because this is a to do. So let's call it add database migrations. OK. VIKTOR LIDHOLT: All right. CRAIG LABENZ: Great. That's promising. Now we'll flip over to Postico and go to the table and refresh. [CACKLES] It's there. Would you look at that? A really cartoonishly large font size. [LIDHOLT LAUGHS] Nice. All right. Now let's show all the to-dos in our UI. Obviously we need to do that. Let me see-- don't give me any hints. Let me see how quickly I can guess how to do it. VIKTOR LIDHOLT: All right. [BOTH CHUCKLE] CRAIG LABENZ: So, yeah. We've got a scaffold by-- so the column here. I'm not going to worry about overflow or anything. Like, this should be a list view of course, but I'm not going to worry about that. So to do dot-- we don't-- hoping there would be a static method that read all the to-dos. Because we had to do dot insert, but I'm also not even seeing-- oh, I am seeing insert. OK. So this is a really, really terribly unhelpful bit of completion here. Why isn't it giving me all the actual methods? VIKTOR LIDHOLT: I think you need to do this on the server. CRAIG LABENZ: Oh, I can't load them. OK. I was imagining it was going to be different. I'm going to have to call the thing and return them. VIKTOR LIDHOLT: Yes. CRAIG LABENZ: Got it. Got it, got it. I was imagining that I would have lots of stuff that-- where I could just make like a live network call, but actually this makes more sense. That would be bad-- you'd be putting network requests in your-- no, no. No, it wouldn't be bad; but whatever. That's not how it is. It's fine. So load to-dos, and we're not going to take a parameter. And so here this is going to be a list of to-dos of course. So now it's going to be to do dot-- I'll overwrite. This is what we want. So maybe it's find, and we pass, like, no parameters. And so there's no where clause, and it just reads them all. That's what I'm guessing. Let's see. I'm going to also read the documentation on find, but it's generated code, so there isn't documentation on find. But there probably is here. So let's kick the tires on the docs a little bit. Pretend I didn't have you on the stream, and let's figure out what I need to know here. So I want to look for database communication. There we are. And now I'm just going to search for find because I've already-- finding multiple rows, here we are. And that is what it is. So company.find, all right. But there, what is this first parameter here? T company. Oh. This is like a bit of-- VIKTOR LIDHOLT: I see something we need to fix there. Should be session. CRAIG LABENZ: Session here. OK, that makes sense. Got it. VIKTOR LIDHOLT: Make sure that gets fixed. Old code. CRAIG LABENZ: All right. We aren't going to pass any of these where things ourselves, so that's nice. So find session, and that's it. [CHUCKLES] And we're just returning these, so-- actually, let's make this the shorthand version. OK. Well, that's easy. So now, back in our widgets, we've got a stateful widget, so maybe we'll do this in it state, which doesn't exist yet. Got far without an it state. OK. So let's create a late list of to-dos called to-dos. I keep wanting to write toods. [CHUCKLES] And this is going to be-- no, this is client, right? A client dot example. Client dot example dot load todos. Yeah. And this didn't take any parameters. What does it want? Huh? What do you want? Isn't defined. Probably because I need to restart the thing. Everything else this server-- VIKTOR LIDHOLT: You didn't do the Serverpod Generate there. CRAIG LABENZ: Oh, that's right. That's right, that's right. Yeah. Yeah, yeah. All right. I'm going to do this-- so it'll be and, Dart, bin, Dart. No. Main dot Dart. There we go. So no. Now I'll stop forgetting to restart it after I generate. VIKTOR LIDHOLT: That's [? march. ?] CRAIG LABENZ: OK. It's done, it's restarted. Instantly found. Love it. VIKTOR LIDHOLT: Yeah-- so maybe it doesn't need to do that, like-- CRAIG LABENZ: Oh, but this can't be-- we can't have a future in here, so this will just be load to dos. And here we'll say future-- actually, you know what? Everyone does this, but we can do it-- we can go old school. Remember, folks-- there is a then. It still exists. Even though none of us ever use it anymore. VIKTOR LIDHOLT: Hey, yeah. CRAIG LABENZ: Why would we want to do that? In here, we can say todos equals todos. And just so that it's a little less confusing, let's call this loaded todos. Why, that was not necessary. All right. Cool. Cool, cool. And now-- now we can make a change in our UI. Oh, instead of making this late, let's just make it a future, and we use a FutureBuilder. It'll be not null-able. Oh, now we can say-- oh, see, now I do kind of-- but this won't be-- this is fine, actually, because we're not going to await it. Ah. Here we go. What are you upset about? Oh but you said-- VIKTOR LIDHOLT: Late. [BOTH CHUCKLE] CRAIG LABENZ: I couldn't get away from late anyway. Actually, what, I had before, I think that would have crashed. I think that would have not worked because it probably would have rendered the future-- VIKTOR LIDHOLT: Oh, yeah. CRAIG LABENZ: Before the future was resolved, and so the late thing, it would have been an unassigned late variable. VIKTOR LIDHOLT: True. CRAIG LABENZ: All right. So we're in the column, we're in our children, and now let's have a-- I wanted it to be like a list comprehension in here. Easiest way to do this. It's we can save for-- no we have to use the future. That's what it was. And the future is todos. And then the builder. Eggs. [SCOFFS] I never use FutureBuilder. The context and then the async snapshot, that's right. So this will say if snapshot dot has data; but if it doesn't have data, then return the loading indicator. Circular. Adaptive. You need to be const. Then here we get to actually return a-- could this be, like, another column? [BOTH CHUCKLE] We should really-- VIKTOR LIDHOLT: [INAUDIBLE]. CRAIG LABENZ: Explode this out into another widget, but I'm just not going to bother. Children. And here now is where we can say-- oh no. Should we-- let's put the for loop here. I haven't done this-- VIKTOR LIDHOLT: Yeah, you can put that in the list there. I think. Four. CRAIG LABENZ: Though should we say-- I kind of want to do four. VIKTOR LIDHOLT: Yeah. That should work. CRAIG LABENZ: Yeah. OK. Gosh, I haven't done this in a long time. So for todo and todos-- no, todos is-- so we need loaded todos here. Oh, wait a minute. I'm not even-- does this variable need to exist? It is-- oh, just because it's the future. But then, we don't access the todos from the variable-- we access it from the snapshot. That's right. The [? Marvin ?] method. All right. Loaded todos. Oh. It doesn't love me. VIKTOR LIDHOLT: Why is it that way? CRAIG LABENZ: We have to give that a type. And then, what's your problem? It can't be-- null-able expression. Mine is a null-able. What type do you think you are? VIKTOR LIDHOLT: Can you use a-- type the-- CRAIG LABENZ: We have to-- VIKTOR LIDHOLT: Maybe you need to type the FutureBuilder or something. CRAIG LABENZ: I thought I would infer it from this. VIKTOR LIDHOLT: Yeah. One would think. Maybe. CRAIG LABENZ: Oh, so now it's not, but it's just null-able. Did we make it null-able up top? No. Because it's late, maybe. Oh, it doesn't need to be late anymore. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: You're a kind of something to be late. Boo. OK. Huh. What's the issue here? This is when I look over Chat and realize that they all need-- yeah. OK, so we just need to cast it, someone says. Well, I'm really surprised we need to cast it. You can see that I haven't done a-- haven't used the Future Builder in a really long time. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: So. All right. So here, we're going to have a-- let's just, I guess at this point, use a text widget. It's going to be really ugly. VIKTOR LIDHOLT: You can use the list title. It's a-- CRAIG LABENZ: Oh, that's a good idea. Yeah. Yeah, yeah. That's a way better idea. The leading, maybe. And that's one, so this could be text. Actually, make this like a-- how do you? VIKTOR LIDHOLT: Title is what you want, I think. CRAIG LABENZ: The title? I was going to put the name in title. VIKTOR LIDHOLT: OK. CRAIG LABENZ: To do dot ID. I say, OK. CRAIG LABENZ: Ode to string. Right? I can get there. VIKTOR LIDHOLT: Oh, right. Yeah. CRAIG LABENZ: And then-- VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: So, title is going to be text to do dot name. VIKTOR LIDHOLT: All right. CRAIG LABENZ: None of them are done, so we don't have to do, like, check marks and whatnot. VIKTOR LIDHOLT: All right. CRAIG LABENZ: OK. VIKTOR LIDHOLT: I feel like there's a-- CRAIG LABENZ: There's an error? VIKTOR LIDHOLT: How big is the chance for this to work on the first try? [CHUCKLES] CRAIG LABENZ: The chance is low. VIKTOR LIDHOLT: What with, like, all the code around it. What was it? CRAIG LABENZ: So we go to Flutter. Restart. Oh. And we are calling it-- the button here does now use Save to do. That's right. VIKTOR LIDHOLT: Oh yeah, it's there! CRAIG LABENZ: Look at that! Now it needs a card widget around it, obviously. So map this in anything, and then just replace it with card. And the numbers are-- [CACKLES] VIKTOR LIDHOLT: Huh. Pretty sweet. CRAIG LABENZ: Pretty great. Now next thing I want to do, because I don't imagine this is going to work yet-- oh, maybe in save todos. OK. Wait, let's check here. What if in save todos, we just call load todos. So let's make this another method, VIKTOR LIDHOLT: Right. CRAIG LABENZ: Actually, no-- it's not even a future interest. Load todos. And now, in save todo-- so after we-- Oh, should we call set state in that? I'm just thinking, if we call load todos here, it's not going to update. VIKTOR LIDHOLT: Right. CRAIG LABENZ: Because of call setState, the future won't resolve yet. Then the future will resolve, so we kind of need to call setState here. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: Right? VIKTOR LIDHOLT: Maybe it updates with a FutureBuilder or-- no. CRAIG LABENZ: I don't think so, because the future won't change. VIKTOR LIDHOLT: Yeah. All right. So maybe this will work then. CRAIG LABENZ: I guess we can test it, but I think it wouldn't have worked before anyway. So we add database migrations, and we want to add hot reload. Huh. Boom. It's almost as if he knows what he's doing, folks. (WHISPERS) Almost as if. [LIDHOLT LAUGHS] All right. So let's get rid of this setState. Yeah, let's just test that. I think, without that setState, it's not going to work. Will load the todos and then not show them. That's my theory. VIKTOR LIDHOLT: That's probably true. CRAIG LABENZ: Restart. So this will be the refresh the UI. Oh, why did-- how did that work? VIKTOR LIDHOLT: I feel like when the future completes, it must do a setstate. CRAIG LABENZ: But we have a brand new future. Same variable, yes, but a totally different spot in memory. I don't think this FutureBuilder should know anything about that. Unless, oh. Oh, oh. Oh, I see why it works. It's really quite sneaky. So we call load todos. This is worth unpacking. We call the-- VIKTOR LIDHOLT: Ah, it does the setstate somewhere there, yeah. CRAIG LABENZ: Yeah, that's in a setstate. So first of all, this gets-- this gets updated before setState actually calls. After setState, that's when the frame is marked dirty. That's when the widget tree is rebuilt. And so at that point, todos has already been updated. VIKTOR LIDHOLT: All right. CRAIG LABENZ: That's right. Someone says we can use a null assertion operator on line 116. Now has line 116 changed since I-- since you typed that? [BOTH CHUCKLE] VIKTOR LIDHOLT: I think it's where you did a cast. You could have done the exclamation point instead. Maybe. CRAIG LABENZ: I think you're-- oh! OK. It is typed now. And that did come-- maybe it comes from this? Yes. I don't love that we have to do this. It should infer-- interesting. So if we don't type the FutureBuilder, then it just thinks it's an object. [LIDHOLT HUMS] It doesn't infer it from the type of [INAUDIBLE].. That's a little stinky. OK. Thank you for that, Khanat. A hot tip. All right. Well, this is really great. And now our last step. Oh, yeah. We got-- we got to clear the-- we got to clear the controller. If we don't clear the controller, what are we even doing? So our text editing controller dot text equals nothing. OK. So then this is going to be when we have to refresh. So empty controller after saving. Great. And now, we need to deploy to GCP. VIKTOR LIDHOLT: Right. CRAIG LABENZ: (WHISPERS) Let's go. And we can see all these things in Postico, and soon they will be in Google Cloud. VIKTOR LIDHOLT: All right. CRAIG LABENZ: Incredible. All right. Let's do it. How do I start? VIKTOR LIDHOLT: So I would-- maybe we can start with the database. So we need to set up all those tables in the database. So you need to be able to connect to the database. CRAIG LABENZ: Got it. VIKTOR LIDHOLT: From Postico. That's a good-- that's a good-- so I think you need to do that you, need to add-- CRAIG LABENZ: Oh, I know-- Users. OK. So I'm going to go back to Postico and set up the connection. Now folks-- we made this database just before the stream. And I saved the password. So I'll be able to make a new connection, new server. VIKTOR LIDHOLT: I think-- have you added the IP number? Oops. For your-- CRAIG LABENZ: Oh, you did mention that, and I haven't done it. I meant to. Connections. VIKTOR LIDHOLT: Yeah. This is a little bit sneaky too. CRAIG LABENZ: Oh, no! It-- oh, it is available. VIKTOR LIDHOLT: Yeah, that is the public IP, but it will not allow you to connect unless you go to Networking and add a network. CRAIG LABENZ: Are you sure? I think we're going to be able to connect to this. I mean, you've done this more recently than me. Are you, like, you're totally sure? VIKTOR LIDHOLT: I'm, like, 95% sure. CRAIG LABENZ: OK, let's try it real quick. VIKTOR LIDHOLT: Yeah, try it out. CRAIG LABENZ: So this is going to be a mypod server, and the host is this IP address, and the database was postgres, and the user was postgres. And password is saved over here. VIKTOR LIDHOLT: So you may actually want to create another database on the database in here. CRAIG LABENZ: Use a different one? VIKTOR LIDHOLT: Yeah. So the postgres is-- yeah, so everybody's a good name. [LABENZ CHUCKLES] CRAIG LABENZ: You would think so. VIKTOR LIDHOLT: But [LAUGHS] otherwise, the postgres database just keeps, like, the postgres tables, so you always have that database in there. CRAIG LABENZ: OK. I think you're winning. I'm connecting. You win, you win. You did this recently. VIKTOR LIDHOLT: Yeah. But it is enough to add if you go to that connections tab, CRAIG LABENZ: (WHISPERS) I really thought that was going to work. You can see-- VIKTOR LIDHOLT: I guess it's like a security feature, so you would not be able to, even if you have the password, connect to your database from anywhere. CRAIG LABENZ: All right. So how do I-- VIKTOR LIDHOLT: If you go to-- if you Google, like, what's my IP? You'll see your IP number there. CRAIG LABENZ: Oh. This is me. I have to put the IP here? VIKTOR LIDHOLT: Yeah. You're from-- your public IP. CRAIG LABENZ: Was this the same number? VIKTOR LIDHOLT: No, no. No, no. So you need to-- if you Google, what is my IP? CRAIG LABENZ: Oh. I see. All right. VIKTOR LIDHOLT: Usually could have a little-- CRAIG LABENZ: IPv4. This is the one I thought I was going to do it to. VIKTOR LIDHOLT: Yeah. Google usually puts just-- put that up on there, but CRAIG LABENZ: Yeah. I guess. VIKTOR LIDHOLT: You have to have a chat with your coworkers. [BOTH LAUGH] CRAIG LABENZ: All right. So this is actually just my local host connection. VIKTOR LIDHOLT: Yeah. CRAIG LABENZ: OK. And then save. OK. I see what the issue was, I see. All right. while I continue to futz around with the database-- VIKTOR LIDHOLT: Right. CRAIG LABENZ: Why don't you take this one? Does Serverpod have any built in authentication or authorization, something that allows us to block or allow access to endpoints based on roles? VIKTOR LIDHOLT: Yes, definitely. So-- yeah. We bought, like, built a very extensive authentication system. So it has support for social logins with Google, Apple. But you can also restrict endpoints to only signed in users or different roles. So, yeah. It's very, very easy to do that too. CRAIG LABENZ: I presume that it's all documented? VIKTOR LIDHOLT: Yes. CRAIG LABENZ: All right. We have our table with a Production database. VIKTOR LIDHOLT: All right so you need to add the Serverpod tables there too. So if you one up here, you have all the tables for server there . All right. So there's a bunch of tables that we add. So we use this for the log in features, and for statistics, and health checks, and authentication, and you get all those built in with Serverpod. So that's why you need to add those. CRAIG LABENZ: OK. VICTOR LIDHOLT: All right. CRAIG LABENZ: So database has been updated. It's ready. I bet now we need to go to production.yaml and make sure-- VICTOR LIDHOLT: Yeah, this needs to be up to date here. But I think you can-- what you need to really change here is the database part. All of the rest is good. CRAIG LABENZ: So I'm going to leave this one, 5, 4, 3, 2, name-- [INTERPOSING VOICES] CRAIG LABENZ: Would be the repository. It's the user from Postgres. VICTOR LIDHOLT: Yes. CRAIG LABENZ: Now-- VICTOR LIDHOLT: So I think-- CRAIG LABENZ: --connection here, right? VICTOR LIDHOLT: Yeah, you need to-- let me check it. CRAIG LABENZ: [INAUDIBLE] VICTOR LIDHOLT: It's a little bit special having you to do that. CRAIG LABENZ: Is it not just this? Because it's going to be connecting from Cloud Run. VICTOR LIDHOLT: Yes, if you-- I can send-- there's a-- if you look at the documentation and deploying Serverpod and Google Cloud around with GCP, it will show you an example how to do that. Because you need to do the dash cloud SQL, and you need to set the Unix-- sorry-- Unix socket. CRAIG LABENZ: Yeah. VICTOR LIDHOLT: Yeah. So that's an example of what that looks like. CRAIG LABENZ: Nice. Great job putting this in here. So for anyone who's not used Cloud Run-- actually, let me give a quick primer on how all of this works. The connection string to talk to a database on Cloud SQL, which is Google Cloud's Relational Database Service, is found right here. And this is the magic string. It's your project name, and then your region, and the name of your instance, not the database. The name of the instance, which for me is the same. They're both just called Serverpod. So when you have that, then you're ready to connect. Now, the best way to connect to Cloud SQL is over this Unix socket. And the way that works on Cloud Run is, once you add a connection to a Cloud Run instance, which I can show where that's done manually. And I bet his-- I bet, Victor, your scripts are going to do it-- on Cloud Run, it always places the Unix socket at this magic directory. So it makes a directory called Slash Cloud SQL at the root of the machine. And then, the name of the next folder is the same database that you're connecting to. And then, there's just this magic gobbledygook. So we would copy all of this. And you wrote database name here. But it is the serve-- well, it's a little confusing. VICTOR LIDHOLT: The instance name is what it should be. CRAIG LABENZ: Yeah. VICTOR LIDHOLT: But it also tells you to copy that string. CRAIG LABENZ: Right, which we'll have. VICTOR LIDHOLT: It's just an example, yeah. CRAIG LABENZ: Yeah. VICTOR LIDHOLT: But maybe we should actually clarify that. I bet all these things, if you get one of those wrong, it's really hard, because it will say, no. Doesn't really-- [INAUDIBLE] CRAIG LABENZ: Right, yeah, it will just be like, oh, everything's broken. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: So what I did is copied the connection string from Cloud SQL. And then, it's this middle part that I'm replacing with what I copied from the Cloud SQL console. VICTOR LIDHOLT: Right. CRAIG LABENZ: And then, these values are still correct. Oops-- these values are still correct. VICTOR LIDHOLT: That looks correct. And you need to go into the passwords file there. CRAIG LABENZ: Right, and add-- VICTOR LIDHOLT: And then you have a deployment or production database. CRAIG LABENZ: Oh, here we go. VICTOR LIDHOLT: This is the way you need to enter your password. CRAIG LABENZ: My actual protection password. VICTOR LIDHOLT: Yes. CRAIG LABENZ: I'm going to pretend like the security of this is really important to me. And I'm going to copy it in a different window. [LAUGHTER] VICTOR LIDHOLT: Sounds good. CRAIG LABENZ: Oh, no. The last-- this is really quite funny-- the last character in the password is a closing quote. [LAUGHS] VICTOR LIDHOLT: Oh, OK. CRAIG LABENZ: What a friendly password it generated. Make a new-- VICTOR LIDHOLT: Let's hope that works. CRAIG LABENZ: I don't think it's going to. VICTOR LIDHOLT: Maybe you can do a backslash in YAML. CRAIG LABENZ: That's not working either. VICTOR LIDHOLT: Ah, OK. CRAIG LABENZ: Well, I don't think it's working. Let me check. VICTOR LIDHOLT: I mean, you can add that to that passwords file, it's just a YAML. CRAIG LABENZ: Yeah, it's not-- VICTOR LIDHOLT: Oh. CRAIG LABENZ: Yeah, it's just not working. So-- [INTERPOSING VOICES] CRAIG LABENZ: --is the raw password. It's like, OK, we need a quote. We need to escape that. We need another quote. And then, [INAUDIBLE] will you just put it in the right spot? And then everything is off. But this could be the syntax highlighting. Maybe [INAUDIBLE] VICTOR LIDHOLT: I think maybe you need to put a backslash after the backslash to escape that too? CRAIG LABENZ: Of the double VICTOR LIDHOLT: Yeah, there's one earlier too. CRAIG LABENZ: Wait, what? VICTOR LIDHOLT: P4 backslash backslash. CRAIG LABENZ: Oh, here. VICTOR LIDHOLT: Maybe? Yeah. CRAIG LABENZ: I'm just going to make a password that doesn't have a-- VICTOR LIDHOLT: Yeah, that seems easier. CRAIG LABENZ: --a quote at the end. [LAUGHTER] So I'm here. And users, I think, is how this works. VICTOR LIDHOLT: It's probably possible to do that somehow, but-- CRAIG LABENZ: Change password, generate. Just give me one without-- OK, that's a good one. Hang on. [LAUGHTER] VICTOR LIDHOLT: I guess they still need your IP number to access it. CRAIG LABENZ: True. Here we go. All right, so we've updated the password. VICTOR LIDHOLT: All right, so I think-- CRAIG LABENZ: We did this. VICTOR LIDHOLT: Let's see. CRAIG LABENZ: Yeah, so what do we do next? VICTOR LIDHOLT: Scanning through the documentation here. Because it's, you have a service account. And you need to add the editor role and the Cloud SQL role to your service account. CRAIG LABENZ: OK. So and this would be the service account for Cloud Run, right? VICTOR LIDHOLT: Right. CRAIG LABENZ: Should I make the Cloud run-- I mean, the Cloud Run instance doesn't exist yet. And if this was a brand new project, that would probably also mean I wouldn't have a service account yet. VICTOR LIDHOLT: Right. CRAIG LABENZ: So should I run this command first? Should I run this? VICTOR LIDHOLT: Let's see. You probably need to-- let's see. Copy that script to your-- CRAIG LABENZ: I'm gonna start by doing what the documentation says. VICTOR LIDHOLT: Yeah, that sounds like a good plan. CRAIG LABENZ: Whoops. VICTOR LIDHOLT: You need to go into the server directory there too. Perfect. CRAIG LABENZ: Now I'm going to copy that script to the top. VICTOR LIDHOLT: Yeah, right. CRAIG LABENZ: And then I'm going to make it executable. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: And run it. VICTOR LIDHOLT: Maybe look at that script. I think you need to do some modifications there. You may be a step ahead. So check the script. CRAIG LABENZ: [INAUDIBLE] Go to this thing. Cloud, Deploy, Over here. VICTOR LIDHOLT: So it will need your connection name and that service account with the name and the regions and stuff. CRAIG LABENZ: So connection name, let me grab that. And then the service account doesn't exist yet. So I'm going to go to-- oh, that's not what I wanted to click-- IAM service account. VICTOR LIDHOLT: Yeah, service accounts. CRAIG LABENZ: Hmm, hmm, hmm. So these are some-- VICTOR LIDHOLT: Yeah, sorry. You create a new one here, create service accounts. CRAIG LABENZ: OK. VICTOR LIDHOLT: And then you can call it maybe Serverpod, or Cloud Run, or something. CRAIG LABENZ: How about both? VICTOR LIDHOLT: Oh, wow. CRAIG LABENZ: Email address, we're going to let it-- oh, this is-- VICTOR LIDHOLT: Create and continue. CRAIG LABENZ: Yeah, I guess we don't really-- this is a descriptive name. So I don't think we need much of a description. VICTOR LIDHOLT: Yeah, but now you need to add roles there. CRAIG LABENZ: So what roles do I need? VICTOR LIDHOLT: You need a basic editor. So if you-- CRAIG LABENZ: [INAUDIBLE] VICTOR LIDHOLT: If you do the browser, it will say-- if you skip this section there. CRAIG LABENZ: Oh, basic. [LAUGHS] Oh, that stinks that I didn't find it. VICTOR LIDHOLT: And then you need a Cloud SQL client. Yeah, those are the ones. And done. CRAIG LABENZ: Nice. [INAUDIBLE] VICTOR LIDHOLT: And you should get an email address for that. CRAIG LABENZ: Yeah, so I've copied the email address, just with the name I just chose. VICTOR LIDHOLT: And you paste it into there. CRAIG LABENZ: And now back to the script, and in you go. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: So I went in US West 2. VICTOR LIDHOLT: All right. CRAIG LABENZ: Run mode is still production. VICTOR LIDHOLT: Sounds good. CRAIG LABENZ: And this is fine. This is fine. This looks pretty good. Ah, yeah, this one's important, as we discovered. VICTOR LIDHOLT: Right. CRAIG LABENZ: And set [INAUDIBLE],, run mode, server list, allow, unauthenticated. Great. Allow unauthenticated is not as scary as it sounds. It just means that people not authenticated with your Google Cloud project can access it. So this means people of the internet can access it. VICTOR LIDHOLT: Right. CRAIG LABENZ: Oh, and then, you can play the Insights app. Nice. VICTOR LIDHOLT: Yeah. So it actually adds two services. So you'll be able to access the Insights app or view the logs and everything. CRAIG LABENZ: [INAUDIBLE] VICTOR LIDHOLT: Interesting. Fingers crossed, everything's working here. CRAIG LABENZ: Big money. Big money. Big money. [LAUGHTER] So let's look at what other questions we've got. Hmm, hmm, hmm. Here's one. Serverpod UI, is default or it's changeable? Now, there's two things this could be about. One is the Insights app UI. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: And the other one was the actual web app of the default thing. Obviously, the web app is completely changeable because that's just a non-opinionated Flutter app. VICTOR LIDHOLT: Right. CRAIG LABENZ: But I don't know, I think this looks pretty good. Maybe they want dark mode. VICTOR LIDHOLT: Oh, we have dark mode. CRAIG LABENZ: Oh. VICTOR LIDHOLT: It uses your System Settings. Maybe we should make it an option so you can toggle through. CRAIG LABENZ: Oh, I see. VICTOR LIDHOLT: Ah, maybe you stopped your server. So there's a little bit of a bug there. CRAIG LABENZ: I did. I stopped my server. VICTOR LIDHOLT: It's still a beta version. So you may need to restart the app. CRAIG LABENZ: Yeah, I absolutely start-- stopped everything. So, yeah, of course, that would stop working. That's still building. Let's look at some other questions here. This is an interesting one. Well Flutter make its own native serverless function support you can deploy to the Cloud simply with Flutter Deploy? The first version of this that will likely exist will be when there's pure Dart functions on Firebase. That is the likely-- I don't think they're ever going to be evokable from the Flutter CLI. But if you-- one day, Firebase will have pure Dart functions, and then we'll be quite close to this. Let's see. Oh, this is a little interesting one. Under the hood, what is the driver used to connect to Postgres? VICTOR LIDHOLT: So, I mean, we're using Postgres library. CRAIG LABENZ: Yeah. Just a [INAUDIBLE] VICTOR LIDHOLT: It's a dot library. And then, yeah. Yeah, that's what we're using. CRAIG LABENZ: Pay no attention to this problem here. [LAUGHTER] This is the library to use. VICTOR LIDHOLT: Oh, that makes-- well, so it's not [INAUDIBLE]---- no, what is the issue? It's failing? CRAIG LABENZ: Oh, yeah, I mean, there's probably some failing test or something. It's actually still going to be fine. VICTOR LIDHOLT: Yeah. But, yeah, that's sort of interchangeable, I guess, because we built the layer on top of that. Which is the whole [INAUDIBLE] typed data and stuff. So the actual Postgres driver is-- that's what we use. And it's been working great for us. It also uses the postgres pool package to pull connections. You have many simultaneous connections. So that's not an issue. CRAIG LABENZ: Super important. VICTOR LIDHOLT: Yeah. So we have tested it with our staff. And that really works well. CRAIG LABENZ: Oh, people are pointing out that with the password we could have just used double quotes. And then the single quote in the password, we would have been OK. VICTOR LIDHOLT: Yeah, that is probably true. CRAIG LABENZ: Multiple people made that observation. [LAUGHTER] Oh, there's another one. VICTOR LIDHOLT: Yeah, we need to learn to use YAML one day. [LAUGHTER] So complicated. CRAIG LABENZ: Yeah, I'm really, really quite bad at-- I consider myself just like, I don't know, I don't like YAML programming. I don't like it. I've never been able to learn Kubernetes, because as soon as I try to point my brain at really dense YAML, especially self-referential YAML, because there's so many files in a Kubernetes cluster. It's like, I just, I can't do it. VICTOR LIDHOLT: Yeah. So this is taking a long time for some reason, or-- CRAIG LABENZ: It is. It is taking a long time. VICTOR LIDHOLT: But something else is happening. CRAIG LABENZ: Oh, building and deploying new service. Reconciling latest revision template. VICTOR LIDHOLT: Yeah, I can answer this question in the meantime here. Yeah, I mean, you don't need Docker. You just need a Postgres connection. That's all you need, really. So the reason we use Docker is just it's very simple to use, typically. We'll see now. But, I mean, you can run Serverpod anywhere you can run Dart. CRAIG LABENZ: Hmm. VICTOR LIDHOLT: So maybe you can check the console and see what it's done there. CRAIG LABENZ: Yeah, I'm trying to see if that error is popping up anywhere. It's just spamming. Yeah, no, there's something going on with Cloud Run here, but I don't know what it is. I'm going to go back up. I am-- yeah, there's something is a little funky here. Cloud Run, I'm also noticing this like-- a potential violation error up here that I've never seen before. So I wonder if this deploy script is causing something to be upset. VICTOR LIDHOLT: Hmm. CRAIG LABENZ: --provide-- the user-provided container failed to start on the port defined 8080. Logs for this can be seen somewhere over here. See what it says. Failed to connect to the database. That's why we're not getting it. VICTOR LIDHOLT: Ah, all right. CRAIG LABENZ: Oh, I know why. I think I know why. No, you have this. This should work. VICTOR LIDHOLT: All right. So it must be some of the settings that is not correct. So if you can start looking at the Cloud Run instance. And it should have, in the revision, you can check the settings. Edit and deploy new revision, you can see there. CRAIG LABENZ: Oh, right. Yeah. VICTOR LIDHOLT: That it set the database correctly or no. CRAIG LABENZ: It's under connection. Yeah, no, it sees the database. It thinks it should be able to talk to it. US West 2. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: That's what I said the region was. And then I typed that into somewhere. Yeah, US West 2. VICTOR LIDHOLT: So, actually, when-- if you can check the log at the very top of it, the first time it tried to connect to the database and fails, it will print all the settings that we used to try to connect to the database. So that can give us a hint if something is not correct. CRAIG LABENZ: Let me make this bigger. The database password. Oh, wait, Unix socket false. But we're definitely using a Unix socket. We're absolutely using a Unix socket, which we can tell because we have-- I don't remember where this-- VICTOR LIDHOLT: Can you check the database or the configuration file? CRAIG LABENZ: Yeah, which one? VICTOR LIDHOLT: Config slash production? CRAIG LABENZ: Oh, yeah, that's in Production VICTOR LIDHOLT: Oh, yeah, so here, you need to specify that we use the-- CRAIG LABENZ: Use the socket. VICTOR LIDHOLT: It's called-- CRAIG LABENZ: Using Unix socket? VICTOR LIDHOLT: It's Unix socket. Is Unix socket. CRAIG LABENZ: Is Unix socket? VICTOR LIDHOLT: Yeah. So you need-- CRAIG LABENZ: This? VICTOR LIDHOLT: Yes, that's what we need. CRAIG LABENZ: So now we regenerate. I need to regenerate? Or no? VICTOR LIDHOLT: No, that's-- CRAIG LABENZ: Won't need to-- [INTERPOSING VOICES] VICTOR LIDHOLT: It doesn't hurt. CRAIG LABENZ: OK. [LAUGHTER] VICTOR LIDHOLT: So you need to run the deployment script again, I guess. CRAIG LABENZ: Yeah. All right. VICTOR LIDHOLT: Yeah, so if you do dot slash, what was the Cloud Run deploy? Try that. CRAIG LABENZ: I can only use Control R. I have no memory for previous commands. VICTOR LIDHOLT: Oh. CRAIG LABENZ: Hmm, hmm, hmm. If I want to use Serverpod on my own hosting, is it free? So I guess this is asking, is Serverpod free? VICTOR LIDHOLT: Yeah, I mean, it's all open source. So the answer is, yes. CRAIG LABENZ: That's an easy one. VICTOR LIDHOLT: That's an easy one. CRAIG LABENZ: I want to use RLS. I'm not going to lie. I don't know much about RLS. If you don't either, Victor, maybe we will look it up or skip. [INTERPOSING VOICES] VICTOR LIDHOLT: Yeah, so it doesn't really protect-- that's a way to limit users to specific tables and rows in Postgres. So we do not support that. CRAIG LABENZ: Oh. I read low level. VICTOR LIDHOLT: Yeah, it's pretty low level. So I guess you can do it with if you have raw SQL queries. But, otherwise, we don't have the [INAUDIBLE] support for it. But you can-- the idea is we-- you typically do it on your endpoints instead. So you limit access to the users on the endpoint level. CRAIG LABENZ: Yeah, you can either have Postgres firmly enforce rules, or you can have your business logic, your Dart code, enforce the rules. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: Like, I know what endpoint I'm in, and I know what group the user has to be in. And are they? VICTOR LIDHOLT: Right. So we provide a lot of help on that level. CRAIG LABENZ: Ah, we're still getting that error. So I'm going to refresh here. Well, we're also coming up on time. So maybe we give this one more go. Oh, wait a minute, it did deploy. VICTOR LIDHOLT: It looks like it so. CRAIG LABENZ: Yeah. VICTOR LIDHOLT: So you have a URL there. You can just-- CRAIG LABENZ: See if it's working? VICTOR LIDHOLT: --go to that URL and check if it's working. Yeah. It's up. CRAIG LABENZ: Oh. What URL do we want to go to actually see-- VICTOR LIDHOLT: So what you need to do now is to try this in the app. You have that URL. If you copy that. CRAIG LABENZ: Oh, yeah, I'm gonna copy it. VICTOR LIDHOLT: And let's check, if you go to the main file in your Flutter app. CRAIG LABENZ: OK. Oh-- VICTOR LIDHOLT: So in the main, yeah, at the top of that file you create a client. And that's tell you where to connect. CRAIG LABENZ: Oh, right, because we didn't deploy the Flutter app. We just deployed the server. I was somehow thinking we were going to get Firebase Hosting for our Flutter app. VICTOR LIDHOLT: Oh, yeah. Yeah, I mean, you can do that too. It's a few more steps. CRAIG LABENZ: Yeah. VICTOR LIDHOLT: But let's try that for now. CRAIG LABENZ: Yeah, I agree. I agree. Here we go. If this works, we're going to be sitting pretty. Let's see, did the Insights app deploy? No. So the Insights app is having a little trouble. Wonder why? Well, this launched. So put that over here real quick. Actually, let's do the thing first. So we're talking to the production database. So the first thing that we need to do is save a to-do. VICTOR LIDHOLT: Boom. CRAIG LABENZ: Fast. Did that really go to the server? VICTOR LIDHOLT: Yeah. [LAUGHTER] CRAIG LABENZ: That felt like localhost. Post to go. This is still the production thing, save a to-do. VICTOR LIDHOLT: Boom. Nice. We-- [INTERPOSING VOICES] VICTOR LIDHOLT: --on Google Cloud. So basically-- CRAIG LABENZ: Pretty great. VICTOR LIDHOLT: --you just need to-- when you change the code, you do another deploy and you're good to go. CRAIG LABENZ: And we win. VICTOR LIDHOLT: So that's a super simple deployment. We made a more advanced [INAUDIBLE] with whole Terraform widget, if you want to do file uploads and Reddis and the whole shebang, you have a guide for that too. That takes a little bit of time because you need to set up your own domain names and stuff like that. But if you just do something simple, this is definitely the way to do it. And it's pretty quick and easy. You just need to get all the passwords right and there's always some fiddling somewhere, I guess. CRAIG LABENZ: Right, always, always. Yeah, there's-- speaking of that, let's see. What's going on here? Oh, same error. Apparently, oh, Insights has its own connection block, and we just didn't make the change there. So that's what's going on. VICTOR LIDHOLT: Yeah, that may be true, yeah. CRAIG LABENZ: I guess I already killed that. Anyway, in production, YAML, inside server, yeah, look at us not adding-- actually, no, that is-- VICTOR LIDHOLT: No, it should be the same database. CRAIG LABENZ: Oh, yeah. OK. Wait, why did that-- all right. Well, here's a takeaway for you. We had one typo we found in the docs where it said T-- VICTOR LIDHOLT: Company instead of session. CRAIG LABENZ: Company, yeah, instead of session. VICTOR LIDHOLT: So we made that part in before the one that they released. But I guess we missed the documentation in that spot. CRAIG LABENZ: And then, for some reason, the Insight server isn't seeming to pick this up. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: Because the inside server still says database Unix socket false. VICTOR LIDHOLT: Yeah, that is weird. CRAIG LABENZ: But pretty darn-- pretty darn good. VICTOR LIDHOLT: Yeah. I'm not sure why that's happening. But I'll definitely try it out a second time maybe something didn't sync up when you did a-- I don't know. Yeah. CRAIG LABENZ: Yeah, who knows. VICTOR LIDHOLT: Maybe there was a deployment going on at the same time in the background or something like that. That would be my best guess. CRAIG LABENZ: Yeah, I don't know. That's kind of weird. But anyway, all right, folks. I think we accomplished a lot today. We built a simple thing in Serverpod, talked to a local Postgres database, saw that working. It's now running on Google Cloud. We haven't deployed the Flutter web app. But you could put that on Firebase Hosting quite simply, or any other hosting choice that you like. And then we saw that working. It's a local host, a local host build of the Flutter app, which is in this window, is talking to the production deploy of the back-end. And it's doing it really fast. Holy smokes. Really fast. [LAUGHTER] Nice. VICTOR LIDHOLT: Nice. CRAIG LABENZ: Man, really, really cool. So, Victor, man, thanks for all the time and the energy that you've obviously got an unimaginable amount of time and energy that you've put into building Serverpod. And now, it's getting to be ready. So you're starting to advocate it and talk about it. You've been having these nice release videos. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: Any final thoughts from you, Victor? VICTOR LIDHOLT: Yeah, I mean, today we just scratched the surface of what you can do with Serverpod. Obviously, we have support for everything from real-time communication, authentication, with social logins, caching into server communication if you have a cluster of servers. You can build stateful servers with a little bit more work. So there's, I mean, that's what we have already. And we are just building a whole lot of tools around this, both the visual editor will come with the database viewer very soon. And you will be able to do your database migrations with Serverpod. And so, that will take away a few of the steps that we made today too, which will be really nice. CRAIG LABENZ: Hmm, right. We can manually apply those schema queries. VICTOR LIDHOLT: Right. So that will be all automatic in the future. Yeah, and go check it out, serverpod.dev. And follow. CRAIG LABENZ: Nice. Yeah, where can people join? Do you have a Discord? VICTOR LIDHOLT: So we have a community on GitHub. The discussions there is the best place to reach us. So we check in there every day, answer questions. And there is already-- I mean, there are hundreds of apps being built with Serverpod already. So it's some really big companies. I'm not sure I can give the names here. But I mean, there are some serious apps being built with Serverpod, which is really cool to see. So it's, I think, we tried to really minimize the effort to build servers and making it really smooth. I think if you compare the amount of code you write with Serverpod with other frameworks, it's probably going-- I think we're going to win all categories there. CRAIG LABENZ: Yeah, I mean, based on what we just did, yeah. That was incredibly simple. I really love-- gosh, I just loved-- and I want to go back and cover one last time-- how this worked where we write a simple method on the server. And then we never cracked open this my pod client thing. But that's where the common stuff is going, I understand. VICTOR LIDHOLT: Right, yeah. CRAIG LABENZ: So in here, this is the stuff shared by everybody, and allowed. VICTOR LIDHOLT: Yeah, so you can open it. CRAIG LABENZ: Can I actually peek at it? VICTOR LIDHOLT: Yeah, why not. Look at the to-do class, for instance. So this is the code that you would have had to write otherwise to do all that stuff. It's quite a bit of stuff to do the serialization and communication with the database. And it's really nice to be able to use the same objects. You pull them from the database, and you can send that object to the server. If you use different protocols here, you need to convert them somewhere along the way. So we handle those conversions for you. And you can specify. Say you have a user info with passwords or hashed passwords. If you have passwords, I would suggest. But then you can just mask them from the protocol so they just work with the database. So you can set different scopes for where the information is visible. So you just specify that in the YAML file. And you can also do-- you can actually throw exceptions on the server side and catch them on the client. So we can serialize those exceptions, which can be a really smooth way to handle something-- [INTERPOSING VOICES] CRAIG LABENZ: --toast or something? VICTOR LIDHOLT: Yeah, right. So it's very seamless to work with Serverpod. It's almost like you have the server and the client, it's like the same system. Obviously, there is a layer in between. But you just add the methods in the server, and you just use them on the client. So that's the gist of it. CRAIG LABENZ: That's quite cool. Yeah, that's quite cool, the magic of code generation. VICTOR LIDHOLT: Yeah. CRAIG LABENZ: All right. Well, Victor, thanks for joining. I had an absolute blast here. Everybody, I'm going to be traveling for a while. I'm going to be in Miami for I/O connect. Then I'll be in Paris for Flutter Connection. Then I'll be in Amsterdam for I/O connect again. And then, lastly, I'll be in Berlin for Flutter Con alongside Droid Con. So, Observable Flutter is going to go on a little summer holiday, and we won't be back until mid July until after I return from Flutter Con in Berlin. So, Victor, you were the final guest for the next probably two months. VICTOR LIDHOLT: Yes. CRAIG LABENZ: But you know-- VICTOR LIDHOLT: I'll sure I'll meet you in person in Berlin. I'm giving a talk there on building multiplayer games with Serverpod. CRAIG LABENZ: Nice. I was going to say, yeah, what are you talking about? [LAUGHTER] VICTOR LIDHOLT: Yeah. What am I talking about? But also, I used to be a game developer, or worked a lot with game tools. So that's definitely something that I care about too. So I wanted to combine those into building some quite simple multiplayer game. CRAIG LABENZ: Nice. Yeah, my talk is related but different. In Berlin, my talk is titled something like, a year of headaches, how not to build a real-time multiplayer game. [LAUGHTER] And it's just a nonstop story of misadventures. And, anyway, I'm looking forward to your talk, and I'm looking forward to that whole event. VICTOR LIDHOLT: Yeah, it should be really good. CRAIG LABENZ: Yeah. Victor, thank you. Everybody who tuned in today, thank you. A lot of great questions. We didn't really-- we were not able to get to them all. But we did at least get to deploying this app to GCP. So that was pretty great. VICTOR LIDHOLT: Cool. CRAIG LABENZ: All right. All right, everybody. That will do it for us today. And until next time, yeah, thanks for watching. VICTOR LIDHOLT: Yeah, thank you.
Info
Channel: Flutter
Views: 16,768
Rating: undefined out of 5
Keywords:
Id: 8sCxWBWhm2Y
Channel Id: undefined
Length: 112min 4sec (6724 seconds)
Published: Fri May 19 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.