├── README.md └── app.py /README.md: -------------------------------------------------------------------------------- 1 | # Blog API with Flask & SQLAlchemy 2 | 3 | This project is a simple backend API for managing blog posts built using Python's Flask framework and SQLAlchemy. It provides CRUD (Create, Read, Update, Delete) operations for blog posts and can serve as a starting point for more advanced blog or content management systems. 4 | 5 | --- 6 | 7 | ## Table of Contents 8 | 9 | 1. [Overview](#overview) 10 | 2. [Features](#features) 11 | 3. [Architecture](#architecture) 12 | 4. [Tech Stack](#tech-stack) 13 | 5. [Installation & Setup](#installation--setup) 14 | 6. [Usage](#usage) 15 | - [API Endpoints](#api-endpoints) 16 | 7. [Testing](#testing) 17 | 8. [Deployment](#deployment) 18 | 9. [Future Enhancements](#future-enhancements) 19 | 10. [Troubleshooting & FAQ](#troubleshooting--faq) 20 | 11. [Contributing](#contributing) 21 | 12. [License](#license) 22 | 23 | --- 24 | 25 | ## Overview 26 | 27 | The Blog API is a RESTful service that allows clients to manage blog posts. It supports the following operations: 28 | - Retrieve all blog posts 29 | - Retrieve a single post by ID 30 | - Create a new post 31 | - Update an existing post 32 | - Delete a post 33 | 34 | The API is built using Flask for the web framework and SQLAlchemy for ORM (Object Relational Mapping) with a SQLite database for data persistence. 35 | 36 | --- 37 | 38 | ## Features 39 | 40 | - **CRUD Operations:** Full support for creating, reading, updating, and deleting blog posts. 41 | - **RESTful Design:** Standardized endpoints and HTTP methods for API operations. 42 | - **Lightweight & Simple:** Minimal dependencies, making it easy to set up and extend. 43 | - **Data Serialization:** Returns JSON responses for easy consumption by clients (web or mobile). 44 | - **Auto-updating Timestamps:** Automatically records when posts are created and updated. 45 | 46 | --- 47 | 48 | ## Architecture 49 | 50 | ### System Overview 51 | 52 | - **Flask App:** Handles incoming HTTP requests and routes them to appropriate handlers. 53 | - **SQLAlchemy ORM:** Maps Python objects to database records, simplifying database interactions. 54 | - **SQLite Database:** Stores blog posts; ideal for development and testing. 55 | 56 | ### Component Breakdown 57 | 58 | 1. **API Endpoints:** 59 | The application defines endpoints for: 60 | - Listing all posts 61 | - Getting a single post 62 | - Creating a new post 63 | - Updating a post 64 | - Deleting a post 65 | 66 | 2. **Model Definition:** 67 | The `Post` model defines the structure of a blog post, including fields for title, content, and timestamps for creation and updates. 68 | 69 | 3. **Data Serialization:** 70 | Each post is converted to a dictionary format via the `to_dict()` method before being sent as JSON in the response. 71 | 72 | --- 73 | 74 | ## Tech Stack 75 | 76 | - **Programming Language:** Python 3.x 77 | - **Web Framework:** Flask 78 | - **ORM:** SQLAlchemy 79 | - **Database:** SQLite (for simplicity; can be switched to PostgreSQL/MySQL for production) 80 | - **Dependency Management:** pip 81 | 82 | --- 83 | 84 | ## Installation & Setup 85 | 86 | ### Prerequisites 87 | 88 | - Python 3.x installed on your system. 89 | - `pip` for installing Python packages. 90 | 91 | ### Steps 92 | 93 | 1. **Clone the Repository** 94 | 95 | ```bash 96 | git clone https://github.com/your-username/blog-api.git 97 | cd blog-api 98 | ``` 99 | 100 | 2. **Create a Virtual Environment (Optional but Recommended)** 101 | 102 | ```bash 103 | python3 -m venv venv 104 | source venv/bin/activate # On Windows: venv\Scripts\activate 105 | ``` 106 | 107 | 3. **Install Dependencies** 108 | 109 | ```bash 110 | pip install flask flask_sqlalchemy 111 | ``` 112 | 113 | 4. **Run the Application** 114 | 115 | The application will automatically create the SQLite database and required tables on the first run. 116 | 117 | ```bash 118 | python app.py 119 | ``` 120 | 121 | The API will now be accessible at [http://localhost:5000](http://localhost:5000). 122 | 123 | --- 124 | 125 | ## Usage 126 | 127 | ### API Endpoints 128 | 129 | #### 1. Get All Posts 130 | 131 | - **Endpoint:** `GET /posts` 132 | - **Description:** Retrieve a list of all blog posts. 133 | - **Response:** 134 | 135 | ```json 136 | [ 137 | { 138 | "id": 1, 139 | "title": "My First Post", 140 | "content": "This is the content of my first post.", 141 | "created_at": "2025-03-29T12:34:56.789123", 142 | "updated_at": "2025-03-29T12:34:56.789123" 143 | }, 144 | ... 145 | ] 146 | ``` 147 | 148 | #### 2. Get a Single Post 149 | 150 | - **Endpoint:** `GET /posts/` 151 | - **Description:** Retrieve details of a specific post by its ID. 152 | - **Example:** `GET /posts/1` 153 | - **Response:** 154 | 155 | ```json 156 | { 157 | "id": 1, 158 | "title": "My First Post", 159 | "content": "This is the content of my first post.", 160 | "created_at": "2025-03-29T12:34:56.789123", 161 | "updated_at": "2025-03-29T12:34:56.789123" 162 | } 163 | ``` 164 | 165 | #### 3. Create a New Post 166 | 167 | - **Endpoint:** `POST /posts` 168 | - **Description:** Create a new blog post. 169 | - **Request Body:** 170 | 171 | ```json 172 | { 173 | "title": "My New Post", 174 | "content": "Content for my new post." 175 | } 176 | ``` 177 | 178 | - **Response:** 179 | Returns the created post with a unique ID and timestamps. 180 | 181 | ```json 182 | { 183 | "id": 2, 184 | "title": "My New Post", 185 | "content": "Content for my new post.", 186 | "created_at": "2025-03-29T13:00:00.000000", 187 | "updated_at": "2025-03-29T13:00:00.000000" 188 | } 189 | ``` 190 | 191 | #### 4. Update an Existing Post 192 | 193 | - **Endpoint:** `PUT /posts/` 194 | - **Description:** Update the title and/or content of an existing post. 195 | - **Request Body:** 196 | 197 | ```json 198 | { 199 | "title": "Updated Title", 200 | "content": "Updated content." 201 | } 202 | ``` 203 | 204 | - **Response:** 205 | Returns the updated post. 206 | 207 | ```json 208 | { 209 | "id": 1, 210 | "title": "Updated Title", 211 | "content": "Updated content.", 212 | "created_at": "2025-03-29T12:34:56.789123", 213 | "updated_at": "2025-03-29T14:00:00.000000" 214 | } 215 | ``` 216 | 217 | #### 5. Delete a Post 218 | 219 | - **Endpoint:** `DELETE /posts/` 220 | - **Description:** Delete a post by its ID. 221 | - **Response:** 222 | 223 | ```json 224 | { 225 | "message": "Post deleted successfully." 226 | } 227 | ``` 228 | 229 | --- 230 | 231 | ## Testing 232 | 233 | ### Unit Testing 234 | 235 | - You can write tests using Python's `unittest` or `pytest` modules. 236 | - Test the functionality of each endpoint (e.g., ensure a new post is created, retrieval returns correct data, updates persist, etc.). 237 | 238 | ### Example with `pytest` 239 | 240 | 1. **Install pytest:** 241 | 242 | ```bash 243 | pip install pytest 244 | ``` 245 | 246 | 2. **Create a test file (e.g., `test_app.py`) and write tests:** 247 | 248 | ```python 249 | import json 250 | from app import app, db, Post 251 | 252 | def test_get_posts(): 253 | with app.test_client() as client: 254 | response = client.get('/posts') 255 | assert response.status_code == 200 256 | 257 | def test_create_post(): 258 | with app.test_client() as client: 259 | data = { 260 | "title": "Test Post", 261 | "content": "This is a test." 262 | } 263 | response = client.post('/posts', json=data) 264 | assert response.status_code == 201 265 | json_data = json.loads(response.data) 266 | assert json_data['title'] == "Test Post" 267 | ``` 268 | 269 | 3. **Run the tests:** 270 | 271 | ```bash 272 | pytest 273 | ``` 274 | 275 | --- 276 | 277 | ## Deployment 278 | 279 | ### Docker 280 | 281 | Create a `Dockerfile` in the project root: 282 | 283 | ```dockerfile 284 | FROM python:3.9-slim 285 | 286 | WORKDIR /app 287 | 288 | # Copy requirements file and install dependencies 289 | COPY requirements.txt requirements.txt 290 | RUN pip install -r requirements.txt 291 | 292 | # Copy the rest of the application code 293 | COPY . . 294 | 295 | EXPOSE 5000 296 | 297 | CMD ["python", "app.py"] 298 | ``` 299 | 300 | Build and run the Docker container: 301 | 302 | ```bash 303 | docker build -t blog-api . 304 | docker run -d -p 5000:5000 blog-api 305 | ``` 306 | 307 | ### Cloud Deployment 308 | 309 | - Use platforms like Heroku, AWS Elastic Beanstalk, or Google Cloud Run. 310 | - Ensure environment variables and database settings are configured for production. 311 | - Configure logging and monitoring. 312 | 313 | --- 314 | 315 | ## Future Enhancements 316 | 317 | - **User Authentication:** Secure endpoints so only authorized users can create or modify posts. 318 | - **Pagination:** Add pagination for the `GET /posts` endpoint. 319 | - **Search Functionality:** Implement search capabilities for blog posts. 320 | - **Frontend Interface:** Develop a frontend dashboard using React or Vue.js. 321 | - **Advanced Error Handling:** Improve error messages and logging for better debugging. 322 | 323 | --- 324 | 325 | ## Troubleshooting & FAQ 326 | 327 | - **Application Not Starting:** 328 | Ensure that all dependencies are installed and the virtual environment is activated. 329 | 330 | - **Database Issues:** 331 | Verify that the SQLite file is accessible or update the `SQLALCHEMY_DATABASE_URI` for other database systems. 332 | 333 | - **API Returns 404:** 334 | Make sure you are using the correct URL and that the server is running on the expected port. 335 | 336 | --- 337 | 338 | ## Contributing 339 | 340 | Contributions are welcome! Please follow these steps: 341 | 1. Fork the repository. 342 | 2. Create a feature branch (`git checkout -b feature/my-feature`). 343 | 3. Commit your changes. 344 | 4. Push the branch (`git push origin feature/my-feature`). 345 | 5. Open a pull request with a detailed description of your changes. 346 | 347 | --- 348 | 349 | ## License 350 | 351 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 352 | 353 | --- 354 | 355 | Enjoy using the Blog API, and feel free to extend it further to suit your needs! 356 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, jsonify, abort 2 | from flask_sqlalchemy import SQLAlchemy 3 | from datetime import datetime 4 | 5 | # Initialize Flask app and configuration 6 | app = Flask(__name__) 7 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db' # SQLite database file 8 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 9 | 10 | # Initialize SQLAlchemy 11 | db = SQLAlchemy(app) 12 | 13 | # Define the Post model 14 | class Post(db.Model): 15 | id = db.Column(db.Integer, primary_key=True) 16 | title = db.Column(db.String(150), nullable=False) 17 | content = db.Column(db.Text, nullable=False) 18 | created_at = db.Column(db.DateTime, default=datetime.utcnow) 19 | updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) 20 | 21 | def to_dict(self): 22 | return { 23 | 'id': self.id, 24 | 'title': self.title, 25 | 'content': self.content, 26 | 'created_at': self.created_at.isoformat(), 27 | 'updated_at': self.updated_at.isoformat() 28 | } 29 | 30 | # Create the database tables 31 | @app.before_first_request 32 | def create_tables(): 33 | db.create_all() 34 | 35 | # API endpoint to retrieve all posts 36 | @app.route('/posts', methods=['GET']) 37 | def get_posts(): 38 | posts = Post.query.all() 39 | return jsonify([post.to_dict() for post in posts]), 200 40 | 41 | # API endpoint to retrieve a single post by id 42 | @app.route('/posts/', methods=['GET']) 43 | def get_post(post_id): 44 | post = Post.query.get_or_404(post_id) 45 | return jsonify(post.to_dict()), 200 46 | 47 | # API endpoint to create a new post 48 | @app.route('/posts', methods=['POST']) 49 | def create_post(): 50 | data = request.get_json() 51 | if not data or 'title' not in data or 'content' not in data: 52 | abort(400, description="Title and content are required.") 53 | 54 | new_post = Post( 55 | title=data['title'], 56 | content=data['content'] 57 | ) 58 | db.session.add(new_post) 59 | db.session.commit() 60 | return jsonify(new_post.to_dict()), 201 61 | 62 | # API endpoint to update an existing post 63 | @app.route('/posts/', methods=['PUT']) 64 | def update_post(post_id): 65 | post = Post.query.get_or_404(post_id) 66 | data = request.get_json() 67 | if not data: 68 | abort(400, description="No data provided.") 69 | 70 | if 'title' in data: 71 | post.title = data['title'] 72 | if 'content' in data: 73 | post.content = data['content'] 74 | 75 | db.session.commit() 76 | return jsonify(post.to_dict()), 200 77 | 78 | # API endpoint to delete a post 79 | @app.route('/posts/', methods=['DELETE']) 80 | def delete_post(post_id): 81 | post = Post.query.get_or_404(post_id) 82 | db.session.delete(post) 83 | db.session.commit() 84 | return jsonify({"message": "Post deleted successfully."}), 200 85 | 86 | # Run the app 87 | if __name__ == '__main__': 88 | app.run(debug=True) 89 | --------------------------------------------------------------------------------