Dart for your Backend : Part-1

Aswin Gopinathan
5 min readNov 25, 2022

--

When I talk about Dart, the first thing that comes to our mind is Flutter 💙. Well, that’s true right, most of us know Dart because of Flutter, to build amazing applications for Mobile, Web, and Desktop!

What if I say… That’s not the only thing you can do with Dart. Yes! as the title of this article goes, you can also use Dart for your Backend.

By Backend, I mean :
- Creating APIs
- Running scripts on the cloud
- Building discord or slack bots
And so on…

Soooo… EXCITED ??!

Characters from the Friends series celebrating

Let’s get started by creating a Dart application.

I prefer using the CLI for creating the template application. Use the following command to get started with a server-shelf application :

dart create -t server-shelf first_server_app

server-shelf: Template for creating a server shelf Dart application.
first_server_app: Name of the Dart application.

Once you run the above command, you will get a dart application template generated for you to get started with…

But Wait! What is shelf ?

Shelf is a Web-server Middleware that makes it easy to develop dart server applications. Its developed by the Dart team @ Google!

We will be using this package to build our API 💙

— -

Now, let’s check the template application that was created for us. The project structure looks like this :

Project directory structure of a Pure Dart application

Doesn’t this project structure look familiar? Flutter projects have somewhat similar structure right ??!

But there are few differences:

  • In Flutter, the main application dart files lie inside the lib folder, but in Pure Dart applications, the main dart files lie inside the bin folder.
  • The entry point of a Flutter application is the main.dartfile, where as in a Pure Dart application the entry point is the server.dartfile.

Now, let’s dive into the server.dart file :

import 'dart:io';

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

// Configure routes.
final _router = Router()
..get('/', _rootHandler)
..get('/echo/<message>', _echoHandler);

Response _rootHandler(Request req) {
return Response.ok('Hello, World!\n');
}

Response _echoHandler(Request request) {
final message = request.params['message'];
return Response.ok('$message\n');
}

void main(List<String> args) async {
// Use any available host or container IP (usually `0.0.0.0`).
final ip = InternetAddress.anyIPv4;

// Configure a pipeline that logs requests.
final handler = Pipeline().addMiddleware(logRequests()).addHandler(_router);

// For running in containers, we respect the PORT environment variable.
final port = int.parse(Platform.environment['PORT'] ?? '8080');
final server = await serve(handler, ip, port);
print('Server listening on port ${server.port}');
}

Let’s break down the entire code and understand what each of these lines do:

  1. The entry point of this code is the void main() function, which takes in a list of arguments. This argument list can contain the port number to which we would like to host the server application.
    This is very helpful when you are running your server scripts on some cloud platform and you can pass the port number through the command line directly!
  2. final ip = InternetAddress.anyIPv4;
    Now, we define the IP version we would like to use for this server application.
  3. final handler = Pipeline().addMiddleware(logRequests()).addHandler(_router);
    Now, we need to define a Pipeline to which we can add any number of Middlewares.
    A Pipeline is basically a medium through which we can send Requests and receive Response between the client and the server.
    A Middleware in this context, is a piece of code that adds an extra functionality to the Pipeline. It could be a Middleware to log the incoming requests, to perform authentication and authorisation, and so on…
  4. The addHandler(_router) method attaches a Router instance to act upon incoming requests and return appropriate response.
  5. The following code defines a Router instance that defines various handler functions to act upon the incoming Request based on the HTTP Methods (GET, POST, PUT, etc) :
// Configure routes.
final _router = Router()
..get('/', _rootHandler)
..get('/echo/<message>', _echoHandler);

Response _rootHandler(Request req) {
return Response.ok('Hello, World!\n');
}

Response _echoHandler(Request request) {
final message = request.params['message'];
return Response.ok('$message\n');
}

As i mentioned earlier, the server receives a Request and returns a Response.

The Request instance contains a lot of parameters to gather more information about the incoming Request :

GIF of the list of attributes that a Request instance contains

Similarly, the Response class as a lot of named constructors to define the type of Response (200 OK, 404 Not Found, etc) :

GIF of the list of named constructors of the Response class.

Now, let’s run this server application. We use the following command for running a pure dart application:

dart run bin/server.dart 

Once it runs successfully, open localhost:<port-number> (default port is 8080) to see the application!

Type /echo/<message> to see how _echoHandler(Request request) method handles its incoming message.

Now, let’s build our own handler with a different endpoint…

Add the following line of code to the Router instance:

final _router = Router()
..get('/', _rootHandler)
..get('/echo/<message>', _echoHandler)
// Add this following line.
..get('/echo/<num1>/<num2>', _ourHandler);

Now, let’s create a handler function _ourHandler :

Response _ourHandler(Request request) {
final num1 = int.parse(request.params['num1'] ?? '0');
final num2 = int.parse(request.params['num2'] ?? '0');
return Response.ok('The sum is ${num1 + num2}\n');
}

Save, and re-run the application.
Now, lets use the endpoint /echo/1/2 . Notice the output The Sum is 3 .

So…how does this work?

In the ..get() we defined the pattern for the endpoint as /echo/<num1>/<num2> . This will look for an endpoint with this pattern and assign the characters associated with the placeholder to the corresponding field in the params map.

ie, I used the endpoint /echo/1/2 , this assigned 1 to the key num1 and 2 to the key num2 which i was able to retrieve in the _ourHandler function.

Note: The application will throw an exception if you pass any non-digit characters in the above endpoint, as we haven’t added any checks on the value stored in the keys num1 and num2 .

— -

Well, this is basically how we create a basic server using Dart!

With that, we have come towards the end of this blog…but this is not it!

I have planned a lot of amazing content for the upcoming days, where i will help you build a full-fledged Dart server application!!

In my next blog, i will talk about how you can connect a local json file and use its data to build an API!

I am not perfect, i just try to give my best everyday!

If you find any mistake, please do reach out to me. Would love to hear your views on this article!

Feel free to connect with me on the following platform:

--

--

Aswin Gopinathan
Aswin Gopinathan

Written by Aswin Gopinathan

Software Engineer @PhonePe (iOS)

No responses yet