# Checkpoint 2: Modular Organization

So far we've got a simple app with a few routes, but its already starting to get a bit cluttered, so to make this app easier to maintain as it grows, let's start adopting a more modular organizational structure.

We'll move the app into a new directory and split the route definitions across multiple files.

## Application Factory Pattern

First, let's implement the Flask ["application factory pattern"](https://flask.palletsprojects.com/en/1.1.x/patterns/appfactories/) where we define our application inside a function. Among other benefits, this can help us test the app later.

Create a new subdirectory in your repo called "web*app" with a file called "\\*\_ini&#x74;*\\*.py" and place the following contents inside:

```python
# web_app/__init__.py

from flask import Flask

from web_app.routes.home_routes import home_routes
from web_app.routes.book_routes import book_routes
from web_app.routes.weather_routes import weather_routes

def create_app():
    app = Flask(__name__)
    app.register_blueprint(home_routes)
    app.register_blueprint(book_routes)
    app.register_blueprint(weather_routes)
    return app

if __name__ == "__main__":
    my_app = create_app()
    my_app.run(debug=True)
```

Review the new "create\_app" function and notice we are importing and referencing the routing functions from their own logically-related files.

> NOTE: in the future, whenever we add more routing files, we'll need to import and register them in this way for our app to recognize them!

## Route Blueprints

Let's now move the route definitions to their own logically-related files, just to be more organized.

We're using [Flask Blueprints](https://flask.palletsprojects.com/en/1.1.x/blueprints/) to store the route definitions in a way the app can recognize.

Inside the "web\_app" directory, create a new subdirectory called "routes" with new files called "home\_routes.py", "book\_routes.py", and place the following contents inside, respectively:

```python
# web_app/routes/home_routes.py

from flask import Blueprint, request #, render_template

home_routes = Blueprint("home_routes", __name__)

@home_routes.route("/")
@home_routes.route("/home")
def index():
    print("HOME...")
    return "Welcome Home"

@home_routes.route("/about")
def about():
    print("ABOUT...")
    return "About Me"

@home_routes.route("/hello")
def hello_world():
    print("HELLO...", dict(request.args))
    # NOTE: `request.args` is dict-like, so below we're using the dictionary's `get()` method,
    # ... which will return None instead of throwing an error if key is not present
    # ... see also: https://www.w3schools.com/python/ref_dictionary_get.asp
    name = request.args.get("name") or "World"
    return f"Hello, {name}!"
```

```python
# web_app/routes/book_routes.py

from flask import Blueprint, jsonify

book_routes = Blueprint("book_routes", __name__)

@book_routes.route("/api/books")
@book_routes.route("/api/books.json")
def list_books():
    print("BOOKS...")
    books = [
        {"id": 1, "title": "Book 1", "year": 1957},
        {"id": 2, "title": "Book 2", "year": 1990},
        {"id": 3, "title": "Book 3", "year": 2031},
    ] # some dummy / placeholder data
    return jsonify(books)

@book_routes.route("/api/books/<int:book_id>")
@book_routes.route("/api/books/<int:book_id>.json")
def get_book(book_id):
    print("BOOK...", book_id)
    book = {"id": book_id, "title": f"Example Book", "year": 2000} # some dummy / placeholder data
    return jsonify(book)
```

```python
# web_app/routes/weather_routes.py

from flask import Blueprint, request, jsonify

from app.weather_service import get_hourly_forecasts

weather_routes = Blueprint("weather_routes", __name__)

@weather_routes.route("/weather/forecast.json")
def weather_forecast_api():
    print("WEATHER FORECAST (API)...")
    print("URL PARAMS:", dict(request.args))

    country_code = request.args.get("country_code") or "US"
    zip_code = request.args.get("zip_code") or "20057"

    results = get_hourly_forecasts(country_code=country_code, zip_code=zip_code)
    if results:
        return jsonify(results)
    else:
        return jsonify({"message":"Invalid Geography. Please try again."}), 404
```

## Running the Web App

We can now delete the original "hello.py" file.

Since we moved the Flask app into the "web\_app" directory, we'll use this new command to run it moving forward:

```bash
# mac:
FLASK_APP=web_app flask run

# windows:
export FLASK_APP=web_app
flask run
```

Remember, the "init" file is the entry-point into a local Python module, so when we run the app this way, Flask will run the "web\_app" module's "init" file.

Let's restart the web server using this new command, and visit the same URLs in the browser to see the app is working like it was before.

Great! With the modular codebase we'll be able to maintain and extend our app more easily in the future.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://prof-rossetti.gitbook.io/intro-to-python/exercises/web-app/checkpoints/2-modular-org.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
