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.