TODO API Example#
A complete REST API for managing TODO items, demonstrating CRUD operations, validation, and error handling.
Overview#
This example shows:
- ✅ RESTful API design
- ✅ In-memory data storage
- ✅ Request validation
- ✅ Error handling
- ✅ CORS support
Complete Code#
import 'package:dart_express/dart_express.dart';
class Todo {
final String id;
String title;
bool completed;
DateTime createdAt;
Todo({
required this.id,
required this.title,
this.completed = false,
DateTime? createdAt,
}) : createdAt = createdAt ?? DateTime.now();
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'completed': completed,
'createdAt': createdAt.toIso8601String(),
};
}
void main() async {
final app = DartExpress();
// In-memory storage
final todos = <String, Todo>{};
var nextId = 1;
// CORS
app.use(app.cors());
// GET /todos - List all todos
app.get('/todos', (req, res) {
res.json({
'todos': todos.values.map((t) => t.toJson()).toList(),
'count': todos.length,
});
});
// GET /todos/:id - Get single todo
app.get('/todos/:id', (req, res) {
final todo = todos[req.params['id']];
if (todo == null) {
return res.status(404).json({'error': 'Todo not found'});
}
res.json(todo.toJson());
});
// POST /todos - Create todo
app.post('/todos', (req, res) async {
final body = await req.body;
// Validation
if (body['title'] == null || body['title'].toString().trim().isEmpty) {
return res.status(400).json({'error': 'Title is required'});
}
// Create todo
final id = (nextId++).toString();
final todo = Todo(
id: id,
title: body['title'].toString().trim(),
);
todos[id] = todo;
res.status(201).json(todo.toJson());
});
// PUT /todos/:id - Update todo
app.put('/todos/:id', (req, res) async {
final todo = todos[req.params['id']];
if (todo == null) {
return res.status(404).json({'error': 'Todo not found'});
}
final body = await req.body;
// Update fields
if (body['title'] != null) {
todo.title = body['title'].toString().trim();
}
if (body['completed'] != null) {
todo.completed = body['completed'] as bool;
}
res.json(todo.toJson());
});
// DELETE /todos/:id - Delete todo
app.delete('/todos/:id', (req, res) {
final todo = todos.remove(req.params['id']);
if (todo == null) {
return res.status(404).json({'error': 'Todo not found'});
}
res.status(204).send();
});
await app.listen(3000);
print('🚀 TODO API running on http://localhost:3000');
}
API Endpoints#
List Todos#
GET /todos
Response:
{
"todos": [
{
"id": "1",
"title": "Buy groceries",
"completed": false,
"createdAt": "2024-01-01T10:00:00.000Z"
}
],
"count": 1
}
Get Todo#
GET /todos/:id
Response:
{
"id": "1",
"title": "Buy groceries",
"completed": false,
"createdAt": "2024-01-01T10:00:00.000Z"
}
Create Todo#
POST /todos
Content-Type: application/json
{
"title": "Buy groceries"
}
Response (201):
{
"id": "1",
"title": "Buy groceries",
"completed": false,
"createdAt": "2024-01-01T10:00:00.000Z"
}
Update Todo#
PUT /todos/:id
Content-Type: application/json
{
"completed": true
}
Response:
{
"id": "1",
"title": "Buy groceries",
"completed": true,
"createdAt": "2024-01-01T10:00:00.000Z"
}
Delete Todo#
DELETE /todos/:id
Response: 204 No Content
Testing with cURL#
# Create a todo
curl -X POST http://localhost:3000/todos \
-H "Content-Type: application/json" \
-d '{"title":"Buy milk"}'
# List all todos
curl http://localhost:3000/todos
# Get specific todo
curl http://localhost:3000/todos/1
# Update todo
curl -X PUT http://localhost:3000/todos/1 \
-H "Content-Type: application/json" \
-d '{"completed":true}'
# Delete todo
curl -X DELETE http://localhost:3000/todos/1
Extensions#
Add Filtering#
app.get('/todos', (req, res) {
var filtered = todos.values;
// Filter by completion status
final completed = req.query['completed'];
if (completed != null) {
final isCompleted = completed == 'true';
filtered = filtered.where((t) => t.completed == isCompleted);
}
res.json({
'todos': filtered.map((t) => t.toJson()).toList(),
});
});
Add Sorting#
app.get('/todos', (req, res) {
var list = todos.values.toList();
// Sort by creation date
list.sort((a, b) => b.createdAt.compareTo(a.createdAt));
res.json({'todos': list.map((t) => t.toJson()).toList()});
});
Add Pagination#
app.get('/todos', (req, res) {
final page = int.tryParse(req.query['page'] ?? '1') ?? 1;
final limit = int.tryParse(req.query['limit'] ?? '10') ?? 10;
final list = todos.values.toList();
final start = (page - 1) * limit;
final end = start + limit;
res.json({
'todos': list.skip(start).take(limit).map((t) => t.toJson()).toList(),
'page': page,
'totalPages': (list.length / limit).ceil(),
'total': list.length,
});
});
Running the Example#
The full example is available at:
/apps/dart_express_examples/todo_api_example
Run it:
cd apps/dart_express_examples/todo_api_example
dart run bin/server.dart