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