Dart and Nginx

I’ve been enjoying Flutter a lot. I’ve written a couple of apps, and I’m getting a good handle on the dart language. Now, I’d like to use dart for the whole system rather than having half of my code in PHP and the other half in Dart/Flutter.

I found the solution in Dart Shelf. The code is hosted at GitHub, and it can easily be installed as any other Dart package.

The best use case is that I can have a shared package between the Flutter front-end and the Dart shelf back-end. So, I can create, for example, a Transaction class that represents a transaction in my system. I can give the class toJson() and fromJson() methods that will convert between a Json-formatted string. Then, I can easily send it from the Flutter front-end to the back-end and have the same code running on both sides. I only maintain it in one place.

So, I learned how to make all that work pretty quickly. The challenge is how do I push that out onto a server already setup as a website. The server is using Nginx, and I don’t want to replace all the other stuff that Nginx is already doing.

So, here’s that experiment. I setup a virtual machine Ubuntu server to do my test. Note that you will want to configure the networking on the machine with either a bridged setup or a second card that is host only. That way you can access the IP address from your host machine. The first step is to install Nginx on it. That’s an easy apt command:

sudo apt install nginx

At this point, I can visit the IP address of the virtual machine in a browser. It will bring up the default Nginx web page.

Install Dart

Now, we need to install dart on the server. Actually, you’ll see later that we can skip this step, but I’m going to take the approach that you don’t have dart anywhere else. So, we need Google’s repository first.

sudo apt install apt-transport-https
sudo sh -c 'wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
sudo sh -c 'wget -qO- https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
sudo apt update

With the repository installed, we can just issue a simple dart command.

sudo apt install dart

At this point, you can develop dart programs on this server. As I said earlier, you probably would want to develop them on a separate computer, but this just an exercise to see if this works.

Creating a Shelf Server

So, let’s start a simple Shelf Server project. We’ll put it under the www directory in /var/www/dart and we need to make sure that our web server user has access to it. The “dart create” command builds the basic project in that directory.

sudo mkdir /var/www/dart
cd /var/www/dart
sudo chmod 777 .
dart create test
cd test/
dart run

You should have a simple “Hello world” example at this point. “Dart run” executes the program you just created, and your output should look like this:

Building package executable... 
Built test:test.
Hello world!

Now, we need to add the Shelf package so that we can turn this into a web server. The “dart pub add” command will go out, download, and install the library automatically.

dart pub add shelf

Next, I created a simple shelf program. I am a vi editor fan, so that’s what I used. Feel free to use any editor you like such as Nano.

vi bin/test.dart

Here’s my code in the test.dart file. It basically just listens for requests on my IP address (192.168.56.102). I have assigned it port 8080. Note that I can’t use port 80 because Nginx is already using that. The _echoRequests() handler simply prints out whatever request it was just sent.

import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

void main() async {
  var handler =
      const Pipeline().addMiddleware(logRequests()).addHandler(_echoRequest);

  var server = await shelf_io.serve(handler, '192.168.56.102', 8080);

  // Enable content compression
  server.autoCompress = true;

  print('Serving at http://${server.address.host}:${server.port}');
}

Response _echoRequest(Request request) =>
    Response.ok('Request for "${request.url}"');

To run it, I just ran `dart run` on the server, and then, when I visit: http://192.168.56.102:8080/testing I get this response in the browser:

Request for "testing"

Compiling

I learned that I can compile to a self-contained executable. That speeds up the processing because it doesn’t have to compile as it starts. These two commands compile and then run the server (even though it says exe, it works on Linux):

dart compile exe bin/test.dart 
bin/text.exe

So, after running this, I can visit that same Url and get the same result in the browser. I know it looks like a Windows program with the “exe” extension, but it works fine on Linux. Note, that this “exe” file doesn’t require dart to be installed on the computer. So, we’ll come back to that later.

Setting up Nginx

Now, we need to make Nginx and Dart work together. I don’t want my flutter app to have to use a different port than the rest of the website. Also, Nginx can have an SSL certificate, and I don’t want to have to figure out how that fit that into my Shelf server.

I edited /etc/nginx/sites-available/default. I added the following after the existing location section:

        location /dart {
                proxy_pass http://192.168.56.102:8080;
        }

This tells Nginx to pass everything sent to the /dart Url through to the port that the Dart Shelf Server is running on. To make sure I did it right, I checked the config:

sudo nginx -t

Now, to get Nginx to use that new configuration, I told it to reload.

sudo service nginx reload

After that, this URL loads the dart service through nginx: http://192.168.56.102/dart/testing. It returns:

Request for "dart/testing"

Now, we are triggering our Dart code directly through Nginx. It works! Now, we need to make it a little more permanent.

What if we reboot? We need the Dart server to automatically start running. We’ll setup the dart program as a service. So, I create the file /etc/systemd/system/dartrest.service

[Unit]
Description=Dart Rest Service

[Service]
ExecStart=/var/www/dart/test/bin/test.exe

[Install]
WantedBy=multi-user.target

First, I need to install this new service. This will make it install the service.

sudo systemctl daemon-reload

To start the service, I just issue the start command:

sudo service dartrest start

To make it start on reboot:

sudo systemctl enable dartrest.service

At this point, we have a fully functioning Dart Web Service running through Nginx. I would call that a success.

Without Installing Dart

Now, in a real world, I would rather not have Dart installed on the server. I’ll build the app on my laptop and just copy the exe out to the server. So, I wiped out my first machine, and ran through all of the same steps except for the part about installing Dart. I built test.exe on my laptop and copied it into the /var/www/dart directory on the new server.

Everything worked exactly the same. Dart is not required to be installed on the server. We just need the compiled web service.

One more thing though. I don’t want it running on the public IP address. I changed the IP address to localhost both in the dart code and on Nginx. So, here’s that line in my dart program:

  var server = await shelf_io.serve(handler, '127.0.0.1', 8080);

Then, in the nginx config, I changed it to go to localhost:

        location /dart {
                proxy_pass http://localhost:8080;
        }

Resources

Leave a Comment

Your email address will not be published. Required fields are marked *