Shelf API
- Dart course
- Advanced
- Lesson 80/83
- 6 min
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
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
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
import 'package:____/shelf.dart';
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 |