Creating Backends with Dart

Hi! This article will be on how to build backends with the Dart programming language.

What is a backend?

A backend is defined as the part of an application that the user cannot see. It is necessary for many modern apps as it is frequently important for organizing, validating, and querying data, as well as much more.

A backend is frequently made up of three main parts:

  • a server

  • an app

  • a database

Servers

We usually refer to servers as the physical computer(s) on which the backend runs, but we may also use the term to refer to a program that handles incoming/out coming calls.

Backend apps

The app is responsible for the actual logic of our backend. It listens for requests, retrieves information from a database, parses it, and sends back a response

Databases

Databases could be seen as giant warehouses for data inside an application. We can perform various operations with them, the main ones being CRUD (Create, Read, Update, Delete). Create adds data to a database, read retrieves it, update is able to modify it, and delete naturally deletes it.

What do we need this for?

Knowing how to properly build backends can give tons of additional functionality to our apps, for example:

  • being able to connect users together

  • being able to store data on your users

  • being able to run code that your users’ devices would otherwise be too weak to run

  • and much, much more

What will we learn?

The most important concepts of backend development. How to send data from our application to a server, and subsequently how to receive it.

Another important part of backend development is knowing how to organize code in an efficient and organized manner, which is often easier said than done.


Fetching data from a server: learn by example

Consider the following scenario: You would like to be able to alert all of your users when a new version of your app comes out.

Initially, this may seem like a rather simple task, something along the lines of:

if (currentVersion < latestVersion) {
	//alert user
}

Getting the current version is trivial, with the package_info_plus package for instance, but how can our app know the latest version? This is where our backend comes in.

Contacting our backend

The first thing that we have to do, is asking our server what the latest version is, as we can directly access the device that the backend runs on to update it, but not our users’ devices. The connecting bridge between our application and the server is called an API (Application Programming Interface). Calling an API from our Flutter app is simple:

  • Note: for this example, we are using a REST API, the most common and simple type, which uses the HTTP protocol. There are other types available, but let’s not worry about them for now
//the first step is to import the http package
import 'package:http/http.dart'

//then whenever we want to check the version, we can call this:
var response = await get(Uri.parse('[URL]'));
//[URL] has to be replaced with a real URL, we will talk about that later

//response will be a Response type, and we can get the pure data by using its
//body property, ie:
int latestVersion = response.body;

Very basic apps in Flutter usually use the http package, however there are many alternatives to it, seeing as it is so basic. The most popular being dio. Here is its description on pub.dev:

Dio is a powerful Http client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout etc.

Here is how we do the same thing with Dio (I’m deciding to show this seeing as you will most likely encounter Dio more in the real world):

//the first step is to import the dio package
import 'package:dio/dio.dart';

//then whenever we want to check the version, we can call this:
var response = await Dio().get(Uri.parse('[URL]'));
//[URL] has to be replaced with a real URL, we will talk about that later

//response will be a Response type, and we can get the pure data by using its
//data property, ie:
int latestVersion = response.data;

See? it’s not that different, however using Dio we are able to access much more powerful functions, which you will certainly encounter later on in your coding journey.

Responding from the backend

The first step to creating a backend application in dart is to initialize a project, do this by running: dart create -t console-full <Project name>. The structure of a Dart console project is not at all dissimilar to a regular Flutter one, so you have pubspec.yaml for dependencies, analysis_options.yaml, and so forth. However, this time around not only do we have a lib directory but also a bin directory, the main difference being that elements in the lib directory are meant to be shared with other parts of the application, and maybe other applications in the future, while those in bin, logic wise, stay in bin. This is something that is much easier to understand in action.

We will also need some libraries to get things going. The most popular library for creating backends with Dart is the Shelf package. Here is its description on pub.dev:

Shelf makes it easy to create and compose web servers and parts of web
servers
. How?

Expose a small set of simple types.

Map server logic into a simple function: a single argument for the request,
the response is the return value.

Trivially mix and match synchronous and asynchronous processing.

Flexibility to return a simple string or a byte stream with the same model.

Lots of big words, but in reality what it does is rather simple, especially compared to a lot of traditional popular backend frameworks. We will see its features one by one in the following lessons. Go ahead and install it using dart pub add shelf.

Now we are ready to write some real code, go ahead and open bin/<Project name>.dart. The first step is to import our packages:

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

The shelf_io package is responsible for the Input/Output of our application.

The next step is to add a Pipeline and a Handler for our application. Think of it the Pipeline as a tunnel where information passes through. We can filter this information, and react to incoming data in different ways. Let me explain with a code example:

//inside the main() function
var handler = const Pipeline().addMiddleware(logRequests()).addHandler(_echoRequest); 

We are creating a Pipeline, with some Middleware, or an intermediate step between a request and a response. In this case, it adds a logger to our Pipeline. To the Middleware, we are adding a Handler, which has the job of calling a function (_echoRequest) on any received request coming through the Pipeline.

We also have to initialize a server, which is responsible for giving our handler a physical place to listen to.

//inside the main() function
var server = await shelf_io.serve(handler, 'localhost', 8080);

[localhost](http://localhost) is the address we are running the server at, and 8080 is the port. The address and port are where our application is accessible on our system.

Now to create a response to that request:

//outside of the main() function
Response _echoRequest(Request request) =>
    Response.ok('Latest Version: 1.201.2');

This is a function with a return

Response, and with a parameter type of Request. The type of response it is generating is a Response.ok, meaning that nothing is out of the norm, and we have everything running as expected. The response takes a string parameter, which in our case is the latest version of our application, ready to be used inside of Flutter. To try out our code, all we have to do is call dart run and navigate to our address and port defined earlier, in this case, http://localhost:8080. On our local machine we can simply open the address in a browser and get this:

Latest Version: 1.201.2

Success! Now we can also use this link in our Flutter app to get the latest version. In production, our backend would run on some publicly accessible server though, not localhost. There are many different ways of deploying Dart code, seeing as it has many different compilation options, some of which being more or less efficient or portable, but that’s beyond the scope of this article.

Takeaways

Backends are important for many apps, and they are made of a server, an app, and a backend. We can use them to receive external information in our Flutter apps.

We can make a backend application in Dart using the shelf package, which is made of Handlers that generate different responses depending on the requests sent. We can subsequently retrieve these responses using libraries like http or dio.