This episode builds a backend with Dart: getting to know the Shelf, Dart Frog, and Serverpod frameworks, using routing and middleware, handling requests and responses, JSON serialization and persistence, and deploying a Dart server application.

Dart's capabilities don't stop at mobile applications. With a fast runtime, a strong async model, and AOT compilation, Dart is a legitimate choice for building backends. Episode 10 takes you through building an HTTP server, handling routing, using middleware, serializing data, and preparing deployment.
We'll focus on Shelf — the official lightweight, modular framework — while also recognizing alternatives like Dart Frog and Serverpod. After this episode, you can write a real API and run it in production.
Shelf was built by the Dart team and is the foundation for many other frameworks. It treats handlers as simple functions that accept a Request and return a Response. Here's your first server:
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
void main() async {
var handler = const Pipeline()
.addMiddleware(logRequests())
.addHandler((Request req) {
return Response.ok('Halo dari Shelf');
});
var server = await io.serve(handler, '0.0.0.0', 8080);
print('Server berjalan di port ${server.port}');
}Pipeline() and addMiddleware(logRequests()) add logging middleware before the handler runs. io.serve binds the server to port 8080.
Besides Shelf, there are two popular frameworks:
Choose according to scale: Shelf for full control, Dart Frog for productivity, Serverpod for complete applications with a database.
For many endpoints, use shelf_router:
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf/shelf.dart';
final router = Router()
..get('/api/halo', (Request req) => Response.ok('Halo'))
..get('/api/users/<id>', (Request req, String id) {
return Response.ok('User $id');
});
void main() async {
var server = await io.serve(router, '0.0.0.0', 8080);
print('Server berjalan');
}router.get('/api/users/<id>', ...) captures the id parameter from the path. Routes are defined declaratively, so the endpoint list is easy to read.
Handlers can read the body, query parameters, and headers:
import 'package:shelf/shelf.dart';
Response halo(Request req) {
var nama = req.url.queryParameters['nama'] ?? 'tamu';
return Response.ok('Halo, $nama');
}req.url.queryParameters['nama'] gets the query parameter from the URL. For POST requests, read the JSON body with await req.readAsString() then parse it.
Data exchange between services commonly uses JSON:
import 'dart:convert';
class User {
final String id;
final String nama;
User(this.id, this.nama);
Map<String, dynamic> toJson() => {'id': id, 'nama': nama};
}
void main() {
var user = User('1', 'Arman');
var json = jsonEncode(user.toJson());
print(json);
}jsonEncode(user.toJson()) converts an object into a JSON string. dart:convert is the bridge between Dart objects and the format sent over the network.
To store data, connect to a database. The most common directions in Dart:
package:postgres, with a connection pool.package:mongo_dart.package:serverpod or package:angel3_orm for an abstraction layer.Start with a connection created once and reused — don't open a new connection per request.
The biggest benefit of a Dart backend: AOT compilation into a single self-contained binary:
dart compile exe bin/server.dart -o bin/serverdart compile exe bin/server.dart produces a native executable without the Dart runtime. This binary can be wrapped in a container:
FROM debian:bookworm-slim
WORKDIR /app
COPY bin/server /app/server
EXPOSE 8080
CMD ["/app/server"]The built container can be deployed anywhere: a VM, Kubernetes, or a serverless platform that supports containers.
Key takeaways:
Pipeline and middleware compose handlers.shelf_router handles path parameters and many endpoints.dart:convert with jsonEncode and toJson for JSON serialization.dart compile exe produces a self-contained binary ready to wrap in Docker.In the next episode 11, we'll cover web development with Dart — compilation to JavaScript with dart2js and build tools, interop with JavaScript, state management and client-side DOM manipulation, and frameworks like Flutter Web.