Build a REST API with Python and Flask
Build a Task Manager REST API with full CRUD operations using Flask.
Introduction
30 Second Summary
Every time you open a weather app, scroll social media, or check your bank balance, your phone is talking to a REST API behind the scenes. These APIs are the invisible backbone of the internet.
In this project, you will build a Task Manager REST API from scratch using Python and Flask. You will create, read, update, and delete tasks through HTTP requests, learning the universal patterns that power every web application.
What You'll Build
A Task Manager REST API that exposes five endpoints for managing tasks, returns proper HTTP status codes, validates incoming data, and handles errors gracefully.
By the end of this project, you'll have:
- A complete Flask REST API running locally that performs full CRUD operations on tasks.
- Five working API endpoints tested from the terminal using curl, proving you understand the relationship between HTTP methods and CRUD actions.
- Proper HTTP status codes, input validation, and error handling that follow real-world conventions.
- Secret Mission: Add filtering and sorting to your task list endpoint.
Are there any prerequisites?
You should be comfortable with basic Python concepts like functions, dictionaries, and lists. No prior experience with web development or APIs is needed.
Set Up Your Project
Every app you use communicates with a server through an API. To build your own, you need a clean workspace with the right tools installed.
In this step, you'll create a dedicated project folder, set up a Python virtual environment to keep your dependencies isolated, and install Flask.
In this step, get ready to:
- Create a project folder and Python virtual environment.
- Install Flask.
- Create a minimal Flask app and verify it runs.
Create a project folder and virtual environment
A virtual environment is an isolated Python installation that keeps your project's packages separate from the rest of your system. This prevents version conflicts between different projects.
- Move to your Desktop by running this command in your terminal:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir task-manager-api
cd task-manager-api
- Confirm the folder exists by running:
ls ~/Desktop
You should see task-manager-api listed in the output.
- Create your virtual environment by running:
python3 -m venv venv
What does this command do?
- python3 -m venv tells Python to run its built-in virtual environment module.
- The second venv is the name of the folder where the environment files will live.
- Activate the virtual environment so your terminal uses it:
macOS
source venv/bin/activate
Windows
venv\Scripts\activate
Your terminal prompt should now show (venv) at the beginning of the line. This confirms the virtual environment is active.
Don't see (venv) in your prompt?
- Make sure you ran the activate command from inside the task-manager-api folder.
- On Windows, if you see an execution policy error, run Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned in PowerShell and try again.
- Help me activate my Python virtual environment.
Install Flask
Flask is a lightweight Python framework for building web applications and APIs. It handles routing, request parsing, and response formatting so you can focus on your application logic.
- Install Flask inside your virtual environment by running:
pip install Flask==3.1.3
- Verify the installation by running:
python3 -c "import flask; print(flask.__version__)"
You should see 3.1.3 printed in your terminal.
Seeing an error during install?
- Make sure (venv) appears in your prompt. If it does not, re-run the activate command from the previous substep.
- If you see a network timeout, check your internet connection and try again.
- Help me install Flask in my virtual environment.
Create your project file and verify the server runs
Now you'll create the single file that holds your entire API. This minimal version just proves your server can start and respond to requests.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- Click File in the top menu bar.
- Click Open Folder.
- Navigate to Desktop and select the task-manager-api folder, then click Open.
- In the Explorer sidebar, click the new file icon next to TASK-MANAGER-API.
- Name the file app.py and press Enter.
- In app.py, add the following code:
from flask import Flask
# Create the Flask application
app = Flask(__name__)
# Define a home route to confirm the server is running
@app.route("/")
def home():
return {"message": "Task Manager API is running"}
# Start the server when this file is run directly
if __name__ == "__main__":
app.run(debug=True)
What does this code do?
- from flask import Flask imports the Flask class from the package you just installed.
- app = Flask(__name__) creates your application instance. Flask uses __name__ to locate resources relative to your file.
- @app.route("/") maps the root URL to the home() function. When someone visits /, Flask calls this function and returns the dictionary as JSON.
- app.run(debug=True) starts the development server with auto-reload enabled, so it restarts automatically when you change code.
- Save app.py by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- Switch back to your terminal and start the server by running:
python3 app.py
You should see output indicating the server is running on http://127.0.0.1:5000.
- Open a new terminal tab by pressing Cmd+T (macOS) or Ctrl+Shift+T (Windows).
- Test your server by running:
curl http://127.0.0.1:5000/
You should see {"message":"Task Manager API is running"} printed in your terminal.
Not seeing the JSON response?
- Make sure the server is still running in your other terminal tab. You should see log output there, not a command prompt.
- Check that you saved app.py before running it. Unsaved changes won't take effect.
- If you see Address already in use, another process is using port 5000. Stop it with Ctrl+C in the terminal running the server, then try python3 app.py again.
- Help me get my Flask server running.
✔️ Awesome, I've got everything!
Great. Double check you've saved app.py and your server is still running.
ⓧ I'd like to double check the full code
Here is the complete app.py file so far:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return {"message": "Task Manager API is running"}
if __name__ == "__main__":
app.run(debug=True)
Your development environment is ready and your server is responding to requests. Next up, you'll add task data and build the endpoints that read it.
Build the GET Endpoints
Your Flask server is running and responding to requests. Now it's time to give your API real data and build the endpoints that serve it.
Every REST API needs a data source. For this project, you'll store tasks in a simple Python list. This keeps the focus on REST patterns without adding database complexity.
In this step, get ready to:
- Set up sample task data as an in-memory list.
- Build a GET endpoint that returns all tasks.
- Build a GET endpoint that returns a single task by ID.
Set up in-memory task data
Before building endpoints, you need data for them to return. You'll create a list of task dictionaries that acts as a simple database.
- In app.py, find this import line at the top of the file:
from flask import Flask
- Update it to also import request, so the line now looks like this:
from flask import Flask, request
Why import request now?
You won't use request in this step, but you'll need it in the next step when handling incoming data from POST and PUT requests. Adding it now saves you from editing the import line again later.
- Add the following task data below the app = Flask(__name__) line and above the @app.route("/") decorator:
# In-memory task storage
tasks = [
{
"id": 1,
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"done": False
},
{
"id": 2,
"title": "Walk the dog",
"description": "Evening walk in the park",
"done": False
}
]
next_id = 3
What does this code do?
The tasks list holds two sample task dictionaries. Each task has four fields: id (unique identifier), title (task name), description (details), and done (completion status).
The next_id variable tracks what ID to assign to the next task you create. It starts at 3 because the sample tasks already use 1 and 2.
- Save app.py.
You won't see a change yet. The next chunk creates the endpoint that serves this data.
Build the GET /tasks endpoint
The first endpoint returns all tasks as a JSON list. In REST, a GET request to a collection URL (like /tasks) returns every item in that collection.
- In app.py, add the following route below the home() function and above the if __name__ == "__main__": block:
@app.route("/tasks", methods=["GET"])
def get_tasks():
return tasks, 200
What does this code do?
The @app.route decorator maps the URL /tasks to the get_tasks() function. The methods=["GET"] argument tells Flask this route only responds to GET requests.
Returning tasks, 200 sends the list as a JSON response with a 200 status code. Flask automatically converts Python lists and dictionaries into JSON.
- Save app.py.
- Start your Flask server by running this command:
python3 app.py
- Open a second terminal window and test the endpoint by running:
curl http://127.0.0.1:5000/tasks
You should see a JSON array containing both sample tasks with their id, title, description, and done fields.
Seeing "Connection refused"?
Make sure your Flask server is still running in the other terminal window. If you see an error about the address already being in use, press Ctrl+C in the original terminal to stop the old process, then run python3 app.py again.
If you see a syntax error, check that your tasks list has matching brackets and commas after each dictionary.
Help me debug this
Build the GET /tasks/<id> endpoint
Retrieving all tasks is useful, but clients often need a specific task. A GET request to a URL like /tasks/1 should return only that one task.
- In app.py, add the following route below the get_tasks() function and above the if __name__ == "__main__": block:
@app.route("/tasks/<int:task_id>", methods=["GET"])
def get_task(task_id):
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
return task, 200
What does this code do?
The <int:task_id> in the URL rule captures a number from the URL and passes it to the function as the task_id parameter. Flask's int converter automatically validates that the value is a positive integer.
The next() function searches the tasks list for a matching ID. If no match is found, it returns None instead of raising an error.
If the task doesn't exist, the endpoint returns a JSON error message with a 404 status code. This tells the client that the requested resource was not found.
- Save app.py. Your server auto-reloads in debug mode.
- Test retrieving a task that exists by running:
curl http://127.0.0.1:5000/tasks/1
You should see the "Buy groceries" task returned as JSON.
- Test requesting a task that doesn't exist by running:
curl http://127.0.0.1:5000/tasks/999
You should see {"error":"Task not found"} returned with a 404 status.
Getting an unexpected response?
If you see Flask's default HTML error page instead of JSON, make sure your get_task() function is placed above the if __name__ == "__main__": block. Code below that block doesn't register as a route.
If curl http://127.0.0.1:5000/tasks/1 returns all tasks instead of one, double-check that the route uses /tasks/<int:task_id> and not just /tasks.
Help me debug my GET endpoint
✔️ Awesome, I've got everything!
Great. Double check you've saved app.py before moving on.
ⓧ I'd like to double check the full code
from flask import Flask, request
app = Flask(__name__)
# In-memory task storage
tasks = [
{
"id": 1,
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"done": False
},
{
"id": 2,
"title": "Walk the dog",
"description": "Evening walk in the park",
"done": False
}
]
next_id = 3
@app.route("/")
def home():
return {"message": "Task Manager API is running"}
@app.route("/tasks", methods=["GET"])
def get_tasks():
return tasks, 200
@app.route("/tasks/<int:task_id>", methods=["GET"])
def get_task(task_id):
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
return task, 200
if __name__ == "__main__":
app.run(debug=True)
Your API can now read data. Next up, you'll add the ability to create and delete tasks using POST and DELETE requests.
Create and Delete Tasks
Your API can read tasks, but it cannot create or remove them. A read-only API is like a notebook you can look at but never write in.
In this step, you will build two endpoints that let clients add new tasks and delete existing ones. These map to the POST and DELETE HTTP methods.
In this step, get ready to:
- Build a POST endpoint that creates new tasks with input validation.
- Build a DELETE endpoint that removes tasks by ID.
- Test both endpoints with curl from the terminal.
Build the POST /tasks endpoint
The POST method tells the server to create a new resource. Your endpoint needs to read the incoming JSON data, validate it, and add a new task to the list.
- In app.py, find the get_task function (the last route before if __name__ == "__main__":).
- Add the following code below the get_task function, above the if __name__ block:
@app.route("/tasks", methods=["POST"])
def create_task():
global next_id
data = request.get_json()
if not data or "title" not in data:
return {"error": "Title is required"}, 400
new_task = {
"id": next_id,
"title": data["title"],
"description": data.get("description", ""),
"done": False
}
tasks.append(new_task)
next_id += 1
return new_task, 201
What does this code do?
- global next_id lets the function modify the next_id variable defined outside it. Without this keyword, Python would treat it as a new local variable.
- request.get_json() parses the JSON body that the client sends with the request.
- The validation check ensures the client sent a title field. If not, the API returns a 400 (Bad Request) status code.
- data.get("description", "") uses an empty string as the default if no description was provided.
- The function returns the new task with a 201 (Created) status code, which tells the client the resource was successfully created.
- Save app.py. Flask's debug mode automatically restarts the server when you save.
- Test the endpoint by running this curl command in your terminal:
curl -X POST http://127.0.0.1:5000/tasks -H "Content-Type: application/json" -d '{"title": "Learn REST APIs", "description": "Build a project with Flask"}'
You should see a JSON response with your new task, an id of 3, and the title and description you sent.
- Now test the validation by sending a request without a title:
curl -X POST http://127.0.0.1:5000/tasks -H "Content-Type: application/json" -d '{"description": "No title here"}'
You should see {"error":"Title is required"} with a 400 status code. Your validation is working.
Getting a 405 Method Not Allowed?
- Make sure your route decorator says methods=["POST"]. Without this, Flask only accepts GET requests on the route.
- Check that the route path is /tasks (no trailing slash). A mismatch between your curl URL and route path causes this error.
- Confirm Flask restarted by checking your server terminal for a "Restarting with stat" message after you saved.
Help me debug my POST endpoint
Why 201 instead of 200?
HTTP status codes communicate what happened. 200 means "OK, here's your data." 201 means "OK, and I created something new for you." Using the right code helps clients know exactly what the server did without parsing the response body.
Build the DELETE /tasks/<id> endpoint
The DELETE method tells the server to remove a resource. Your endpoint needs to find the task by ID, remove it from the list, and return a confirmation message.
- In app.py, add the following code below the create_task function, above the if __name__ block:
@app.route("/tasks/<int:task_id>", methods=["DELETE"])
def delete_task(task_id):
global tasks
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
tasks = [t for t in tasks if t["id"] != task_id]
return {"message": f"Task {task_id} deleted successfully"}, 200
What does this code do?
- global tasks is needed here because the function reassigns the entire tasks list to a new filtered list (instead of just appending to it).
- The list comprehension [t for t in tasks if t["id"] != task_id] rebuilds the list without the deleted task.
- If no task matches the given ID, the endpoint returns a 404 error so the client knows the resource does not exist.
- Save app.py.
- Test the DELETE endpoint by running this command:
curl -X DELETE http://127.0.0.1:5000/tasks/1
You should see {"message":"Task 1 deleted successfully"} in the response.
- Confirm the task is gone by listing all tasks:
curl http://127.0.0.1:5000/tasks
You should only see task 2 ("Walk the dog") in the list. Task 1 is gone.
- Test the 404 case by trying to delete a task that does not exist:
curl -X DELETE http://127.0.0.1:5000/tasks/999
You should see {"error":"Task not found"} with a 404 status code.
Seeing an Internal Server Error?
- Check your server terminal for a traceback. The most common cause is a missing global tasks line at the top of the function.
- Make sure you have methods=["DELETE"] in the route decorator, not methods=["POST"].
Help me debug my DELETE endpoint
✔️ Awesome, I've got everything!
Great. Double check you have saved app.py and your server is still running.
ⓧ I'd like to double check the full code
Here is the complete app.py file at this point in the project:
from flask import Flask, request
app = Flask(__name__)
# In-memory task storage
tasks = [
{
"id": 1,
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"done": False
},
{
"id": 2,
"title": "Walk the dog",
"description": "Evening walk in the park",
"done": False
}
]
next_id = 3
@app.route("/")
def home():
return {"message": "Task Manager API is running"}
@app.route("/tasks", methods=["GET"])
def get_tasks():
return tasks, 200
@app.route("/tasks/<int:task_id>", methods=["GET"])
def get_task(task_id):
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
return task, 200
@app.route("/tasks", methods=["POST"])
def create_task():
global next_id
data = request.get_json()
if not data or "title" not in data:
return {"error": "Title is required"}, 400
new_task = {
"id": next_id,
"title": data["title"],
"description": data.get("description", ""),
"done": False
}
tasks.append(new_task)
next_id += 1
return new_task, 201
@app.route("/tasks/<int:task_id>", methods=["DELETE"])
def delete_task(task_id):
global tasks
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
tasks = [t for t in tasks if t["id"] != task_id]
return {"message": f"Task {task_id} deleted successfully"}, 200
if __name__ == "__main__":
app.run(debug=True)
Your API can now create and destroy tasks. Next up, you will add the ability to update existing tasks and polish your error handling so the API behaves consistently in every situation.
Update Tasks and Add Error Handling
Your API can already create, read, and delete tasks. The missing piece is the ability to update an existing task without deleting and recreating it.
In this step, you will build the PUT endpoint that modifies a task's fields in place. You will also add a global error handler so every 404 response from your API returns clean JSON instead of an HTML page.
In this step, get ready to:
- Build a PUT endpoint that updates existing tasks.
- Add a custom 404 error handler for consistent JSON responses.
- Test the complete CRUD cycle from creation to deletion.
Build the PUT /tasks/<id> endpoint
PUT is the HTTP method used when you want to replace or update an existing resource. Your endpoint will accept JSON data and update whichever fields the client sends, leaving the rest unchanged.
- In app.py, find the line that says @app.route("/tasks/<int:task_id>", methods=["DELETE"]).
- Add the following code above that DELETE route:
@app.route("/tasks/<int:task_id>", methods=["PUT"])
def update_task(task_id):
# Find the task by ID
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
# Update only the fields provided in the request
data = request.get_json()
task["title"] = data.get("title", task["title"])
task["description"] = data.get("description", task["description"])
task["done"] = data.get("done", task["done"])
return task, 200
What does this code do?
This route handles PUT requests to /tasks/<int:task_id>. It finds the task with the matching ID, then selectively updates its fields.
- data.get("title", task["title"]) uses the new value if one was sent, otherwise keeps the existing value. This lets you update just one field (like done) without touching the others.
- The response returns the full updated task with a 200 status code, confirming the change was applied.
- Save app.py.
- Test the PUT endpoint by running this command in your terminal:
curl -X PUT http://127.0.0.1:5000/tasks/2 -H "Content-Type: application/json" -d '{"done": true}'
You should see the task returned with "done": true while the title and description remain unchanged.
Seeing a 404 or 405 error?
- A 404 means the server restarted and the URL path does not match any route. Check that your @app.route decorator uses <int:task_id> (not <task_id>).
- A 405 (Method Not Allowed) means the route exists but does not accept PUT. Confirm methods=["PUT"] is in your decorator.
- Make sure you saved the file. Flask debug mode auto-reloads on save, but unsaved changes are not picked up.
Still stuck? Help me debug my PUT endpoint. I'm getting an error when I run the curl command.
Add a custom 404 error handler
Right now, if someone visits a URL that does not match any route (like /nothing), Flask returns an HTML error page. For an API, every response should be JSON so that client applications can parse it consistently.
- In app.py, scroll to the bottom of the file and find the line if __name__ == "__main__":.
- Add the following code above that line:
@app.errorhandler(404)
def not_found(error):
# Return JSON instead of Flask's default HTML error page
return {"error": "Resource not found"}, 404
What does this code do?
The @app.errorhandler(404) decorator registers a custom handler for all 404 errors across your entire application. Any time Flask would return a 404, this function runs instead and returns a clean JSON response.
This covers both your explicit return {"error": "Task not found"}, 404 statements and cases where someone visits a route that does not exist at all.
- Save app.py.
- Test the error handler by requesting a URL that does not exist:
curl http://127.0.0.1:5000/nothing
You should see {"error":"Resource not found"} as clean JSON instead of an HTML page.
Still seeing an HTML error page?
- Make sure @app.errorhandler(404) is placed above if __name__ == "__main__": but below all your route functions.
- Confirm the file is saved and the terminal shows Flask reloading.
Need more help? Why is my Flask error handler not returning JSON?
✔️ Awesome, I've got everything!
Great. Double check you have saved app.py and your terminal shows Flask has reloaded.
ⓧ I'd like to double check the full code
from flask import Flask, request
app = Flask(__name__)
# In-memory task storage
tasks = [
{
"id": 1,
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"done": False
},
{
"id": 2,
"title": "Walk the dog",
"description": "Evening walk in the park",
"done": False
}
]
next_id = 3
@app.route("/")
def home():
return {"message": "Task Manager API is running"}
@app.route("/tasks", methods=["GET"])
def get_tasks():
return tasks, 200
@app.route("/tasks/<int:task_id>", methods=["GET"])
def get_task(task_id):
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
return task, 200
@app.route("/tasks", methods=["POST"])
def create_task():
global next_id
data = request.get_json()
if not data or "title" not in data:
return {"error": "Title is required"}, 400
new_task = {
"id": next_id,
"title": data["title"],
"description": data.get("description", ""),
"done": False
}
tasks.append(new_task)
next_id += 1
return new_task, 201
@app.route("/tasks/<int:task_id>", methods=["PUT"])
def update_task(task_id):
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
data = request.get_json()
task["title"] = data.get("title", task["title"])
task["description"] = data.get("description", task["description"])
task["done"] = data.get("done", task["done"])
return task, 200
@app.route("/tasks/<int:task_id>", methods=["DELETE"])
def delete_task(task_id):
global tasks
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": "Task not found"}, 404
tasks = [t for t in tasks if t["id"] != task_id]
return {"message": f"Task {task_id} deleted successfully"}, 200
@app.errorhandler(404)
def not_found(error):
return {"error": "Resource not found"}, 404
if __name__ == "__main__":
app.run(debug=True)
Test the full CRUD cycle
You have now built all four CRUD operations. Time to run through a complete lifecycle to prove everything works together.
- Create a new task by running:
curl -X POST http://127.0.0.1:5000/tasks -H "Content-Type: application/json" -d '{"title": "Test CRUD cycle", "description": "Full lifecycle test"}'
You should see the new task returned with "id": 3 and a 201 status.
- Verify it exists by running:
curl http://127.0.0.1:5000/tasks/3
You should see the task you just created.
- Update the task by marking it as done:
curl -X PUT http://127.0.0.1:5000/tasks/3 -H "Content-Type: application/json" -d '{"done": true}'
You should see the task returned with "done": true.
- Verify the update stuck by fetching it again:
curl http://127.0.0.1:5000/tasks/3
Confirm "done": true is still present.
- Delete the task:
curl -X DELETE http://127.0.0.1:5000/tasks/3
You should see "Task 3 deleted successfully".
- Confirm it is gone:
curl http://127.0.0.1:5000/tasks/3
You should see {"error":"Task not found"} with a 404 status. The full lifecycle is proven.
What just happened?
You proved that your API handles the complete lifecycle of a resource. Create (POST), Read (GET), Update (PUT), and Delete (DELETE) all work together seamlessly.
This pattern is how every REST API in the world manages data. The same four operations power social media posts, shopping carts, user profiles, and everything in between.
Getting unexpected results during the cycle?
- If the task ID is not 3, your server likely restarted mid-test. Each restart resets the in-memory data. Start the cycle again from the POST command.
- If GET returns a task you already deleted, you may have a typo in delete_task. Compare your code to the full code tab above.
Need help? My CRUD cycle test is giving unexpected results. Help me debug.
Secret mission
Add Filtering and Sorting to Your Task List
Your API returns all tasks every time you call GET /tasks. What if you only want to see incomplete tasks, or sort them alphabetically? Add query parameters to make your endpoint smarter.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- The task-manager-api project folder on your Desktop.
- The venv virtual environment inside that folder (contains Flask and its dependencies).
Keep everything running
No action needed. Choose this if you're still actively building or plan to extend the API with more features.
- Your task-manager-api folder stays on your Desktop.
- Your virtual environment stays ready to activate with source venv/bin/activate whenever you return.
Pause - I'll come back to this later
Shut down the Flask server and deactivate the virtual environment to free up your terminal. Your files stay intact for next time.
- If your Flask server is still running, press Ctrl+C in the terminal to stop it.
- Deactivate the virtual environment by running:
deactivate
- The (venv) prefix disappears from your terminal prompt. Your project folder remains on the Desktop.
Delete - I don't want to use this again
Remove all project files and the virtual environment completely. This frees up disk space and leaves no trace of the project.
- If your Flask server is still running, press Ctrl+C in the terminal to stop it.
- Deactivate the virtual environment by running:
deactivate
- Navigate to your Desktop and delete the entire project folder by running:
cd ~/Desktop
rm -rf task-manager-api
- Confirm the folder is gone by running ls and checking that task-manager-api no longer appears.
That's a Wrap!
That's a Wrap!
Nice work! You just built a fully functional REST API from scratch using Python and Flask.
You've learned how to:
- Implemented all four CRUD operations (Create, Read, Update, Delete) mapped to HTTP methods (POST, GET, PUT, DELETE) with proper routing in Flask.
- Used proper HTTP status codes (200 OK, 201 Created, 400 Bad Request, 404 Not Found) along with input validation and error handling to make your API robust.
- Tested every endpoint using curl from the terminal, understanding the relationship between requests and JSON responses.
- Secret Mission: Added query parameter filtering and sorting to the GET endpoint.
Ready to quiz yourself?