Day: June 29, 2025

  • Utilizing WebSockets in FastAPI for Real-time Applications

    Utilizing WebSockets in FastAPI for Real-time Applications

    Introduction

    WebSockets have become a crucial part of building modern, real-time web applications. They allow for bidirectional, full-duplex communication channels over a single TCP connection, which is essential for applications like online games, chat applications, and live updates for dashboards. FastAPI, with its asynchronous capabilities, provides excellent support for WebSockets. In this article, we’ll dive into integrating WebSockets into your FastAPI application.

    Setting Up Your FastAPI Project

    Before we begin, make sure you have FastAPI and a suitable ASGI server installed. We’ll use uvicorn for this tutorial. Install them with the following pip commands:

    pip install fastapi
    pip install "uvicorn[standard]"
    

    Create a new Python file, app.py, and import the necessary modules:

    from fastapi import FastAPI, WebSocket
    from fastapi.responses import HTMLResponse
    

    Basic WebSocket Implementation

    A basic FastAPI app with WebSocket support involves defining an endpoint that listens for WebSocket connections:

    app = FastAPI()
    
    html = """
    <!DOCTYPE html>
    <html>
    <head>
        <title>FastAPI WebSocket Test</title>
    </head>
    <body>
        <h1>WebSocket Echo Test</h1>
        <button onclick="connectWebSocket()">Connect</button>
        <ul id="messages">
          <li>Connecting to server...</li>
        </ul>
        <script>
            function connectWebSocket() {
                const ws = new WebSocket('ws://localhost:8000/ws');
                ws.onmessage = function(event) {
                    const messages = document.getElementById('messages');
                    const message = document.createElement('li');
                    const content = document.createTextNode(event.data);
                    message.appendChild(content);
                    messages.appendChild(message);
                };
                ws.send('Hello from the client!');
            }
        </script>
    </body>
    </html>
    """
    
    @app.get("/")
    async def get():
        return HTMLResponse(html)
    
    @app.websocket("/ws")
    async def websocket_endpoint(websocket: WebSocket):
        await websocket.accept()
        while True:
            data = await websocket.receive_text()
            await websocket.send_text(f"Echo: {data}")
    

    This code sets up a simple FastAPI application with one WebSocket endpoint. It accepts incoming connections, listens for messages, and sends them back as a simple echo service.

    Running the Application

    To run the FastAPI application, use the following command:

    uvicorn app:app --reload
    

    Visit http://127.0.0.1:8000 to see your WebSocket echo test in action.

    Adding More Functionality

    To enhance this basic implementation, consider managing multiple WebSocket connections, broadcasting messages to all connected clients, or integrating authentication. FastAPI’s dependency injection system can help manage complex state and service dependencies.

    Conclusion

    WebSockets add real-time capabilities to your applications and FastAPI makes it simple to get started. Whether you’re building chat applications or real-time notifications for dashboards, understanding how to integrate WebSockets effectively could greatly enhance your application’s functionality.

    In future articles, we’ll explore more complex WebSocket use cases in FastAPI, including authentication, message broadcasting, and multi-client handling strategies. Stay tuned!

    Further Reading

  • Effective Techniques for Testing FastAPI Applications

    Effective Techniques for Testing FastAPI Applications

    As a software engineer and backend web developer, I’ve always found testing to be an integral part of the development process. When working with FastAPI, a modern, fast web framework for building APIs with Python, setting up efficient testing routines can significantly enhance code quality and reliability. In this article, we’ll explore some effective techniques for testing FastAPI applications.

    Why Write Tests?

    Testing is crucial for verifying the correctness of your applications, ensuring that everything works as expected. It helps catch bugs and errors early, which can save time and resources. Furthermore, having a robust test suite allows developers to make changes and refactor code with confidence, knowing that the existing functionality remains intact.

    Setting Up Your Testing Environment

    1. Choose a Testing Framework:
      FastAPI works seamlessly with popular testing frameworks like pytest, which I highly recommend due to its powerful features and ease of use. Start by installing pytest in your development environment:

      pip install pytest
      
    2. Use TestClient:
      FastAPI provides a TestClient that is based on Starlette's TestClient, powered by requests. It allows you to simulate requests to your API without any running server:

      from fastapi.testclient import TestClient
      from my_fastapi_app import app
      
      client = TestClient(app)
      

    Writing Your First Test

    Let’s look at a simple example of writing a test for a FastAPI application endpoint:

    from fastapi import FastAPI
    from fastapi.testclient import TestClient
    
    app = FastAPI()
    
    @app.get("/items/{item_id}")
    async def read_item(item_id: int):
        return {"item_id": item_id}
    
    client = TestClient(app)
    
    def test_read_item():
        response = client.get("/items/42")
        assert response.status_code == 200
        assert response.json() == {"item_id": 42}
    

    This simple test checks if the endpoint correctly responds with the expected JSON output.

    Utilizing Fixtures

    Fixtures in pytest can be used to create a fixed baseline upon which tests can be reliably and repeatedly executed. This is particularly useful for complex setups:

    import pytest
    
    @pytest.fixture
    def client():
        with TestClient(app) as c:
            yield c
    
    def test_read_item(client):
        response = client.get("/items/42")
        assert response.status_code == 200
        assert response.json() == {"item_id": 42}
    

    Testing Asynchronous Code

    FastAPI supports async and await, which means you might encounter asynchronous operations in your code. Thankfully, pytest supports async tests with the help of pytest-asyncio:

    pip install pytest-asyncio
    

    Here’s how you can write async tests:

    import pytest
    
    @pytest.mark.asyncio
    async def test_async_endpoint(client):
        response = await client.get("/async/items/42")
        assert response.status_code == 200
        assert response.json() == {"item_id": 42}
    

    Conclusion

    Testing in FastAPI is streamlined thanks to its flexible design and seamless integration with tools like pytest. By regularly running tests, using fixtures, and leveraging async support, you will be able to maintain and grow your application with confidence.

    Keep writing and improving your tests as part of your development workflow, and your FastAPI applications will reach a higher level of reliability and performance. Happy coding!

  • I need to convert a crontab entry to a systemd timer

    john:

    I have a crontab entry that executes a shell script every 30 minutes. It passes a parameter to th script. I need to convert it to be a systemd timer. My crontab entry is:

    */30 * * * * /Users/jturman/development/john/fastapi/cron.sh sandbox >> /Users/jturman/development/john/fastapi/python-cron.log 2>&1

    Chatbot:

    To convert your cron job to a systemd timer, you need to create two separate files: a service unit file and a timer unit file. Additionally, you need to make sure that systemd can access your script and that it has the necessary execution permissions.

    1. Create the Service Unit File

      This file defines the actual work to be executed. Let’s create it at ~/.config/systemd/user/fastapi-cron.service:

      [Unit]
      Description=Run fastapi cron script
      
      [Service]
      Type=oneshot
      ExecStart=/Users/jturman/development/john/fastapi/cron.sh sandbox
      StandardOutput=append:/Users/jturman/development/john/fastapi/python-cron.log
      StandardError=append:/Users/jturman/development/john/fastapi/python-cron.log
      
    2. Create the Timer Unit File

      This file defines when the script should be executed. Let’s create it at ~/.config/systemd/user/fastapi-cron.timer:

      [Unit]
      Description=Run fastapi cron script every 30 minutes
      
      [Timer]
      OnBootSec=15min
      OnUnitActiveSec=30min
      
      [Install]
      WantedBy=timers.target
      
      • OnBootSec=15min: This means the timer will trigger 15 minutes after boot (you can adjust it if needed).
      • OnUnitActiveSec=30min: This means the script will rerun every 30 minutes after it was last run.
    3. Reload systemd daemons and enable the timer

      Since these files are in your user directory, you should manage them using the --user flag. Execute the following commands:

      # Reload the systemd manager configuration
      systemctl --user daemon-reload
      
      # Start and enable the timer
      systemctl --user start fastapi-cron.timer
      systemctl --user enable fastapi-cron.timer
      
    4. Verify the Timer Status

      Check if the timer is active and running as expected:

      systemctl --user list-timers fastapi-cron.timer
      

      You should see output indicating that your timer is active and when it is next set to fire.

    By following these steps, you should have successfully converted your cron job to use systemd timers instead. Keep in mind that if your script requires any specific environment variables or paths, you may need to explicitly set them in the service file or adjust your script accordingly.

    john:

    how did you choose the path for the 2 files?

    Chatbot:

    The paths for the systemd service and timer files were chosen based on the assumption that you want to use user-specific systemd units, which are managed without requiring root privileges. Here’s why those paths were chosen:

    1. User-Specific Directory (~/.config/systemd/user/):

      • Files placed here apply only to the user that owns them. This is appropriate when you are creating systemd units that do not need to be managed by the system-wide systemd but rather on a per-user basis.
      • This directory structure conforms to the XDG base directory specification, which is widely used for user-specific configuration files.
      • Using user-specific units means you don’t need administrative privileges (sudo) to manage the services and timers.
    2. File Names (fastapi-cron.service and fastapi-cron.timer):

      • The .service file defines what will happen when the timer triggers.
      • The .timer file defines when the .service should be triggered.
      • Names are chosen to be meaningful and descriptive of their function. You want them to reflect the task they are managing, especially if you end up creating more timers and services later.

    If setting up systemd units for the system that apply to all users, you would place them in directories like /etc/systemd/system/ for custom units or /usr/lib/systemd/system/ for units provided by packages. However, you would need administrative rights to manage these system-wide systemd units.

    john:

    is the .config directory in my home directory expected by systemctl?

    Chatbot:

    Yes, the ~/.config/systemd/user/ directory is the expected location for user-specific systemd unit files, including both .service and .timer units. When you manage systemd units with the --user flag, systemctl looks in the ~/.config/systemd/user/ directory by default for unit files specific to your user account.

    Here’s a brief summary of how this works:

    1. Per-User Units:

      • By placing unit files in ~/.config/systemd/user/, you create a setup where these units are managed at the user level.
      • This means you don’t need root privileges to enable, start, stop, or view the status of these units.
    2. Enabling User Services:

      • You control and manage these services using systemctl --user commands.
      • For example, to start or enable a user-specific unit, you’d use: systemctl --user start myservice.timer and systemctl --user enable myservice.timer respectively.
    3. Activating the User Manager:

      • You have to ensure that your user session is running a systemd user manager instance for this to work.
      • On most modern Linux distributions, this is done automatically when you log into your graphical or text session. If not, you may need to manually activate user systemd with commands like loginctl enable-linger [your-username].
    4. Debugging and Status:

      • You can view the status of user-specific units similarly to system-wide ones: systemctl --user status myservice.service.

    Using this setup allows regular users to have personal background services and scheduled tasks without needing administrative intervention, and it keeps your user-specific configurations isolated within your home directory.

    john:

    please explain again how to make my new fastapi service keep running even if I log out.

    Chatbot:

    To ensure that your user-specific systemd services continue running even after you log out, you need to use the linger feature of systemd. Here’s how you can set it up:

    1. Enable Linger for Your User Account:

      • Linger is a systemd feature that allows user services to continue running after you log out and to start user services without the need for the user to log in.

      • To enable linger for your user account, use the loginctl command:

        sudo loginctl enable-linger your-username
        

        Replace your-username with your actual username. This command allows your user systemd manager to continue operating even when your user is not logged into any active session.

    2. Setup Your Systemd Service and Timer:

      • Ensure your .service and .timer files are correctly placed in ~/.config/systemd/user/.
      • Be sure to reload the user manager if you make any changes using systemctl --user daemon-reload.
    3. Start and Enable Your Timer:

      • Use systemctl --user to start and enable your timer:

        systemctl --user enable fastapi-cron.timer
        systemctl --user start fastapi-cron.timer
        
      • enable ensures that the timer is automatically activated at boot time or when your systemd user manager starts.

      • start immediately begins the timer service.

    4. Verify the Status:

      • Check the status of your timer and the associated service:

        systemctl --user status fastapi-cron.timer
        systemctl --user status fastapi-cron.service
        
    5. Test by Logging Out:

      • After setting the above, you can test by logging out of your session and checking if the service continues to run. If properly configured with linger enabled, your timer should continue to operate.

    By enabling lingering for your user, you ensure that your user-level systemd manager remains active, thus keeping your services and timers running independently of your current login session state. This setup is common for server environments or scenarios where services must run continuously irrespective of active user sessions.