Dart backend - REST API by Example
By: Christian Muehle
Tags: How To, Backend, API, Dart, Rest

Photo by Kelvin Ang on Unsplash

Ever wondered if you can use your Dart code on your backend? Reusing code is a best practice, allowing the same code to run on multiple platforms will lead to reduced maintenance, time to market, and stress.

In this tutorial, we will build a web server with a REST API in Dart allowing a caller to get JSON data from your Server. Our Dart backend REST API will support the following features:

CORS configuration

Server-side sorting

Server-side paging

Hosted via Nginx (as a back proxy)

Preface

Dart SDK installed

Follow the steps shown below to get the Dart SDK installed. In case you have Flutter 1.21 or newer installed you don’t have to install the Dart SDK as it is included in Flutter.

Any IDE you like

I will use Visual Studio Code (working on Linux), any IDE will work as you just have to edit text (of course, if you want to debug you need something compatible with Dart). A list with possible options can be found here.

Get your project ready

To get you a head start, download the following project from my GitHub page, we will use it in this tutorial. You can either use the download button on GitHub or run the below command in a shell/command window (you will need to have Git installed):

Run the git clone command

How to create a dart project?

We will use the above-mentioned repository in this tutorial, but you might ask yourself: “How can I get a new project template?”

Like Flutter, Dart offers a command line interface to generate project templates:

dart create -t [PROJECT_TYPE] [PROJECT_NAME]

Help text for dart create

If you would like to create a Dart console app and name it helloWorld the command would be the following:

dart create -t console-full helloWorld

Dart.dev has some further details and step-by-step guides about this topic.

Structure of the project

Let’s check out the project you downloaded from GitHub, we will start with the files in the root folder of the project:

pubspec.yaml

Like Flutter, Dart uses .yaml files to track and configure dependencies and project details. The example projects have two dependencies: path and pedantic.

The first one is needed to read data from a file.

Pedantic is a development dependency, that means it’s used during the development process and is not needed to use the final application.

Supported by Pedantic you will get notified if your code violates any style guides or has other short comes, simple said it supports you to write clean code.

analysis_options.yaml

This file is used by Pedantic (the code analytic development dependency) to know what kind of rules you would like to apply. The file is configured to use the default rules. The configuration can be adjusted if needed.

LICENSE & README.md & .gitignore

GitHub/Git related files, not needed for our Dart backend project.

The bin folder

Inside the bin folder you will find the complete code of the application. The tutorial will cover the details below, quick overview:

serveTestData.dart — The main Dart file, including the code for the REST API

data.json — Our data storage, the data inside will be used to answer requests

company_contact.dart — the model class that will be used to read the JSON data

Let’s build a web-server

The first step for any Dart or Flutter application you check out from Git should be to restore the packages (the packages are not in the Git repository). Run the following command inside the root folder of the project:

dart pub get

Once you downloaded the dependencies, you can directly start the project, either using your IDE or via the following command:

dart run

If all is correct, you will see two prompts in the console/IDE output window:

The web server has started, found the data file and is waiting for requests

Now open a browser of your choice and open the URL shown in the output or copy the one below:

http://127.0.0.1:4044

Result of requesting data in a browser

Please note: The web server will start listing to requests only from your computer (localhost) on port 4044. If this port is already in use the application will print an error and exit.

The code

Great, the project is running! Now, let’s make sure to understand how it is working in detail. Open the serveTestData.dart file.

Inside the file you will find four sections (marked with comments):

Four sections inside the serveTestData.dart file

Section 1

In the beginning, I mentioned that we want to ship data from a JSON file. This section builds up the path to this file automatically when the Dart web server starts.

The path package from pub.dev enables you to interact with file paths and create matching ones for the operating system you are using. Check out line 5, that’s where I import the package and rename it to path.

Section 2

Our REST API needs to answer to the client — to accomplish this the class ResponseModel is used. The class always contains the amount of total rows as an integer and a list of CompanyContact objects.

CompanyContact is a custom class located in the company_contact.dart file and is used as DTO (data transfer object) in our Dart REST API.

Section 3

The REST API should support server-side sorting and paging, to-do, so it needs to know how to sort the data based on the request of a client.

To keep the code simple the REST API uses a numeric index to decide what property of the data should be sorted. Dart offers a handy function called compareTo that does the sorting for us. All decisions in the code are done by a single Dart switch case statement.

To make sure the REST API works also without a specific sorting the code uses the ID property as a fallback in case nothing was requested by the client.

To enable the client to decide if the data should be ascending or descending ordered at the end, the result of the compareTo needs to be multiplied by -1 to turn the default ascending method to descending.

To briefly explain the compareTo (from the Dart documentation):

compareTo(this,other) returns a negative integer if this is ordered before other , a positive integer if this is ordered after other , and zero if this and other are ordered together.

Section 4

Inside section 4 you will find the main functions, the start of the application. Dart backend projects will always start inside this function.

Get the data ready

One of the first things that will happen is that our web server will read the complete file with example data into its memory. The path that was built in Section 1 is now used. This happens only once during the startup, in case the file is missing an error will be printed and the application will stop working.

Bind the web server

The next step will configure and start the web server. The server gets connected to your local loopback IPV4 address (fancy for localhost or 127.0.0.1) on port 4044. This all happens with the call to HttpServer.bind in line 16. In case port 4044 is already in use (or anything else goes wrong) our Dart backend code will print an error and stop the application.

If all works out you will see the messages that the code is now listing for incoming requests.

Handle incoming requests

From this point on the web server is running and waiting for incoming connections. Thanks to the way how Streams work in Dart all you need to do is an async For loop to get new requests.

A new request was received

Once you open the URL (long version, if you send an HTTP GET request to the URL of our Dart backend REST API) the code will go into the for loop.

The first thing that happens is setting the contentType of the response to JSON. This is done to inform the client that we will talk JSON to him.

Followed by another HTTP header to set CORS rules, quick version this allows a browser to use the API even if it is hosted on a different URL (I covered this a bit in the following article):

After all HTTP Headers are in place the code will check the request query parameters. Our backend server supports four parameters:

offset — the amount of data entries that should be skipped

— the amount of data entries that should be skipped pageSize — the number of elements the client would like to get back

— the number of elements the client would like to get back sortIndex — the numerical index of the column to sort the data (see Section 2)

— the numerical index of the column to sort the data (see Section 2) sortAsc — The order to sort the data (see Section 2)

All the above query parameters are optional, in case they are missing a default value is used. In case a request contains an incorrect value for one of the parameters, for example text instead of a number, the request will fail with a 500 server error. Of course, this part could be improved with a proper error messages and the right HTTP code 400 to inform the caller he made an error.

Get the data to the client

Once the request parameters are checked it’s time to sort the data before we apply the offset and page size. It is required to do this as the first step to make sure we get the right data, If you sort the data by name and then take the first 50 rows you will get a different result as if you sort the data by phone numbers.

Once the data is sorted, the code “writes” the response to the client.

Thanks to the build in skip and take methods of List the paging is done quickly. The last step is to wrap the response in our DTO (Section 2) and encode it to JSON with jsonEncode. Dart has a built-in to JSON conversion, JSON data is handled as a Map<String,dynamic>.

The last line of our for loop is closing the response and finally sending the data to the client.

Complete section 4 code

Live example

The complete code is running online, give it a try and checkout a few examples:

https://example.devowl.de

This will give you the first 10 rows sorted ascending by the ID property.

https://example.devowl.de/?sortIndex=2&sortAsc=0&offset=100&pageSize=50

Running the above request will sort the data by the CompanyName property in descending order. The first 100 records will be skipped, and 50 records will be returned.

If you try the URL below, you will get an HTTP 500 error, the value “foobar” is not a valid number for the pageSize:

https://example.devowl.de/?sortIndex=2&sortAsc=0&offset=100&pageSize=foobar

To see the system in action used as a backend of an application, check out my post about server side data tables in Flutter:

Or head directly to the running Flutter Web application:

What’s next?

Play around with the code, you could exchange the state file backend and replace it with SQLite, checkout Moor if you want to go this way.

Another change to the code could be the error handling inside the API. The best practice for web API’s would be to return proper error codes to the client.

Sorting and paging is nice but what about server side filters? Change the code to only return company’s that start with an A or phone numbers from Germany (+49).

Hope I could showcase you how to use Dart on the backend and create a simple REST API. Feel free to comment and clap ☺