Using FastAPI to Bridge Mobile Apps with OCPP EV Charging Systems

As electric vehicles (EVs) become more mainstream, users expect seamless experiences when interacting with EV charging stations — especially from their mobile apps. This means building a backend that is not only robust but also real-time, scalable, and easy to extend.

That’s where FastAPI comes in.


⚡ What is FastAPI?

FastAPI is a modern, high-performance web framework for building APIs with Python 3.7+ based on standard Python type hints.

It’s built on top of:

  • Starlette – for the web parts (like routes and WebSockets)
  • Pydantic – for data validation
  • Uvicorn – for ASGI server execution

🚀 Why FastAPI is Ideal for EV Charging Systems

  • Async-ready: Handles many concurrent WebSocket connections — essential for OCPP-based charger networks.
  • WebSocket-native: Built-in support for protocols like OCPP 1.6 without needing additional libraries.
  • Auto-generated docs: Interactive Swagger UI and ReDoc out of the box — great for app devs.
  • Clean developer experience: Pythonic, strongly typed, and readable.
  • Blazing-fast: Comparable to Node.js and Go in performance — excellent for scalable, real-time systems.

✅ Why FastAPI Over Flask?

While Flask is widely used and flexible, FastAPI is purpose-built for modern, async, high-performance use cases like this one:

Feature Flask FastAPI
Async support ⚠️ Experimental ✅ Native
WebSocket support ❌ Needs plugin ✅ Built-in
Pydantic validation ❌ Manual ✅ Automatic
OpenAPI docs ❌ Extra setup ✅ Built-in
Performance 🐢 Sync WSGI ⚡ ASGI-native

If you need WebSocket + REST API + speed, FastAPI is the best choice.


🚦 What Are We Building?

We’ll build a backend that lets:

  • 🔌 EV chargers connect using OCPP 1.6 over WebSocket
  • 📲 Mobile apps connect using REST API over HTTP
  • 📡 Remote start commands be sent to chargers via API call

⚙️ Architecture Overview

graph TD
    Charger["EV Charger (OCPP 1.6)"] -->|WebSocket| FastAPI_WS["FastAPI WebSocket (OCPP)"]
    FastAPI_REST["FastAPI REST API (Mobile Access)"] -->|Internal call| Dispatcher
    Mobile["Mobile App (Flutter/React Native)"] -->|HTTP API| FastAPI_REST
    Dispatcher --> FastAPI_WS

🗂️ Project Structure

ev-system/
├── main.py              # FastAPI app (WebSocket + REST)
├── charge_point.py      # Custom ChargePoint logic (OCPP)
├── dispatcher.py        # RemoteStartTransaction logic
├── registry.py          # Connection tracking
└── requirements.txt

🔧 Step 1: Install Dependencies

pip install fastapi uvicorn ocpp

🧠 Step 2: OCPP Handler — charge_point.py

from ocpp.v16 import ChargePoint as CP
from ocpp.v16.enums import RegistrationStatus
from ocpp.v16 import call_result
from ocpp.routing import on

class ChargePoint(CP):
    @on('BootNotification')
    async def on_boot_notification(self, charge_point_model, charge_point_vendor, **kwargs):
        return call_result.BootNotificationPayload(
            current_time="2025-06-08T00:00:00Z",
            interval=10,
            status=RegistrationStatus.accepted
        )

🔌 Step 3: WebSocket Connection Registry — registry.py

class ConnectionRegistry:
    def __init__(self):
        self._registry = {}

    def add(self, cp_id, cp):
        self._registry[cp_id] = cp

    def get(self, cp_id):
        return self._registry.get(cp_id)

    def remove(self, cp_id):
        self._registry.pop(cp_id, None)

registry = ConnectionRegistry()

📡 Step 4: Send Remote Start — dispatcher.py

from ocpp.v16 import call

async def send_remote_start(cp, id_tag="MOBILE_USER"):
    request = call.RemoteStartTransaction(
        id_tag=id_tag,
        connector_id=1
    )
    response = await cp.call(request)
    return response.status

🚀 Step 5: Main App — main.py

from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException
from charge_point import ChargePoint
from dispatcher import send_remote_start
from registry import registry

app = FastAPI()

@app.websocket("/ws/{cp_id}")
async def websocket_handler(websocket: WebSocket, cp_id: str):
    await websocket.accept(subprotocol="ocpp1.6")
    cp = ChargePoint(cp_id, websocket)
    registry.add(cp_id, cp)

    try:
        await cp.start()
    except WebSocketDisconnect:
        registry.remove(cp_id)

@app.post("/api/remote-start/{cp_id}")
async def remote_start(cp_id: str):
    cp = registry.get(cp_id)
    if not cp:
        raise HTTPException(404, "Charge point not connected")
    status = await send_remote_start(cp)
    return {"status": status}

📲 Step 6: Test API from Mobile App or Postman

Example Request:

POST /api/remote-start/CP001
Content-Type: application/json

{}

Your charger (connected via WebSocket to /ws/CP001) will receive a RemoteStartTransaction message.


🔐 Bonus: Securing the API

For production, add:

  • 🔑 JWT Authentication (fastapi-jwt-auth)
  • 📈 Rate limiting (e.g., slowapi)
  • 📦 Use Redis or MongoDB for persistent charger state
  • 🔒 HTTPS with Caddy or NGINX

🧱 Use Cases This Enables

  • ✅ Start/Stop charging from the mobile app
  • ✅ Display current charger status
  • ✅ Trigger firmware updates remotely
  • ✅ Support multiple chargers with real-time communication
  • ✅ Clean API docs for frontend/mobile teams

💬 Conclusion

FastAPI brings together everything you need to build a mobile-connected, OCPP-compliant, real-time backend:

  • 🔥 Async performance
  • 🌐 WebSocket + REST support
  • ⚙️ Type-safe development with Pydantic
  • 📲 Beautiful API docs for mobile clients

If you're building an EV charging platform with mobile integration, FastAPI is the future-proof choice.


📦 Want More?

  • ✅ Docker Compose version with MongoDB or Redis?
  • ✅ Flutter mobile client example?
  • ✅ Admin dashboard with authentication?

Let me know — happy to help you build your smart EV platform!



Get in Touch with us

Chat with Us on LINE

iiitum1984

Speak to Us or Whatsapp

(+66) 83001 0222

Related Posts

Our Products