FastAPI Config: Serve Configuration Files Via API
In this comprehensive guide, we will explore how to build a FastAPI application that serves configuration files. This setup allows you to retrieve configuration files stored on your server from anywhere using a simple curl command. Using FastAPI for this purpose provides a robust, efficient, and modern way to manage and distribute your configuration files. This approach ensures that your applications can dynamically fetch the latest configurations, making your system more flexible and maintainable.
Setting Up the FastAPI Project Structure
To begin, let's set up the project structure. A well-organized project structure is crucial for maintainability and scalability. Hereβs a recommended structure:
config_api/
βββ main.py
βββ config/
β βββ config1.json
β βββ config2.yaml
β βββ ...
βββ utils.py
main.py: This is the main file where the FastAPI application will be defined.config/: This directory will hold all your configuration files in various formats such as.json,.yaml,.ini, etc.utils.py: This file can contain utility functions, such as functions to read and parse configuration files.
Step-by-Step Implementation
-
Install FastAPI and Uvicorn
First, you need to install FastAPI and Uvicorn, an ASGI server, which will run your application. Open your terminal and run:
pip install fastapi uvicorn pyyaml ```
Here, `pyyaml` is included to handle `.yaml` configuration files. You can install other necessary libraries based on the types of configuration files you intend to support.
-
Create Configuration Files
Create a
configdirectory and add some sample configuration files. For example, let's createconfig1.jsonandconfig2.yaml.config/config1.json:{ "setting1": "value1", "setting2": 123, "setting3": true }config/config2.yaml:setting4: "value4" setting5: 456 setting6: false -
Implement Utility Functions in
utils.pyCreate a
utils.pyfile to handle reading the configuration files. This file will contain functions to load.jsonand.yamlfiles.import json import yaml from pathlib import Path def read_json_config(file_path: str) -> dict: try: with open(file_path, 'r') as f: return json.load(f) except FileNotFoundError: raise FileNotFoundError(f"Configuration file not found: {file_path}") except json.JSONDecodeError: raise ValueError(f"Invalid JSON format in: {file_path}") def read_yaml_config(file_path: str) -> dict: try: with open(file_path, 'r') as f: return yaml.safe_load(f) except FileNotFoundError: raise FileNotFoundError(f"Configuration file not found: {file_path}") except yaml.YAMLError: raise ValueError(f"Invalid YAML format in: {file_path}") def read_config(file_path: str) -> dict: file_path = Path(file_path) if file_path.suffix == '.json': return read_json_config(str(file_path)) elif file_path.suffix == '.yaml' or file_path.suffix == '.yml': return read_yaml_config(str(file_path)) else: raise ValueError(f"Unsupported configuration file format: {file_path.suffix}")This
utils.pyfile includes functions to read both JSON and YAML configuration files. Theread_configfunction automatically determines the file type based on the file extension and calls the appropriate reading function. Error handling is included to manage file not found scenarios and invalid file formats. -
Create the Main FastAPI Application in
main.pyNow, letβs create the main FastAPI application in
main.py. This application will define the API endpoints to serve the configuration files.from fastapi import FastAPI, HTTPException from fastapi.responses import JSONResponse from utils import read_config import os app = FastAPI() CONFIG_DIR = "config" @app.get("/config/{filename}") async def get_config(filename: str): file_path = os.path.join(CONFIG_DIR, filename) try: config_data = read_config(file_path) return JSONResponse(content=config_data) except FileNotFoundError: raise HTTPException(status_code=404, detail="Configuration file not found") except ValueError as e: raise HTTPException(status_code=400, detail=str(e))In this code:
- We import necessary modules from FastAPI and the
utils.pyfile. - We initialize a FastAPI app.
- We define a
CONFIG_DIRvariable to specify the directory where configuration files are stored. - We create an endpoint
/config/{filename}that takes the filename as a parameter. - The
get_configfunction reads the configuration file using theread_configfunction fromutils.py. - If the file is found and the format is valid, the function returns the configuration data as a JSON response.
- If the file is not found, it raises an HTTP 404 error. If the file format is invalid, it raises an HTTP 400 error.
- We import necessary modules from FastAPI and the
Running the Application
To run the application, open your terminal, navigate to the project directory, and run the following command:
uvicorn main:app --reload
This command starts the Uvicorn server, hosting your FastAPI application. The --reload flag allows the server to automatically reload whenever you make changes to the code.
Testing the Endpoint with curl
Now that the application is running, you can test the endpoint using curl. Open a new terminal and run the following commands:
To fetch config1.json:
curl http://localhost:8000/config/config1.json
Expected output:
{"setting1": "value1", "setting2": 123, "setting3": true}
To fetch config2.yaml:
curl http://localhost:8000/config/config2.yaml
Expected output:
{"setting4": "value4", "setting5": 456, "setting6": false}
If you try to fetch a non-existent file, such as config3.json, you will receive an HTTP 404 error:
curl http://localhost:8000/config/config3.json
Expected output:
{"detail":"Configuration file not found"}
Advanced Features and Improvements
1. Environment Variable Configuration
To make the application more configurable, you can use environment variables. For example, you can set the CONFIG_DIR environment variable to specify the directory where configuration files are stored. Modify the main.py file as follows:
import os
CONFIG_DIR = os.environ.get("CONFIG_DIR", "config")
Now, the application will first check if the CONFIG_DIR environment variable is set. If it is, it will use that value. Otherwise, it will default to the config directory.
2. Adding Validation
To ensure that the configuration files are valid, you can add validation using libraries like Pydantic. First, install Pydantic:
pip install pydantic
Then, create a Pydantic model to define the structure of your configuration files. For example, create a config_model.py file:
from pydantic import BaseModel
class ConfigModel(BaseModel):
setting1: str
setting2: int
setting3: bool
Modify the utils.py file to validate the configuration data against the Pydantic model:
from pydantic import ValidationError
from config_model import ConfigModel
def read_json_config(file_path: str) -> dict:
try:
with open(file_path, 'r') as f:
data = json.load(f)
ConfigModel(**data)
return data
except FileNotFoundError:
raise FileNotFoundError(f"Configuration file not found: {file_path}")
except json.JSONDecodeError:
raise ValueError(f"Invalid JSON format in: {file_path}")
except ValidationError as e:
raise ValueError(f"Configuration validation error: {e}")
Now, if the configuration file does not match the structure defined in the ConfigModel, a validation error will be raised.
3. Using API Keys for Authentication
To secure your configuration API, you can use API keys for authentication. Modify the main.py file to include API key authentication:
from fastapi import Security, HTTPException
from fastapi.security.api_key import APIKeyHeader, APIKey
API_KEY = "your_secret_api_key"
API_KEY_NAME = "X-API-Key"
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=True)
async def get_api_key(api_key_header: str = Security(api_key_header)) -> str:
if api_key_header == API_KEY:
return api_key_header
else:
raise HTTPException(status_code=403, detail="Could not validate API key")
@app.get("/config/{filename}", dependencies=[Security(get_api_key)])
async def get_config(filename: str):
# ... (rest of the code)
Now, you need to include the X-API-Key header in your curl requests:
curl -H "X-API-Key: your_secret_api_key" http://localhost:8000/config/config1.json
If the API key is incorrect or missing, you will receive an HTTP 403 error.
Conclusion
In this guide, you've learned how to create a FastAPI application to serve configuration files. This setup provides a flexible and efficient way to manage and distribute configurations to your applications. By incorporating advanced features such as environment variables, validation, and API key authentication, you can further enhance the robustness and security of your configuration API.
By following these steps, you can easily manage and serve configuration files using FastAPI, making your applications more flexible and maintainable.
For further reading on FastAPI and its capabilities, visit the official FastAPI Documentation.