Skip to content

Commit 0d8aff9

Browse files
Added REST API with full CRUD + tests
1 parent e3d9f47 commit 0d8aff9

6 files changed

Lines changed: 259 additions & 25 deletions

File tree

.coverage

52 KB
Binary file not shown.

README.md

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,45 @@
1-
# Flask Web App Tutorial
1+
# Flask Web App – REST API Enhancement
22

3-
## Setup & Installation
3+
## Project Overview
44

5-
Make sure you have the latest version of Python installed.
5+
This project is based on the Flask Web Application Tutorial by Tech With Tim.
6+
The original application is a web-based note-taking system with user authentication.
67

7-
```bash
8-
git clone <repo-url>
9-
```
8+
This project enhances the original application by adding a **REST API feature** that allows full CRUD operations on notes using JSON.
109

11-
```bash
12-
pip install -r requirements.txt
13-
```
10+
---
1411

15-
## Running The App
12+
## Original Application Features
1613

17-
```bash
18-
python main.py
19-
```
14+
- User registration and login system
15+
- Create, view, and delete notes through a web interface
16+
- SQLite database integration
17+
- Flask-Login authentication system
2018

21-
## Viewing The App
19+
---
2220

23-
Go to `http://127.0.0.1:5000`
21+
## Added Feature: REST API for Notes
2422

23+
A RESTful API was implemented to manage notes without using the frontend.
2524

26-
# 💻 Launch Your Software Development Career Today!
25+
### API Endpoints
2726

28-
🎓 **No degree? No problem!** My program equips you with everything you need to break into tech and land an entry-level software development role.
27+
| Method | Endpoint | Description |
28+
|--------|----------|-------------|
29+
| POST | /api/notes | Create a new note |
30+
| GET | /api/notes | Retrieve all notes |
31+
| GET | /api/notes/<id> | Retrieve a single note |
32+
| PUT | /api/notes/<id> | Update a note |
33+
| DELETE | /api/notes/<id> | Delete a note |
2934

30-
🚀 **Why Join?**
31-
- 💼 **$70k+ starting salary potential**
32-
- 🕐 **Self-paced:** Complete on your own time
33-
- 🤑 **Affordable:** Low risk compared to expensive bootcamps or degrees
34-
- 🎯 **45,000+ job openings** in the market
35+
---
3536

36-
👉 **[Start your journey today!](https://techwithtim.net/dev)**
37-
No experience needed—just your determination. Future-proof your career and unlock six-figure potential like many of our students have!
37+
## Request / Response Format
38+
39+
### Example: Create Note
40+
41+
**Request:**
42+
```json
43+
{
44+
"data": "My first API note"
45+
}

instance/database.db

16 KB
Binary file not shown.

website/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ def create_app():
1515

1616
from .views import views
1717
from .auth import auth
18+
from .api import api
1819

1920
app.register_blueprint(views, url_prefix='/')
2021
app.register_blueprint(auth, url_prefix='/')
22+
app.register_blueprint(api, url_prefix='/')
2123

2224
from .models import User, Note
2325

@@ -38,4 +40,4 @@ def load_user(id):
3840
def create_database(app):
3941
if not path.exists('website/' + DB_NAME):
4042
db.create_all(app=app)
41-
print('Created Database!')
43+
print('Created Database!')

website/api.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from flask import Blueprint, jsonify, request
2+
from .models import Note
3+
from . import db
4+
5+
api = Blueprint('api', __name__)
6+
7+
8+
# CREATE NOTE
9+
@api.route('/api/notes', methods=['POST'])
10+
def create_note():
11+
data = request.get_json()
12+
13+
if not data or 'data' not in data:
14+
return jsonify({
15+
"error": "Note content is required"
16+
}), 400
17+
18+
new_note = Note(
19+
data=data['data'],
20+
user_id=1
21+
)
22+
23+
db.session.add(new_note)
24+
db.session.commit()
25+
26+
return jsonify({
27+
"message": "Note created successfully",
28+
"id": new_note.id,
29+
"data": new_note.data
30+
}), 201
31+
32+
33+
# READ ALL NOTES
34+
@api.route('/api/notes', methods=['GET'])
35+
def get_notes():
36+
notes = Note.query.all()
37+
38+
output = []
39+
40+
for note in notes:
41+
output.append({
42+
"id": note.id,
43+
"data": note.data,
44+
"date": str(note.date)
45+
})
46+
47+
return jsonify(output), 200
48+
49+
50+
# READ SINGLENOTE
51+
@api.route('/api/notes/<int:id>', methods=['GET'])
52+
def get_note(id):
53+
note = Note.query.get(id)
54+
55+
if not note:
56+
return jsonify({
57+
"error": "Note not found"
58+
}), 404
59+
60+
return jsonify({
61+
"id": note.id,
62+
"data": note.data,
63+
"date": str(note.date)
64+
}), 200
65+
66+
67+
# UPDATE NOTE
68+
@api.route('/api/notes/<int:id>', methods=['PUT'])
69+
def update_note(id):
70+
note = Note.query.get(id)
71+
72+
if not note:
73+
return jsonify({
74+
"error": "Note not found"
75+
}), 404
76+
77+
data = request.get_json()
78+
79+
if not data or 'data' not in data:
80+
return jsonify({
81+
"error": "Missing note content"
82+
}), 400
83+
84+
note.data = data['data']
85+
db.session.commit()
86+
87+
return jsonify({
88+
"message": "Note updated successfully",
89+
"id": note.id,
90+
"data": note.data
91+
}), 200
92+
93+
# DELETE NOTE
94+
@api.route('/api/notes/<int:id>', methods=['DELETE'])
95+
def delete_note(id):
96+
note = Note.query.get(id)
97+
98+
if not note:
99+
return jsonify({
100+
"error": "Note not found"
101+
}), 404
102+
103+
db.session.delete(note)
104+
db.session.commit()
105+
106+
return jsonify({
107+
"message": "Note deleted successfully",
108+
"id": id
109+
}), 200

website/test_api.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import pytest
2+
from website import create_app, db
3+
from website.models import Note
4+
5+
6+
@pytest.fixture
7+
def app():
8+
app = create_app()
9+
app.config['TESTING'] = True
10+
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
11+
12+
with app.app_context():
13+
db.create_all()
14+
yield app
15+
db.drop_all()
16+
17+
18+
@pytest.fixture
19+
def client(app):
20+
return app.test_client()
21+
22+
23+
# -------------------------
24+
# CREATE
25+
# -------------------------
26+
def test_create_note(client):
27+
response = client.post('/api/notes', json={
28+
"data": "pytest note"
29+
})
30+
31+
assert response.status_code == 201
32+
33+
data = response.get_json()
34+
assert "id" in data
35+
assert data["data"] == "pytest note"
36+
37+
38+
# -------------------------
39+
# READ ALL
40+
# -------------------------
41+
def test_get_all_notes(client):
42+
client.post('/api/notes', json={"data": "note 1"})
43+
client.post('/api/notes', json={"data": "note 2"})
44+
45+
response = client.get('/api/notes')
46+
47+
assert response.status_code == 200
48+
49+
data = response.get_json()
50+
assert isinstance(data, list)
51+
assert len(data) == 2
52+
53+
54+
# -------------------------
55+
# READ SINGLE
56+
# -------------------------
57+
def test_get_single_note(client):
58+
create = client.post('/api/notes', json={"data": "single note"})
59+
note_id = create.get_json()["id"]
60+
61+
response = client.get(f'/api/notes/{note_id}')
62+
63+
assert response.status_code == 200
64+
65+
data = response.get_json()
66+
assert data["data"] == "single note"
67+
68+
69+
def test_get_single_note_not_found(client):
70+
response = client.get('/api/notes/999')
71+
72+
assert response.status_code == 404
73+
74+
75+
# -------------------------
76+
# UPDATE
77+
# -------------------------
78+
def test_update_note(client):
79+
create = client.post('/api/notes', json={"data": "old note"})
80+
note_id = create.get_json()["id"]
81+
82+
response = client.put(f'/api/notes/{note_id}', json={
83+
"data": "updated note"
84+
})
85+
86+
assert response.status_code == 200
87+
88+
data = response.get_json()
89+
assert data["data"] == "updated note"
90+
91+
92+
def test_update_note_not_found(client):
93+
response = client.put('/api/notes/999', json={
94+
"data": "nothing"
95+
})
96+
97+
assert response.status_code == 404
98+
99+
100+
# -------------------------
101+
# DELETE
102+
# -------------------------
103+
def test_delete_note(client):
104+
create = client.post('/api/notes', json={"data": "to delete"})
105+
note_id = create.get_json()["id"]
106+
107+
response = client.delete(f'/api/notes/{note_id}')
108+
109+
assert response.status_code == 200
110+
111+
112+
def test_delete_note_not_found(client):
113+
response = client.delete('/api/notes/999')
114+
115+
assert response.status_code == 404

0 commit comments

Comments
 (0)