REST APIs & CRUD
Create, Read, Update & Delete with HTTP
Open interactive version (quiz + challenge)Real-world analogy
Imagine ordering food at a restaurant. You (the client app) look at the menu (API documentation), tell the waiter (HTTP) your order using specific words like 'I would like the pasta' (GET request). The kitchen (server) prepares it and the waiter brings back your plate (response) with a receipt number (status code). 200 means 'here is your food!' and 404 means 'sorry, we do not have that dish.'
What is it?
REST APIs are a standardized way for your Flutter app to communicate with a backend server over HTTP. You send requests using methods like GET, POST, PUT, DELETE to specific URLs, and the server responds with status codes and JSON data. Every modern app uses REST APIs to fetch data, authenticate users, and sync information.
Real-world relevance
Every app you use daily relies on REST APIs. When you open Instagram, a GET request fetches your feed. When you post a photo, a POST request uploads it. When you like something, a PATCH request updates it. The team_mvp_kit project communicates with its backend entirely through REST APIs for authentication, user data, and all business operations.
Key points
- What is a REST API? — REST stands for REpresentational State Transfer. It is a set of rules for how apps talk to servers over the internet using HTTP. Each URL represents a resource (like /users or /posts), and you use standard HTTP methods to interact with those resources. REST APIs are stateless -- each request contains all the info the server needs.
- HTTP Methods (Verbs) — GET retrieves data without changing anything. POST creates new data. PUT replaces existing data completely. PATCH updates part of existing data. DELETE removes data. These five verbs cover almost every API operation you will ever need.
- HTTP Status Codes — Every response includes a status code. 200 means OK. 201 means Created. 400 means Bad Request. 401 means Unauthorized. 403 means Forbidden. 404 means Not Found. 500 means Server Error. The first digit tells the category: 2xx success, 4xx client error, 5xx server error.
- Request and Response Structure — An HTTP request has a method, URL, headers (metadata like auth tokens and content type), and optionally a body (data you are sending). The response has a status code, headers, and a body (usually JSON data).
- JSON - The Language of APIs — JSON (JavaScript Object Notation) is the standard data format for REST APIs. It uses key-value pairs with strings, numbers, booleans, arrays, and nested objects. Both your app and the server understand JSON, making it the universal language for data exchange over HTTP.
- Headers and Authentication — Headers carry metadata about the request. Content-Type tells the server your body format. Authorization carries your login token. Accept tells the server what format you want back. The team_mvp_kit sends a Bearer token in the Authorization header for every authenticated request.
- Query and Path Parameters — Path parameters identify a specific resource: /users/42 where 42 is the user ID. Query parameters filter or modify results: /users?role=admin&page=2. Path params answer 'which one?' and query params answer 'how should I filter it?'
- The Request-Response Cycle — Your app sends a request, it travels through the internet to the server, the server processes it (checks auth, queries database, builds response), and sends back a response. This cycle typically takes 50-500ms. Show loading indicators during this time and handle errors gracefully.
Code example
import 'dart:convert';
import 'package:http/http.dart' as http;
// A complete REST API example in Dart
// 1. GET - Fetch a list of users
Future<List<User>> getUsers(String accessToken) async {
final response = await http.get(
Uri.parse('https://api.example.com/users'),
headers: {
'Authorization': 'Bearer $accessToken',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
return data.map((json) => User.fromJson(json)).toList();
}
throw Exception('Failed: ${response.statusCode}');
}
// 2. POST - Create a new user
Future<User> createUser(String name, String email) async {
final response = await http.post(
Uri.parse('https://api.example.com/users'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'name': name,
'email': email,
}),
);
if (response.statusCode == 201) {
return User.fromJson(jsonDecode(response.body));
}
throw Exception('Create failed: ${response.statusCode}');
}
// 3. DELETE - Remove a user
Future<void> deleteUser(int id, String accessToken) async {
final response = await http.delete(
Uri.parse('https://api.example.com/users/$id'),
headers: {'Authorization': 'Bearer $accessToken'},
);
if (response.statusCode != 204) {
throw Exception('Delete failed: ${response.statusCode}');
}
}Line-by-line walkthrough
- 1. Import dart:convert for JSON encoding and decoding
- 2. Import the http package for making HTTP requests
- 3.
- 4. Comment: A complete REST API example in Dart
- 5.
- 6. Comment: GET request to fetch users
- 7. Define an async function that returns a list of Users
- 8. Send a GET request to the /users endpoint
- 9. Passing the full URL to Uri.parse
- 10. Opening the headers map
- 11. Authorization header with Bearer token for authentication
- 12. Content-Type header telling the server we speak JSON
- 13. Closing the headers and the get() call
- 14.
- 15. Check if the response status code is 200 (success)
- 16. Decode the JSON body into a List of dynamic maps
- 17. Map each JSON object to a User using fromJson and return the list
- 18. Closing the success check
- 19. Throw an exception if the status code was not 200
- 20. Closing the getUsers function
- 21.
- 22. Comment: POST request to create a new user
- 23. Define an async function that takes name and email, returns a User
- 24. Send a POST request to the /users endpoint
- 25. Passing the URL
- 26. Content-Type header for JSON
- 27. Encode the name and email as a JSON body string
- 28. Closing the jsonEncode map
- 29. Closing the post() call
- 30.
- 31. Check for 201 Created status code
- 32. Parse and return the created User from the response
- 33. Closing the success check
- 34. Throw if creation failed
- 35. Closing the createUser function
- 36.
- 37. Comment: DELETE request to remove a user
- 38. Define an async function that takes a user ID
- 39. Send a DELETE request with the user ID in the URL path
- 40. Passing the URL with the ID as path parameter
- 41. Authorization header for delete permission
- 42. Closing the delete() call
- 43.
- 44. Check if status code is NOT 204 No Content
- 45. Throw if delete failed
- 46. Closing the deleteUser function
Spot the bug
Future<User> getUser(int id) async {
final response = await http.get(
'https://api.example.com/users/$id',
headers: {'Content-Type': 'application/json'},
);
return User.fromJson(jsonDecode(response.body));
}Need a hint?
Look at what http.get expects as its first parameter, and what happens if the request fails...
Show answer
Two bugs: 1) http.get expects a Uri object, not a raw String. Wrap it with Uri.parse(). 2) There is no status code check -- if the server returns 404 or 500, the code tries to parse an error response as a User and will crash. Add 'if (response.statusCode == 200)' before parsing.
Explain like I'm 5
Think of a REST API like a library. You go to the front desk (the server) and say 'Can I GET the book about dinosaurs?' (GET request). The librarian checks if you have a library card (authentication), finds the book (queries the database), and hands it to you with a little note saying 'Here you go, found it!' (200 OK response). If the book does not exist, the note says 'Sorry, not found' (404). If you want to donate a new book, you POST it to the library.
Fun fact
The term REST was coined by Roy Fielding in his PhD dissertation in 2000. He was one of the principal authors of the HTTP specification itself. So the guy who helped create HTTP also defined the best way to use it!
Hands-on challenge
Using Dart's http package, write a function that GETs a list of posts from https://jsonplaceholder.typicode.com/posts, parses the JSON response, and prints the title of each post. Handle the case where the status code is not 200.
More resources
- HTTP package for Dart (pub.dev)
- REST API Tutorial (restfulapi.net)
- HTTP Status Codes Reference (MDN Web Docs)