Writing server side Dart code

Matias Meno
Dropzone
Published in
7 min readOct 1, 2021

…and: is it worth it?

We recently released Dropzone Plus — a service that handles HTML form uploads for you — and most of it is written in Dart. We recently published a post about our experience with building a Flutter Web App and quite a few people were interested in our server setup as well, so here you go!

What does server side Dart look like?

Server side dart code is very similar to node. The simplest code to actually start an HTTP server in dart is this:

Of course, no one would write a whole application like that.

The next step would be to use the shelf package (with shelf_router and shelf_static), which are packages maintained by the Dart team, and very similar to express in JavaScript world.

Shelf is a great solution, if you have a fairly small REST API. You’ll get very far with this approach.

The Dart team provides a nice example on how a server like this looks like.

But if you’re intending on writing something bigger and more complex, then what you’re looking for is a an actual API framework.

The gRPC framework

gRPC is a framework created by Google that allows you to describe an API with protocol buffers (more on them later) and generate server and client code from that for any language you want.

“RPC” stands for “Remote Procedure Call”. Simply put, it means that you invoke a function (procedure) as if it was a local function, but it get’s executed on your server and you don’t have to think about the communication details.

I have actually given a talk about this concept at Google I/O 2014:

Google I/O 2014, I’m wearing the WTF hoodie.

The implementation that I proposed back in 2014 took advantage of the fact that Dart can run on the server and client, while gRPC makes it easy to switch languages on both sides.

I don’t want to go into too much detail of how gRPC works, but here’s a short introduction:

You define your services (your API endpoints) and the messages you want to send between your client and your server in .proto files which look like this:

This language agnostic definition is then used to generate client libraries as well as a stub server implementation in any language you want.

Dart and gRPC

Fortunately, there is a Dart implementation of the gRPC framework, and it’s maintained by Google. After generating the client libraries, you communicate with the server like this:

Now let’s look at the server. As I said, the gRPC library creates abstract classes for you, that you simply extend, to implement the actual server logic. This is really simple to do. You just include the AccountServiceBase that has been generated, and extend it like this: class AccountService extends AccountServiceBase {}. You then get the warning, that this class is missing the necessary overrides (your method implementations).

By simply choosing the quick fix “Create missing override(s)” dart will generate the whole class for you:

All you need to do now, is to implement the individual methods to do what you want them to do, and then you can run the server with: Server([AccountService]).serve(port: 8080);

This is just a broad overview. For more concrete examples and help on gRPC implementation, please checkout the official documentation.

Ok, now that we’ve got the basic server code out of the way, let’s see how we can benefit from a shared language across server and client.

Shared code with our flutter apps

Depending on your application there’s going to be more or less code that you can share. In our case we mostly shared validation code (password strength, email validation, etc…), timezone logic (using the timezone dart package, we have simple utility classes that make sure the timezones have been loaded, and provide default values when it can’t be determined), and other utility functions like a Config class that is shared across all our projects.

We did setup our project like this:

/api <-- Our API server
pubspec.yaml
lib/
proto/ <-- All .proto files are here
etc...
/client <-- Our Flutter app
pubspec.yaml
lib/
etc...
/_shared <-- Code that is shared between both
pubspec.yaml
lib/

We then have a simple dependency in api/pubspec.yaml and client/pubspec.yamllike this:

dependencies:
_shared:
path: ../_shared

Note: In our case, because we might want to create multiple clients and because we have multiple dart servers (in a Kubernetes cluster), our setup is a bit more complex. Inside _shared/lib/ we have 3 folders: client, server, and generic. Things that can be used by both the server and the client are in generic, the other folders contain code that is only used by clients or servers.

If you’re interested in our Kubernetes setup as well, let me know!

Other benefits

Running dart for everything does also have a few minor other benefits.

The most obvious is, that it makes development a bit easier, because you don’t have to write in another language when switching context. When you want to implement a new feature in your client, you start by implementing the server, in Dart, and then continue with the client implementation, in Dart. It’s just a little less mentally draining to stay in the same language. It also makes it easier to reuse the same developers for all aspects of an app.

Then there’s the benefit of your development setup. You only need to install one language and tools for that language. Upgrading to a new version can be done in one go across all projects, and you don’t need keep on track of multiple languages over all your projects.

Then there’s Continuous Integration. We run all our tests, build the project, and publish new releases in GitHub Actions. For each service/app that we have in our project, we need to setup a CI pipeline that runs the tests, and builds a Docker container. When everything is one language, that just becomes a bit simpler. You just need to figure out the whole CI process once, and you’re good to go.

And then finally, there’s the AOT (Ahead Of Time) Dart compiler! Instead of creating a Docker container with node installed, and copying your files in that container and running it as usual, you compile your whole server app into a single binary, which is optimised and starts instantly, and ship it in a tiny Container.

Downsides of server side Dart

Let’s address the elephant in the room right away: the biggest downside is, that it’s unclear how much effort Google is willing to continue to put into server side Dart code. I think that as long as Flutter exists, they will not stop support for it, but depending on the adoption, it’s possible that they might stop development on the gRPC library (as they did with the RPC library they provided before).

That being said, the fact that they provided the gRPC library after discontinuing the RPC library is a sign that they want a server side dart framework, and gRPC is here to stay for a while, so in that regard I’m not too worried.

Third party libraries

Let’s face it… if your interfacing with a third party library, you’re probably on your own. Using mandrill or socketlabs to send emails? You’ve got to write a library for it (I’ve written both). Using Stripe as a payment gateway? You’ve got to implement the server library yourself. Etc…

Dart has great tooling for implementing APIs like that, and the json_serializable library is fantastic to work with. But depending on how many services you intend to use, this can get tiresome.

There is a good MongoDB library for dart, that we use with json_serializable but it’s mainly maintained by one developer which is a bit worrying.

Google itself does a good job at maintaining libraries for their services — including Firebase — , but I don’t want to be fully locked into their ecosystem.

Necessity of shared code

And of course, it depends on the size of your team. If you’re using a container orchestration solution like Kubernetes, you’re developing and deploying microservices anyway. So if you have separate teams, working on separate projects, then they don’t really care whether the same code is used.

This is especially true when using an API framework like gRPC that will generate the code for any language you want.

Would I recommend it?

If you’re considering using Dart on the server but after reading this article you still can’t quite decide yet, these are a few criteria that might help you get to a conclusion.

If stability is of the uttermost importance to you, then you’d probably not want to go with server side dart. In that case a framework like Java Spring is probably safest (just don’t ask me to implement it 🙂).

If you have dedicated teams working on server and client, and you’re hiring separately for either server or client, then the advantages of using Dart evaporate quickly. Just let each team choose the framework of their choice.

If however, you have a rather small team — or you’re on your own — and you just started with Flutter or simply like the Dart language, then, yeah, go for it! gRPC is going to be around for a while. Dart tooling is excellent and it makes fun to develop. If you’re using a library like gRPC, then migrating to another server language is also not that big of a task, since you can just generate the server stub for another language, and all the work that has gone into designing your database has also been done already. If you’re using a container orchestration solution like Kubernetes, then it’s also rather easy to switch out the API step by step.

--

--