CRAIG LABENZ: Hello, everybody. Welcome to another episode
of "Observable Flutter." I'm your host, as
always, Craig Labenz. But maybe one day
someone else will host-- just "as always" so far. Anyway, today I'm excited to
talk to everybody about gRPC, which, for those who don't
know, stands for, I believe, Google Remote Process Call. I know it was
invented by Google. Maybe my guest will
be able to clarify exactly what the G
looks like-- or stands for, which I realized, as
I started this sentence, I've never actually
looked up before. I've just assumed. But before we get
too far into that, I just want to remind everybody
that here on "Observable Flutter," we're all
very kind to each other. This is the Flutter community. Let's live up to our good name. All right. And my guest is Gianfranco,
who is the CTO and Co-Founder of Somnio Software in Uruguay. And many of you will know him
as just a delightful presence on Twitter in the
Flutter community and an all-around great
software engineer. So Gianfranco, man, I am
really excited to have you. Welcome. GIANFRANCO PAPA: Hey. How are you, Craig? CRAIG LABENZ: I'm great. I'm great. I'm very excited
to talk about gRPC. Maybe, if you'd
like, you could begin by just saying a few
words about your history with tech and in Flutter,
and how did we get here? GIANFRANCO PAPA:
Yeah, of course. OK. So first of all, thanks
for having me here. I'm really excited. I always see the show, so
being part of the show right now is really-- I'm very excited. And yeah, maybe I
can introduce myself. I am Gianfranco Papa, and
I am CEO and Co-Founder of Somnio Software. And yeah, I'm a
software engineer who specializes in Flutter. I really love this technology. And what else? I've been growing the
community in Uruguay here in Latin America. I'm Co-Organizer
of Flutter Uruguay. Yeah, so that's really cool. And yeah, in our company,
we are basically a dev shop that specializes in Flutter. We are 100% focused
in this technology. So yeah, we started
at the same time that Flutter
actually was created. We started with a
beta back in 2018. And then we kept
working in projects and realized, OK,
this is something big. So yeah, that's
how it all started. We basically trusted
in this technology. And yeah, back then we were
working with mobile apps. But now, as we all do now, we
can work with web on desktop. So that's really
great that Flutter keeps opening us doors to
work in any kind of project. CRAIG LABENZ: Nice, yeah. I know we've met
at a couple events. And I always love
to meet and talk to folks who go all the
way back to the early beta, because I don't even-- I found Flutter shortly
after it went to 1.0. So you've got a longer
Flutter tenure than I have. But today, we're not just
talking about Flutter. We're actually hitting a more
kind of Dart-centric concept. Although the concept
itself is larger than Dart, we're just going to implement
it in Dart, which is gRPC. So Gianfranco, you pitched
this episode idea to me, I mean, a long time ago--
basically right when "Observable Flutter" started. And so you knew immediately that
you wanted to talk about gRPC. Tell us a little bit about why
the average developer should agree with you and also
be excited about gRPC. GIANFRANCO PAPA: Yeah, OK. So yeah, I remember
that we talked about even before "Observable
Flutter" was a show, we were talking about maybe-- "The Boring Show,"
I think that I asked you to talk about gRPC
because, yeah, back then, I think I saw a video
about full-stack apps but with WebSockets. So yeah, the thing
about gRPC is that it's another tool you can have
in your toolset to create full-stack apps in Dart. Because as it has
Dart support, you can create your frontend
and your backend by only using Dart. And that's really advantageous
for us Flutter developers who already know a lot Dart
and want to also create backend applications
and we don't want to switch to another language. But I mean, we all know
that there are other things or tools to create
backends in Dart, for example Dart Frog or Shelf. But in the case of gRPC, I think
that it's slightly different because you can not only
make your backend in Dart, but you can also use other
programming languages seamlessly. So yeah, that will be because-- we are going to see it,
but you can basically-- I mean, gRPC has support
for a variety of languages. And you can basically
autogenerate with a thing that is called
protobuf whatever language you are targeting. So it's kind of more or less
language agnostic, in a sense. CRAIG LABENZ: Yeah. The cross-language
part is a huge deal because one of the driving goals
or a value that a developer gets by having a
full-stack Dart application is to be able to reuse
a bunch of classes and not have to duplicate
logic and whatnot. And there are still-- even if you use gRPC,
having Dart on both ends will kind of put you in the
best possible scenario there. But if you can't use Dart on
the back end for any reason-- we'll be using Dart
on the backend today. But if for some reason you
can't, that's a great point. Because those
bindings are generated on both ends in a
variety of languages, whether you're using
JavaScript or Python or Java or whatever on the backend,
the task of communicating with yourself across
that language barrier becomes so much simpler because
from that simple protobuf definition you mentioned,
which we'll get into, everything kind of
falls out of that. And by the way, if any of you
ever want to work at Google and you want to apply
to Google, familiarity-- like protobufs, that is what
every Google Developer uses every day. Everything at Google
runs on protobufs and the code that is
generated downstream of that. So you might be
training yourself a little bit for your next job. All right. Gianfranco, I believe
you are ready to walk us through some steps
of getting started. Am I right? GIANFRANCO PAPA: Sure, yeah. Let's get right. CRAIG LABENZ: OK, let's do it. GIANFRANCO PAPA: Perfect. OK. So my idea for today, we
were discussing with Craig to present kind of
something from the various-- from scratch. It would be something
real simple, but yeah, the idea is
to make it from scratch. So it won't be
something very fancy. But yeah, first of
all, I wanted to have the documentation open here. So actually, I
consider that gRPC is not a topic that there
is so much information on, like blogs. Or it's not so
common such as Rust. But yeah, here's
the documentation. So you can always go. And here are the official docs. Well, "gRPC is a modern,
open-source, high-performance remote procedure call." So remote procedure call
would be the protocol that we are going to use. Just as you have
REST or WebSockets, here we are going to use RPC. And I think the G, it
could be for Google. I actually didn't
ask that question. But probably it would be
because of Google, right? CRAIG LABENZ: That's
my thought as well. Randal tells us that it's just
one of those cheeky recursive algorithms-- or acronyms. So I guess the G is
officially not for Google. It's just meaningless. GIANFRANCO PAPA: A coincidence. OK. CRAIG LABENZ: But since
this was invented by Google, if they picked a
meaningless letter, I'm not surprised that
they picked G, so. GIANFRANCO PAPA: Right. Makes sense. So OK. And here, you can get started. We have a lot of languages. Of course, we are
going to choose Dart. But you can play around with
any of these languages that are very common. And so yeah, another
thing that I want to show is the quick intro. So here, we have an intro. And it will also talk
about protocol buffers that is really tied-- I imagine that you can kind
of look to protocol buffers as JSON for REST. The protocol buffer
for gRPC would be kind of the same the way
it communicates information. But yeah, we are
definitely going to explain better this term. So yeah, I wanted
to only show that. Maybe we keep in hand the
documentation for some things. But we are switching
into VS Code, and we are going to create a
full-stack app from scratch. So yeah, the first thing I
want to talk about, actually, is protocol buffers because
maybe we can start our project by creating some protos-- short for "protobuf." And yeah, my idea is to have
three different folders-- one for the frontend that
will be made in Flutter, then another one for the backend
that will be made in gRPC-- using gRPC, and another one
that will be the share folder that we will call it protos. But we will use it in the
backend and the frontend. And that is one of
the first advantages, that we can reuse a lot of
the code between the frontend and the backend. OK? CRAIG LABENZ: I love it. Yep. GIANFRANCO PAPA: Perfect. So I'm going to just create-- OK. We are here in the terminal. I'm going to create a project. I always forget what
is the template, the default template, so-- CRAIG LABENZ: You
want server-shelf? GIANFRANCO PAPA: No. Actually, we're going to
start with the protos package. So I'm going to select a
package so we can store all of our proto buffer files. So OK. So we're going to create
using the template-package. Oh, maybe we can create
the directory first. So let's create a protos folder. And then we can
move to the protos. And here, we can
create the package. So if we hit package
in this directory-- OK. I might force this. So here, we have a pure
Dart package, real simple. So maybe we can delete
the test folder, because we are not
going to have any tests. And this is going to
explode sooner or later. This is not a good practice. CRAIG LABENZ: Well,
you wouldn't really write tests for your protobufs,
because gRPC is well tested. So unless you just didn't even
define your model correctly, you wouldn't have tests in
this folder, I don't think. GIANFRANCO PAPA: Yeah. I guess you are mistrusting
the way gRPC works, or protobuf, yeah. CRAIG LABENZ: Right. GIANFRANCO PAPA: OK. So here, we have an example. We have a lib folder. And the first thing to do
would be to actually grab our dependencies. So we are going to
use protobuf and gRPC. Those are Dart packages
that you can see popped up. So yeah, let's make sure
that everything is in place. So here we have
our dependencies. Let's put here the dependencies. And that's it, right? CRAIG LABENZ: Looks good. GIANFRANCO PAPA: Perfect. OK. So to create our
first proto file, maybe we can create a folder
that will be called protos. And here, we can actually
create our first definition of our protocol buffer model. And I was thinking
about a basic todo file, but we can choose anything. It would depend on
what you want to build. But let's start with a todo. And the extension
would be proto. So in here, we
can start defining what would be the
protobuf for our model. So I want to get back
to the documentation so we can see the basic
syntax of the protobuf. So let me search in here
for the protocol buffers, that it has separate
documentation. But here, it would be
more or less the syntax that we have to use in order to
create a model using protobuf. So you see that if we would like
to create maybe a todo class, we will have to use
the keyword "message." And then for defining
every property, we'll have to use
this special notation where you define the name
of the property and a code. That code would be like
an incremental integer. CRAIG LABENZ: And that
"message" keyword, that's kind of
like-- it's basically just the keyword "class" in
any other language, right? But it's written as "message"
here because the whole idea of gRPC is the ability to
get this data on the wire, how to send this
data somewhere else. So gRPC thinks of all
these things as messages. Their data that
starts on one computer is going to go to
another computer. But it's really similar
to the keyword "class" if you're looking at
this for the first time. GIANFRANCO PAPA:
Yeah, it could be. So yeah, the thing is that in
order to have this connection, this interchange messages
in an efficient way, gRPC-- protobuf, sorry-- reduced the
whole concept of a message that we are passing through services,
so through the frontend and the backend, so it can be
more efficient in terms of size because if you know JSON-- probably you know it-- it will be something that
is really easy to read. But it's not so efficient
to pass over the wire. So in this case, we are
not producing something so readable, but it will
be very efficient to pass back and forth. OK. So maybe-- CRAIG LABENZ:
Yeah, JSON wastes-- oh, sorry. I was just going to say
JSON spends so much time-- or wastes so much space
with all the parentheses or the curlies and the quotes
and just sending things as raw strings. And gRPC also sends
this in a binary format, so it's incredibly efficient. GIANFRANCO PAPA: Right. So yeah, maybe we can start
defining our first message. So I'm going to copy
and paste this example. We are going to use
this syntax proto3 here. And in here, maybe we
can rename this to Todo. So maybe we can
also remove this. So if you want to also keep
in hand the documentation, we have different types
that protobuf supports. Let me see. But I'm searching for the types. Using the default values-- CRAIG LABENZ:
Scalar value types? No, that's just-- oh, you're
on the whole list here, yeah. You're looking for a single
list that shows all the types. GIANFRANCO PAPA: Right, yeah. Yeah, this is the
whole list of-- yeah, maybe there is a
section where you can actually search all of the values-- your types. But that's not a problem. I mean, we are going to
use basic types here. We're going to use
int32 for our ID so we can define the
name of the property. And remember to define
an actual value that will be an incremental value. This is just for
encoding the information. CRAIG LABENZ: It's basically
like the order of the fields, right? It's nothing more than that. GIANFRANCO PAPA:
Right, of course. So instead of-- right. Like, and JSON would be-- like, you could read it. I know the ID will be
this field, key value. And this would be-- the number 1 will be the
ID, but nothing else. And yeah, these
have to be unique. So apart from that, we
can define a title maybe. So this would be number 2. And then we can use
another type that is bool to say if this
is completed or not. So this is, in essence, our
definition of our Todo message. And we are seeing this in
the proto buffer notation. But the cool thing
about this is that we are going to actually
autogenerate this in Dart-- it can be in any language,
but we are using Dart-- so you can start
sharing your models, not only with your
frontend team, but also with anyone
that wants to implement a definition of Todo. So this is really
an ultimate solution for having a single source
of truth for your models because you are
sharing the whole model with a lot of-- even languages,
programming languages. CRAIG LABENZ: And boy,
did we not write much. That is a terse definition
of this Todo class, I'll even call it. GIANFRANCO PAPA: Of
course, of course. OK. So here, what we have to do is
to create another folder that will be generated. And maybe we can remove this. Once it's part of the
library, we won't need it. Delete. And here. Here, we can export things
later in our library. And here, we are going
to actually export our generated classes. So the idea is to grab
using the protoc command that you can download it. I mean, we skipped the
setup, but the idea is that you can use this setup. Let me see. There is a quick
start for this, but-- OK. So let me do a search,
"quick start"-- in Dart. OK. So basically, here we have
to download the protoc plugin and activate it and
then export the path. And then we can start using
the protoc command line in order to generate
these proto files. So I'm going to
copy this command because it's hard to remember. But we can say, OK, in
here I'm using the protoc. Your Dart output will
be the generated folder. And we are basically
grabbing everything that is under the protos
folder, each and every file, and we are autogenerating
Dart classes. So if we hit Enter, this will
generate a lot of classes, a bunch of classes in Dart. And yeah, this is
not so readable. But the thing is that you
have your models in Dart, and you can start using it. So you have your-- you can actually here create
an object that is Todo. Great. So what we can do here right
now is to actually export the-- let me see-- all of these
files in the library. So maybe in this file,
we can export it. So it would be
src/generated and todo.pb-- protobuf-- .dart. And we can do it
with the other ones as well just in case we need it. But you will find that
everything is-- oh, looks like enum and then json. So great. So here, what we did
is that we defined-- we have to-- maybe we can
remove the example as well. We just defined our
definition of our models. And we can start sharing
this package in our frontend, in our backend. And we will have access
to our Todo object. OK. So maybe another thing
we can do right now is to create our backend
and in a separate package. CRAIG LABENZ: Sounds good. GIANFRANCO PAPA: Perfect. So maybe we can go back
to our full-stack app and create this time server. So I'm just creating
another folder. And if we enter to
this folder, here I would like to use another
template because this would be a package that we can
import in our frontend, but maybe we can use the
console app so we have our main and we can start our server. OK. So this will be actually
the default template. So we hit dart create, instead
of specifying a template, we can use the default one. And we are going to force it
to be created in this folder, in the server folder. So here, we have our projects. And maybe what we can do now
is to import the protos package into our server folder
so we can start using it. So because this is a package
that we have locally, we have to basically
provide the path. And we can actually
say, OK, let's grab the path in the protos folder. Great. OK. It's saying that--
oh, we have to add the "publish to none"
just really quick so we don't have that warning. OK. Perfect. All right. So the idea here
would be we can also-- well, this is going
to also explode because we have our
calculate method, and inside bin, we
have our server.dart that is a main function. But here, what we
actually want to do is to, yeah, create
the gRPC server so anyone can start connecting
with the gRPC server using a client. And yeah, one of the things
that maybe we didn't mention is that a cool thing
about this protocol is that you can call a
function, a remote function, as if you were calling
directly in your frontend. So it will be a really
seamless communication. We are going to
see it, but when we are going to be in
the Flutter app, it will be calling the server. It will be like
calling a function that we have in our
frontend project. It will be really, really great. You don't have to create an
ECP request and parse in. It's like you just kind of call
the function and that's it. But yeah. So here, we have our main file. I'm going to create
the server right now. So this is also some of
the code I bring just to speed up this part. So let me quickly
copy and paste this. OK. So basically, we have
to define a server. We're going to erase
this just for now. But this server will come
from the gRPC library. Oh, we have to also install
this library because we are in the other project. We can actually export it from
the proto library, let me see, inside lib. We can export as well
the gRPC package. So we can start using
it in our server. So if you go here, we can,
yeah, create our server. Then we have to run
our server in a port. I don't know why-- yeah, dart:html. So basically, we are retrieving
the port from the environment. Or as the default, we
can use the 8080 port. That's really default one. And then yeah, we have to
name this function async because we are awaiting. And finally, we can
simply print the server is listening in the port 8080. So I guess we don't
have to use this import. And that's it. This is, like, our whole
server, our whole gRPC server that we are going to
connect in Flutter. And one thing, one
parameter, that we are not seeing right now is this array. This will be an
array of services. That is something
that we actually didn't talk about so far. So maybe we can jump in
and talk about the services because, till now, we
only defined a model. CRAIG LABENZ: Message. GIANFRANCO PAPA:
Right, the message. But we have to define
also the functions that we are going to
call in our frontend to communicate with gRPC. So to do that, we can get
back to our proto file. And instead of
defining a message, we are going to define
this time a service that will be more or less
like an interface that you need to also implement. But here, we can define
all of our methods that we are going to use
to communicate with gRPC. So maybe we can name
this TodoService. And in here, we can use
the notation of rpc. We have our method name, our
request, and our response. So basically, this
is the notation. And I was thinking about
getting a simple todo from gRPC. So maybe we can
name this getTodo. So here we will-- CRAIG LABENZ: Now, we
won't have anything to get until we create
something, right? GIANFRANCO PAPA: Right. Yeah. I mean, maybe we can hardcode
something just to see it. CRAIG LABENZ: OK, sounds good. GIANFRANCO PAPA: Or maybe,
yeah, we can also create-- have a method to create a todo. But let's start with
getting a Todo so we can-- CRAIG LABENZ: Yeah,
hardcoding is fine. GIANFRANCO PAPA: Yeah, perfect. OK. So in here, what we will
have to do is to have a-- so remember the method. We have a request
and a response. So we will have to
return something. And this is the actual
keyword "returns." And we actually have to
create another message because we will have to have
a request and a response that are also messages. So in this case, to create a-- we can create a message
called GetTodoRequest. Maybe it could be by ID. So we can add an
ID to the request. And maybe we can put this here. And here, as we did
with the message Todo, we are going to provide
maybe an int32 so we can pass the ID through the message. If we want to get
the todo by the ID, it's a good idea
to assign the ID. So here in our method, getTodo,
put the GetTodoByIdRequest. And we can return just a todo
that we already put it in our-- we already have a
message for that. So basically, this method will
be sending GetTodoByIdRequest that it has an ID,
and will return a simple todo that, in this
case, will be hardcoded, yeah. All right. So, perfect. And here, again, as we
modify the proto file, we need to autogenerate
again the generated classes. So we have to hit
again the command line. And let's go back to our
proto file-- proto package. CRAIG LABENZ: And normally, if
we were running build_runner here, this is when we'd all
get up and go get some coffee. But this is a pretty
fast generation. You blink, you miss it. GIANFRANCO PAPA: OK. So maybe we can grab the 500-- so we can pass this directly. And here, we can
notice that there is another class that was
generated because we are including services right now. So in this case, this
is not so readable, but it creates all
the communication to actually call
the getTodo method. So maybe what we
can do right now is to export this class so we
can have access in our backend. So here, we can-- CRAIG LABENZ: grpc, right? Nice. GIANFRANCO PAPA: grpc, right. Perfect. So I think we won't be
creating so much other things in our proto file. But maybe we will
create other methods. But we already have a
message and a service. And that's it to basically
have our very first example connecting Flutter with gRPC. OK. So let's get back to our server. In here, what we
want to do actually is to create a todo service
so we can expose the service to our frontend. So what I'm going
to do right now is to actually just
create the service. But the thing is
that this service, we didn't create it, because
we have our base service that is what gRPC-- that protobuf created,
autogenerated. But we have to actually
implement the service. So what we can do is to create
a service class for the service. We're going to name
it todo_service. And here, we can actually
create the service. And we are extending
the TodoServiceBase. So this TodoServiceBase,
what we'll have, we have red highlight here
because we are not implementing yet the getTodo method. Remember that we defined
our method in our service. So it more or less works
such as an interface. You need to implement those
methods so we can basically override them. And in this case,
basically what we can do is just to return a todo,
like a hardcoded todo, so we can have basic
implementation of this method that is returning a todo. So one cool thing
about protobufs-- if you already have
your Todo class, you can use it with named
parameters such as here. Or maybe you have
different methods such as getDefault.
And maybe this is returning no other than the
default implementation with, I don't know, like
empty parameters. But we can maybe create
some basic parameters. Maybe the title,
we can do "title." CRAIG LABENZ: For the ID, can
we pull it out of the request? Why don't we use the
ID that was passed in? GIANFRANCO PAPA: Yeah,
that's a great idea. So basically, if we
go here, maybe we can put final id request.id. And we will use this ID instead. Perfect. So we will be like-- yeah, the frontend
will generate the ID, and we will return a
new Todo with that ID. And then we have the completed
param that is missing. This will be false. Maybe what we can do also
is to put in the title the ID so we can have our
pristine title, in a sense. And yeah, another
thing that we can do is to actually return
the todo, because this is returning a Future of Todo. So here, what we are missing
is the async keyword. And that's it. I mean, we are implementing
this getTodo method and returning it. And so yeah, this
is our definition. And now we can make use of the
service inside our server file. So this will expose this to
the service in the gRPC server. And we can start using
it in our frontend app. And that's really it. OK. So the last thing we
need to do, actually, is to create our frontend
app, Flutter app. So we have everything running. We'll need to, in this
case, create another folder. So maybe you can create
a client app in Flutter. And in here, we are going
to create a basic app in Flutter using the
template, the create template. And as I'm running my
example in a MacBook Pro, maybe we can create it using
Android, iOS, and MacOS. We could use the
web, but it's not going to work,
because the thing is that to deal with
gRPC in the web, we have to do some special
things that it doesn't have full support
yet out of the box. We have to do some
more advanced things. But yeah we can definitely test
it in mobile and desktop, OK? CRAIG LABENZ: Sounds good, yeah. GIANFRANCO PAPA: So maybe for
the name, we can choose app. I'm going to use the
Somnio organization. And then maybe the platforms,
we can choose, as I said, Android, iOS, and MacOS. OK. Let's create everything here. OK. So this should create
our app in Flutter. Really basic stuff. So we have our folders
ios, android, macos. And we have our pubspec.yaml. So maybe what we
can do right now is to clean a little
bit this pubspec.yaml. So what I can do is to use some
regular expressions to actually clean up this. So let's put-- I think it was-- yeah, the notation was
just like this, right? Oh, you have to select
the regex, right? CRAIG LABENZ: Yeah. You're looking to get
rid of all the lines? Yeah. So .*, there you go. Yeah. And then-- nice. Is it not
autoformatting for you? Oh, so generally I replace
that with a new line because sometimes if you don't
replace it with a new line, it can squish things up. GIANFRANCO PAPA: OK. And we are going to do the
same for our main file. So here, let's replace
this for the comments. CRAIG LABENZ: You
know, I found-- I discovered the other day the
--empty flag on Flutter create. It just doesn't put
all this gobbledygook in the files for you. GIANFRANCO PAPA: Oh. So there is a flag to do that? CRAIG LABENZ: Yeah,
flutter create --empty. You've already done
the heavy lifting. But for the future, --empty. GIANFRANCO PAPA:
Maybe we can see it. Yeah, here it is. Specify, no comments. Perfect. Yeah, that's a nice tip so
we don't have to use regex to clean everything. Right. So here what we have to do is to
actually import our protobuf-- proto package, as we
did with the server. So maybe we can
quickly put protos. And also, the path
would be the same. Perfect. So we can have
access to everything. And just for speeding
up the example, we are not going to use any
kind of state management or repository pattern
or something like that. I think that we can just use the
stateful widget that we have. CRAIG LABENZ: Use
our imaginations. GIANFRANCO PAPA: Yeah. And yeah, probably there are
best practices to follow here, but I'm going to speed up. And we kind of would like to
have a client channel so we can start communicating with gRPC. For this, we need to
have a client channel. And we can have this property. Maybe what we can do is to
import the package protos. Protos? Yeah, because we are
exporting the grpc package in that package. So we can then initialize
this channel in our initState. So let's create our initState. And here what we can do is to
create a ClientChannel here. And the host will
be, in this case, localhost, because we're
working in our machine. And the port, remember,
we choose 8080, although it could
be the one that we are providing to the server
through the environment variables. And we also have to pass-- let me see-- in the
ClientChannel the channel options. So for this, we are going to
go for the ChannelOptions, like the insecure mode. But this will depend on if
you want to provide some SSL certificates. But we are going to
go with the fastest. And let's put
everything on const. OK. So here, we already
have our channel. And what we need to create after
right now would be the stub. So the stub would be
just an abstraction, a proxy that we can
use to communicate back and forth with gRPC and
call, actually, our methods. So what we can do here
is to create our stub. And to do that, let's quickly
review how to use that. So basically, we
will need to call another autogenerated
class that we have that would be the TodoServiceClient. So let's put here late
TodoServiceClient. And this will be our stub. So we can also initialize
this in our initState. So here, what we can do is,
basically, TodoServiceClient initialize and send the channel. And that's it. We have our stub,
and we can start using it to interact
with all of the methods available like our
getTodo method. So yeah, let's hide the
terminal one moment. So well, we can
notice that here we have the basic counter example. So what we can do is to
maybe put a fill here to-- maybe what we can do
is have the todo here. This won't be late. So we can display that todo in
our main screen if we have one. So that's why it's nullable. But yeah, in our
counter, this is really setting our state
of our counter. We are going to
delete our counter because we don't need it-- also this. And here, what we can do is
get our todo from the server. So we're going to
replace this in our icon. CRAIG LABENZ: Nice. GIANFRANCO PAPA:
And here, maybe we can say, OK, if our todo
is different than null, we can display maybe
a text with the title. OK, we have to use the-- right. So this is here. And else, we can display maybe
a text that it would say, "get your todo." CRAIG LABENZ: I love it. GIANFRANCO PAPA: Perfect. Really simple. We are basically testing
the whole connection. And OK. So maybe what we can do also
is to put this in a column so we can display
more information. Actually, maybe we can
display also the ID and whether or not
this is completed. And so this information
will be an integer, so we have to parse
it, and this as well. This would be a
Boolean, so perfect. I think this is
complaining about the-- OK, the const. Perfect. OK. So basically, if
everything works, we will be able to see actually
the todo in our Flutter app only after we click
on the getTodo button. Perfect. So how would we do that? We have to, on one
hand, start the server. So we are going to move
to the server folder. CRAIG LABENZ: Starting the
server sounds very helpful. GIANFRANCO PAPA: Yeah, totally. So we have to-- if we go back to the
server, we can see that we have our bin folder. And inside here,
we have the server. So we can simply do
dart bin server.dart. And the server
should be starting. OK, the server is
listening on port 8080. So yeah, it's not like
we can go to Postman and start testing in Postman. It's more difficult because,
yeah, this is another protocol. But we can run our app and
see if everything is working. So here, I can open another tab,
and we can go to our client. So here what we need to
do is to run our app. And I'm going to run
it first on MacOS. CRAIG LABENZ: Yeah, MacOS-- absolutely the easiest
way to get started. No emulators. GIANFRANCO PAPA: No,
yeah, you don't have to start it with emulators. That's right. But we can actually test
it in iOS or Android. The thing with MacOS
is it's not going to actually work the
first time, because we have to configure something-- a special parameter
in the configuration. But yeah. CRAIG LABENZ: [INAUDIBLE],,
that's what it's called? GIANFRANCO PAPA: I think it
is something like that, right? CRAIG LABENZ: Yeah, to be
able to send network requests. GIANFRANCO PAPA: Yeah. So I'm going to show that
this is going to fail, but then we are going to fix it. So yeah, so far, I mean,
that is the whole example. Another thing that we are going
to do that is really especially important in gRPC
is that we are going to be capable of
streaming different things from the server. So we are going to push
information into our client. And that's really nice. It has a lot of use cases. So here, we have
our "Get your todo." And if we hit the
button, this won't work. But yeah, actually-- CRAIG LABENZ: Are you not
getting an error for this? Shouldn't it be-- GIANFRANCO PAPA: I'm not
getting an error, yeah. CRAIG LABENZ: Oh, because you
didn't put anything in getTodo. GIANFRANCO PAPA: Oh, we
didn't put the method, OK. So here what I need to do is-- CRAIG LABENZ: I
thought I missed that. GIANFRANCO PAPA: Yeah. Here, we need to
actually get our todo. So here we need to pass the
todo request that we created. CRAIG LABENZ: We kind of do
need that counter, actually. GIANFRANCO PAPA: Yeah, right? Maybe we can create
a random ID here. CRAIG LABENZ: All right. GIANFRANCO PAPA: So maybe-- I think it's Math.random. I have to export from the-- CRAIG LABENZ: Whoa. While you're typing that,
Postman added gRPC support. GIANFRANCO PAPA: Oh,
I didn't know that. CRAIG LABENZ: That's crazy. GIANFRANCO PAPA: So
someone is saying-- CRAIG LABENZ: Yeah, that's cool. GIANFRANCO PAPA:
That's very cool. I have to test that, yeah. CRAIG LABENZ: Yeah,
thanks for the tip. GIANFRANCO PAPA: So
here, let me see Math-- probably there is a library. Oh, it's Random, I
think, plugin, right? CRAIG LABENZ: Yep. I think you have to
import math for it. GIANFRANCO PAPA: Right. So nextInt-- maybe
we can have 100. So we can use the ID here. OK. So there we are
getting the todo. And we have, of
course, to await this. And then we can set the state
of our todo to this.todo, right? CRAIG LABENZ: Oh,
right, because you added the keyword "final" there. So it actually had not
set it on the widget yet. Nice. GIANFRANCO PAPA: Right. OK. So now, again, if
we restart, we are going to actually do something
in our getTodo button. And let's go back to this. And here, we have the error. OK. So to solve this, we
can enter to the-- CRAIG LABENZ: Yeah. And it said operation permitted,
socket stuff, blah, blah, blah. So when you see that, you've
got to do what Gianfranco is doing now-- open up Xcode. Or are you going to do-- GIANFRANCO PAPA: OK. So let's quickly open. No, I'm going to open
the Xcode project. CRAIG LABENZ: I
thought you were going to show me the greatest trick
of all-- not opening Xcode. GIANFRANCO PAPA: Not this time. Right. CRAIG LABENZ: Not today. GIANFRANCO PAPA: So here, yeah. Here would be signing
and capabilities, outgoing connections. So we have to
restart everything, but this time it's
going to work. And yeah, that's
the whole example. Let's wait a little bit. That is loading the MacOS up. And the other thing I wanted
to show, as I was mentioning, is how we can start streaming
information from the server. So what we are going to
do is to actually stream a todo instead of getting
one todo and that's it. But let's first test this. OK. So this is working. We have our todo
with a random ID. So we can keep going, and that
will produce a random todo. Really basic, right? OK. So the next thing
will be to stream-- CRAIG LABENZ: So
before we move on-- I mean, streaming is
going to be very cool, but I want to just
recap kind of what we've done so far because we've been
at it for about 50 minutes now, but a lot of that's been
explanation, looking stuff up. You've written almost nothing. And we have completely
strongly typed communication with the server. On this very show,
on "The Boring Show," we've had shared code
going from the client to the server,
which, by the way, took more time than
this to get working. But even that was
not strongly typed. But here, the fact that-- the classes, of
course, are strongly typed if you're sharing code. But the network
requests themselves were never strongly
typed before. And that is such
a cool thing here. So if we change-- never before has it been
true that if you change how your server works, your
client will get type errors to remind you, oh, I
deleted that endpoint and I renamed it something else. That has never been
a thing before. So this is really, really neat. And it's super performant. And, like Gianfranco has said,
we're about to start streaming. My dog is making a
lot of noise here. Yeah. Really, really, really neat. Just so cool. GIANFRANCO PAPA: Yeah,
that's a really nice summary. And yeah, this was
really fast mainly because of the autogenerated
files that we have. We didn't have to create
our Todo class or service. These are also autogenerated. So that's really fast here. But yeah, to give a little bit
more about more cool use case, we're going to stream this todo. So we can go back to our--
maybe to our proto file again, where everything happens. So if we go to
our proto package, maybe what we can
do here is, instead of having a getTodo
that returns a todo, we are going to
return as streams-- or a stream of todos. So maybe we can rename-- we can create
another method that will be getTodoStream, maybe. And yeah, the actual
request would be the same. We can pass the-- well, it won't make sense
because, actually, we are not going to create a todo
and send it to our client. What we are going to do is
kind of have a stream of todo and send it and push
it to our client. So maybe we can create
a separate method that will be creating
our todos and sending to our client in streaming. CRAIG LABENZ: Yeah. We could also-- the stream
could just-- once a second, it could just add a
random todo to the stream. And then the frontend
could maybe store them all and show them in a
list view or something. GIANFRANCO PAPA:
Yeah, we can do that. So what we have to do here is
to provide a stream of todos, and that's it. That's really simple. We only need to modify
using this keyword. We can actually also
stream here from-- things from the client. Maybe we can find a
use case for that. But yeah, we can either
have a single call. We can have streaming from
the server, from the client without the server, or both. So that will depend
on your use case. But you have this
bidirectional communication that you can get an
advantage for that. So what we need to do
right now is, of course, to go to our command,
protoc command, again and autogenerate-- CRAIG LABENZ: Do you-- are you intending to
leave the "stream" keyword in the parameter on line 15? GIANFRANCO PAPA:
Oh, that's right. Yeah, maybe we can start simple
with only streaming things from the server. So now if we go and see about
I can generate the proto out, we can see that our server
has an error because we have to re-implement--
implement the other method. CRAIG LABENZ: Oh, I
love these errors. Hey, you forgot something. Just the best. GIANFRANCO PAPA: So maybe we
can create the missing override. And in this case, it is
the exact same method. We have our service call
and a GetTodoByIdRequest. But we have a stream of
todos instead of a Future. So here, what we will need
to do is to actually build a todo instead of returning. So what we can do is maybe-- what would be the
best way to do this? So maybe we can have
a todo controller. So here, we can use-- CRAIG LABENZ: We
could just have a loop that awaits a
duration of one second and then emits another one. Like, it could be while true. GIANFRANCO PAPA: So OK. So we have a while true again. Perfect. While true-- CRAIG LABENZ: Yeah, keep
it real simple here. GIANFRANCO PAPA: Yeah. And we can yield a todo. So maybe we can grab-- CRAIG LABENZ: Just copy. GIANFRANCO PAPA:
--everything from here. CRAIG LABENZ: Well,
maybe a random number. GIANFRANCO PAPA: Right. So maybe here-- CRAIG LABENZ:
Although it's funny. The request specifies a number. So our hypothetical is
getting a little fuzzy here, but bear with us. GIANFRANCO PAPA: So maybe
here we can, yeah, nextInt. CRAIG LABENZ: nextInt, yeah. GIANFRANCO PAPA: And 100. And yeah, we have our todo here. And we need to yield this todo. So actually, for doing
this, we have to-- CRAIG LABENZ: Async*. GIANFRANCO PAPA: --have
the async*, yeah. CRAIG LABENZ: And for anyone
who's not aware, async*-- first of all, there's a great
series on YouTube called-- nope, I forget the name. But Andrew Brogdon, one of the
original Flutter DevRel people, has a five-part
series on YouTube. I think the series is
called "Flutter in Focus." Anyway it's asynchrony and
Dart, goes through all this. But async* means that, A, your
function returns a stream, and every time you use
the "yield" keyword, the object that you yield
just goes on the stream. And so that's what
we're setting up here. GIANFRANCO PAPA: Perfect. So yeah, we forgot
about the delay. So if not, this is
going to provide a-- CRAIG LABENZ: Oh, it's fast. GIANFRANCO PAPA: Yeah. So let's delay it
a little bit this. Maybe a second would be
reasonable enough to notice. So maybe we can put it-- CRAIG LABENZ: Perfect, yeah. After the yield-- either
the beginning or the end, honestly, either one. GIANFRANCO PAPA: OK. Perfect. So here, we need to modify
our client, our Flutter app, so we can start receiving
this messages from the server once in a period of a second. So if we go back to our main
file, we had our todo here. And we will need to have
a stream of todo this time instead of a single one. And maybe what we can do is to
then create a stream of todos. So maybe-- CRAIG LABENZ: I
was going to say, I haven't used the
StreamBuilder widget in so long. Foresee it in our future. [LAUGHTER] GIANFRANCO PAPA: Yeah. That's right. I mean, I used to work a
lot with StreamBuilders before using the
Flutter Bloc library. But then it kind of was
pointless because Flutter Bloc kind of hides the
whole implementation. But yeah, back in the old days-- CRAIG LABENZ: It's
still a StreamBuilder. It's just well disguised. GIANFRANCO PAPA: Right. So yeah, back in the old days,
I used to deal with a lot of streams and [? abrupt ?]
[? starts. ?] So yeah, this was like remembering how
I used to work, in a sense. But yeah, the todo stream,
maybe we can put it as null also so we don't have to
initialize this here. And what we can do is to
initialize our todo stream. But let's put it private. And we, of course, have
to call the stub, right? So the method would
be getTodoStream. And our request would be the
same one that we use here. We can copy. And now, this idea
would be pointless, but we would just
have to put it there. And here, what we will
have is our stream of todo. So what we can do now is
to have a StreamBuilder. Instead of our Column, maybe we
can put here a StreamBuilder. And let's first
provide the stream. The stream will be todoStream. And then we have to
provide the builder, right? So for the builder, we need
the context and the snapshot. This is getting really long-- one line. But I think that's it. OK. So here in the
snapshot, what we can do is to actually see
if there is any data. And else, we can
print or show, maybe, "Loading," because
we will wait-- yeah, we have to cover that
case because nothing else is in the stream. So if this has data,
we can probably grab our todo from the stream. So let's do that. And we can display a
column with our todo. And you have to actually
return the column this time. And that's it. I don't know if we have
to actually parse this or we did already. CRAIG LABENZ: Yeah,
I'm not going to lie, I've not done this in a minute. So I'm not really sure either. Let's find out together. GIANFRANCO PAPA: Yeah. So here, we can remove
the null operators. That's cool. And here, I have an error. Let me see. Oh, I think I
didn't return this. CRAIG LABENZ: Oh, return. Yeah. GIANFRANCO PAPA: Perfect. CRAIG LABENZ: I think
this is going to work. I think this is right. GIANFRANCO PAPA: OK. Let's cross our fingers. So I'm reviewing a little bit. This getToDo one will call-- I mean, can be called, but
it will be the other method. And we are calling
directly the stream and using a StreamBuilder
to present the data. So if we go, we can-- Let's rerun our server
because we made some changes. CRAIG LABENZ: Good idea. GIANFRANCO PAPA:
And let's rerun-- oh, we could actually have
used the-- called restart. CRAIG LABENZ:
Probably restart it. Yeah. GIANFRANCO PAPA: Yeah. But that's my bad. CRAIG LABENZ: It's
called a cold restart. GIANFRANCO PAPA: Yeah. So here, yeah, if
everything works, we are going to see really
fast-- not really fast, like one second-- how random todos are
heading to our app. So apparently it worked. CRAIG LABENZ: Nice. Well done. Flawless, by both you and gRPC. Maybe the G stands
for Gianfranco. Have you thought about that? GIANFRANCO PAPA:
It could be, right? Yeah, that's
another coincidence. CRAIG LABENZ: Yeah. No, we'll have to check. We'll have to ask. Look at this. We are streaming
data from the server. This is so cool. Think about what you have to
do to get this functionality any other way. You have to definitely
use a WebSocket. And that's not the
end of the world, but, whoo, boy, was this easier. And like you said, it feels like
you call a native method that's just locally available. We're just dealing with
a stream on both ends. And then all of the guts to
serialize stuff and get it on the wire and manage
the connection-- never had to think about it. Never thought about it at all. And it was type safe
across the boundary, across the network
request boundary. Just really, really neat. Really cool. GIANFRANCO PAPA: Of course. OK. So yeah, that kind of wraps
up the whole implementation of the full-stack app, like with
our client, with our server, with our proto
package, everything. We can share this package
between client and server. We saw the implementation
of a method that is streaming information. Maybe what we can do now
is test the other streaming capabilities. We mentioned that you can
not only stream something from the server, but you can
also stream from the client. So you could potentially
start streaming things from the client to the server. I don't have a really
well-thought example of this, but it could be
something useful. So-- CRAIG LABENZ: No, I
think folks get it. Yeah. No, I think we get it. But if you did that,
on the frontend you'd just have a
stream, and you'd just add stuff to the
stream, and then it would show up on the backend? GIANFRANCO PAPA: Right, yeah. Yeah, totally. I mean, you will be basically
initializing your stream in your frontend. And then you will be streaming
information to the backend, gRPC, and then
something will happen. You can log everything, print
it, we can see it, or yeah. I mean, we can either
do that to test. Or another thing I was
also wanting to show is how this can work
in different platforms. Maybe we can open iOS
as well and play around. Maybe we can send messages
between both of them because, here, we are streaming
information to one client. But maybe we can
stream information to multiple clients
at the same time, and they all are going to
see the same todo instead of different ones. CRAIG LABENZ: Right, right. Well, I think that is
also a pretty good-- a good example. But I think we've covered
stuff quite nicely here. There are a few questions that
we should-- we could get to. Folks have asked a
couple of good ones. And I think that is probably
a nice way to finish. In terms of on iOS
and Android, it's going to look just like this,
you know, which is amazing. But there shouldn't be
like a big jump there. So we did get one
comment earlier. I kept referring to
the message as a class. And Tokhchukov says, "message is
more of a struct than a class, I think, as it doesn't
contain any logic." And I think that's a
nice way to put it. The message itself is
definitely a struct, but it is mapped one
to one to a class after you run the
protoc command. So the boundary between the
message and the class is-- it feels thin sometimes. But I think you
are technically-- that is a better way
to put it, for sure. Now, Gianfranco, I
think the next few here, I'd love to get
your takes on first, and then maybe I'll add
commentary at the end, whatever I'm-- whatever
thoughts I might have. But I think there's some
good questions here. So Tim asks, does
state management-- Bloc or Riverpod or
others-- work the same with gRPC on the
frontend and the backend? So I think this
question is like, how does using this impact
your state management? GIANFRANCO PAPA: Right. I mean, I know that
in our client app, we actually only present a
simple example where we play around with a stateful widget. But when you deal
with this, you have to actually have a state
management solution such as Bloc or Riverpod. And if you're following a good
structure or good architecture, that won't impact really,
because your bloc will be kind of calling
probably a repository, and that repository
will call ultimately gRPC by using what we saw
that is a client channel. So if you have
everything in place, you can still use Bloc
or Riverpod in Flutter, and that won't be-- I mean, the whole
point of bloc is just to uncouple your frontend-- your presentation layer from
whatever data source you have. So if you are using REST
or WebSockets or gRPC, that would be the same. So yeah, that
won't impact, yeah. CRAIG LABENZ: Nothing to add. Well stated. OK, next question. This one came up
a couple of times. Oh, actually, let's stick
with one from Tim here because it's a
little more related. Then we'll jump to
some other questions. Tim asks later on, "how
are your typical status codes returned in a traditional
REST API architecture?" So I interpret this
as saying, like, how do I differentiate from
a success, from an error? Normally, you can
check, is it a 200, is it a-- if I created something
and maybe expect a 202 or 204, or kind of however
you set it up-- if it's not there, it's a 404. How does that work
in this setting? GIANFRANCO PAPA: Right. Yeah, I interpreted the
same because you clearly have status codes in REST. And every one-- each of
them are really telling you if you're not authenticated,
or if you have, I don't know, different
errors such as 404. But in this case, I think
that you of course-- I mean, we didn't-- so error handling, there
is a separate documentation for that. But you can always
throw your exceptions because you will have
the same problems. Like, you will have to
authenticate the user. So you can-- if the
authentication is not working, you can throw a gRPC error,
and you can handle that into your client. Or you can send
custom exceptions. You can always do that. But I don't think that there
is a map between status codes to actually gRPC. But yeah, definitely do that. Have maybe-- we didn't
see this, but maybe we can have an interceptor
that is authenticating each call if something
is not working, I mean, you're
not authenticated, you just send a gRPC error. And then if the user
is authenticated, you can perform your logic
and throw custom exceptions. What it could happen is that
the channel connection is not working. So you have to handle that
in case you are, for example, streaming something. So you might present
something to the user. Actually, I remember-- we
didn't also cover this case, but remember, when we
opened the channel, we could have closed it whenever
the stateful widget is exposed. I mean, that wasn't
the case, but whenever we exposed the widget-- CRAIG LABENZ: You
would certainly need to do that, yeah. GIANFRANCO PAPA: Yeah. You will need to
close the channel so you don't have something
streaming unnecessary things. And yeah. CRAIG LABENZ: Now,
part of the way this works where Google
uses protobufs-- so like, for example, Firebase SDKs. All the communication
happens using protobufs. And instead of just
returning a todo, which was a perfectly reasonable
kind of shortcut for this demo, instead of returning
a todo, also if you look at the
documentation, you'll see something like
the whatever request and the whatever response. And so instead of just
returning that raw object, you can return
something that wraps it. And then you can have
an error code in there. The error code can be its
own enum, essentially, which is also a gRPC object. And then you-- GIANFRANCO PAPA: A proto buffer. CRAIG LABENZ: Yeah, exactly. Yeah. And then on your client you'd
check, did I get my .todo, or did I get my .error? And you wouldn't necessarily--
you could if you wanted, I guess. You could set 200
equal OK and 404 equal not found if you just kind
of wanted to keep that. But however you set up
your errors at that point. All right. Another question. This came up a couple times
about data migrations. As your data evolves over
time, you add new fields-- I think this was the first
time I saw it in the chat. Daniel says, "What's
the recommended way to handle API
changes, for example, adding new properties
to models or changing optional to required? Is there a way to
version these changes?" GIANFRANCO PAPA: Yeah, actually,
there's a way of versioning. In the documentation it says. But yeah, it's true that
whenever you make some changes, you will impact into
your clients that are using the same protobuf models. So I think you should see
what is the best strategy. This is always something
that is a problem, especially if you're
using, for example-- in REST, it's also a problem
because if you add something, you have to-- you will have your client that
is breaking because it doesn't have the last parameter. But in this case, as
everything is autogenerated, at least your client will
notice that change really soon, as we saw. Like, everything is typed. So yeah, I guess versioning
your protobuf would be a really good solution to implement. I think I'm not hearing you. CRAIG LABENZ: Yeah, sorry. I muted myself. GIANFRANCO PAPA: Were you-- CRAIG LABENZ: My
chair makes noise. Yeah. So yeah, Pascal
asked this question with another good angle. He said-- this gets
at an awkward point when you're always talking about
updating a distributed system that has to talk to itself. So how does this deal
with updates to the model? Will old apps be able to
talk to the updated server? So yeah, what about
people who don't click that Download New App
Version and they're running the old protobuf? I know some thoughts about
how this is handled at Google, but I'd love to first hear
how you handle this at Somnio. GIANFRANCO PAPA: Yeah. Because I mean, the thing
is that for me, something that we didn't mention
is that this kind of gRPC is also used in
microservices architecture. We saw an example of using
a client and a server, so really communicating the
frontend with the backend. But this is really how lots of
systems that uses microservices architecture-- it's really handy
because, as we saw, you can-- especially microservice
architectures where you can really choose
whatever language you need for your use case, that's
really handy because you will be sharing your
definition, and you can share it across multiple
programming languages. So yeah. In our case, we deal
mostly with Dart. So in our case, it's
really easy because when something changes
in the protobuf, maybe because of
the server change, you have to only update your
Flutter app and that's it. But I guess that
if you're having a microservice
architecture, you have to have that strategy well
placed because maybe not all your services need to update. Maybe they have to
continue working as they did without new
definitions or changes. So yeah. I think that there will
be a real problem mostly in your microservice
architecture, especially in the backend, but-- CRAIG LABENZ: Yeah. A way that this works at
Google is, essentially, required fields are swear words. And you're not allowed to add
required fields because you can't trust when clients will
be updated-- even a web client. Someone could just
have an open session and not refresh the page. And if the server changes
out underneath them, all future visitors
will be fine, but current viewers
could be totally hosed. So no required
fields are allowed. Now, that creates its
own thing where now you can't trust any of
your fields, even if you know this field
is actually required. I just pretend it's optional
for backwards compatibility. Do you really want
to carry this burden into perpetuity of, is
the required field there? Oh, weird, it was again. So you could
version it for sure. You could just add
a new endpoint that returns the response
v2 or something, and it returns the updated model
with the required field there, and then old clients
just keep calling v1. And once in your
logs you haven't seen v1 called in a month,
then you can finally delete it or something like that. But that's a tricky
thing, for sure. All right. There was another question,
this one a bit lighter. Whoo. Wojcieszekk? I don't think I was close
there, but I wish I was closer-- said slash asked, "I
thought of creating a chat in Flutter using gRPC. Is this a good idea?" What do you think? GIANFRANCO PAPA: Well,
I mean naturally, you will think about
that because you have a bidirectional support. So yeah, if you're dealing with
a chat, the most common way to have, for example, a frontend
communication with the backend is REST, right? And yeah, REST is not a
particularly good use case for a chat, because you
have to send information between your frontend
and your backend so that it can be actually
pushed to another client that you need to chat. So in this case, as we
can push information, this could be something that
you could actually implement with gRPC, why not, because-- well, we didn't see it. But if we started
another client, we can actually play
around a little bit and share the same streams so
we can push messages between one client and another one. It could be done. But yeah. Also, I guess that a more
subtle analogy for doing that would be also WebSockets. So you can also try
WebSockets to have that communication in a chat. CRAIG LABENZ: Yeah. I would say in a world
where gRPC didn't exist-- in a world-- there would be-- WebSockets would be essentially
the only kind of sane way to do things. Like, if you just tried
to use old-school HTTP requests and
pulling and whatnot, it's like, you'd have
the worst chat app ever. So WebSockets would
always rule the day. But I think gRPC
is essentially-- you get that kind of low-latency
immediacy of WebSockets. And it's just like-- it's not the same as WebSockets. The implementation is different. And where the 1's and the 0's
are on the wire is different. But that doesn't really
matter for most of us. I honestly just think
of gRPC as WebSockets where I got a lot of
code generated for me and it's type safe
across the boundary. I mean, it might literally
be on an actual WebSocket under the hood and then-- I don't really
know how it works. That's one of the great
things about gRPC. Like, you don't have to
know every single part of the stack all the way down. But it just-- it's
really, really, really efficient in a way
that we kind of all expect out of a chat app. So I think it's a
great choice for that. GIANFRANCO PAPA: Yeah. I guess that's
right, that it will be more efficient, probably,
in your mobile phones, that maybe-- like nowadays, you
could really have a really good mobile phone,
but there are other ones that-- other mobile phones that
are not so advanced. So you can push the
binaries instead of string-based
messages more faster. And that would be
a huge improvement in terms of efficiency
by only using gRPC. CRAIG LABENZ: Yeah. Absolutely. Well, Gianfranco, I
think we have gotten to the end of the questions. And the demo was perfect. Everything worked
first try, except when we knew it wasn't going to work
because of the entitlement. So bravo there. And, man, I had a great time. I hope you had a great time. I think our-- I think the viewers
had a great time. But this seems like
a good time to call. Any last thoughts, Gianfranco? GIANFRANCO PAPA: No. I mean, yeah, I
had a great time. I really enjoyed it. I was actually doing a live
demo, which was pretty cool. And yeah, any last thoughts-- I don't know. I guess that maybe if you
haven't tried this technology, I guess this is a really great
starting point for doing that. It really caught
my eye, especially because of the,
basically, sharing code between frontend and backend. As Flutter developers, we
always struggle with, OK, we have to deal with a
different programming language in another backend. So why can't we use Dart? And yeah, that's
why I keep exploring different alternatives for
doing everything full stack. And in terms of
gRPC, you can really use Dart or whatever
language you like. So that's really another
point on top of gRPC for the advantages we named. So yeah. And for me, it's really cool
to autogenerate these things. It works like magic. And you can share your proto
files, and that's awesome. So if you can try
it, give it a try because it's a cool
toolset you could have it. And as you said, Craig,
this is used by Google, so it will let you
know how they work. So yeah, I really
enjoyed to be here, and, yeah, hope to be here soon. CRAIG LABENZ: Awesome, yeah. Well, I think that's a
great place to leave it. So everybody, I think
I'm going to be off next week doing some prep for
Google I/O coming up in May. But we'll be back after that. So until the next episode,
have a good one, everybody. GIANFRANCO PAPA: OK, bye.