CodeBlanks Logo Icon CodeBlanks

Shelf API

Set up the REST API skeleton with Shelf.

The Shelf HTTP server

shelf is a lightweight HTTP server package for Dart. It is imported with import 'package:shelf/shelf.dart';. A Handler is a function type: it takes a Request and returns a Response. The shelf_router package adds route matching.

Writing a handler: Response taskHandler(Request req, TaskService svc) { return Response.ok('...'); }. Response.ok(body) returns a response with status 200. The handler takes a TaskService; so the service can be swapped. Shelf lets you build a REST API without a big framework; a handler chain is enough.

The taskHandler function

taskHandler is the function that receives an HTTP request and responds with the task list. It takes the incoming request as Request req and the service as TaskService svc. It finds the task count with svc.list().length.

Example: Response taskHandler(Request req, TaskService svc) { return Response.ok('tasks: ${svc.list().length}'); }. Here svc is injected from outside; the handler does not know which service it uses. This improves testability: a fake service in tests, a real one in production. With Shelf's Router the handler is bound to a route and the server is started.

Practice The taskHandler function

Add import 'package:shelf/shelf.dart'; and import 'package:shelf_router/shelf_router.dart'; at the top. Write Response taskHandler(Request req, TaskService svc) with return Response.ok('tasks: ${svc.list().length}'); inside. In main add final svc = InMemoryTaskService(); and print('Shelf: ${svc.list().length} task').

Starter code

main.dart
class Task {
  final int id;
  final String title;
  Task({required this.id, required this.title});
}

abstract class TaskService {
  List<Task> list();
}

class InMemoryTaskService implements TaskService {
  @override
  List<Task> list() => [Task(id: 1, title: 'task 1')];
}

// import the shelf and shelf_router packages
// write the taskHandler function (takes Request and TaskService, returns Response)

void main() {
  // print Shelf info
}

Sample solution

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

class Task {
  final int id;
  final String title;
  Task({required this.id, required this.title});
}

abstract class TaskService {
  List<Task> list();
}

class InMemoryTaskService implements TaskService {
  @override
  List<Task> list() => [Task(id: 1, title: 'task 1')];
}

Response taskHandler(Request req, TaskService svc) {
  return Response.ok('tasks: ${svc.list().length}');
}

void main() {
  final svc = InMemoryTaskService();
  print('Shelf: ${svc.list().length} task');
}

Expected output

Shelf: 1 task

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 The shelf package import

Question
import 'package:____/shelf.dart';
Answer
import 'package:shelf/shelf.dart';

Explanation: the shelf package is imported via package:shelf/shelf.dart.

2 What does Response.ok do?

  • Opens a database
  • Throws an error
  • Deletes a file
  • Returns a 200 response Correct answer

Explanation: Response.ok returns a response with status 200.

3 Match the Shelf parts

Term Meaning
shelf HTTP server
Response Response
Request Request
taskHandler Handler

All lessons in Advanced