diff --git a/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/README.md b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/README.md
new file mode 100644
index 00000000..825f761a
--- /dev/null
+++ b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/README.md
@@ -0,0 +1,45 @@
+# Quickstart
+
+Follow these steps to set up and run the FastAPI API:
+
+1. **Create a virtual environment:**
+
+```bash
+python3 -m venv venv
+```
+
+2. **Activate the virtual environment:**
+
+- On macOS/Linux:
+ ```bash
+ source venv/bin/activate
+ ```
+- On Windows:
+ ```bash
+ venv\Scripts\activate
+ ```
+
+3. **Install dependencies:**
+
+```bash
+pip install -r requirements.txt
+```
+
+4. **Run the API with uvicorn:**
+
+```bash
+uvicorn main:app --reload
+```
+
+The API will be available at [http://localhost:8000](http://localhost:8000)
+
+## Credentials
+
+When you run the API, you will be prompted to insert your Elasticsearch endpoint and API key using `getpass`. These credentials are required to connect to your Elasticsearch instance.
+
+At the terminal, you will see prompts like this to insert your credentials:
+
+```
+Insert the Elasticsearch endpoint here:
+Insert the Elasticsearch API key here:
+```
\ No newline at end of file
diff --git a/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/elasticsearch_bulk_request.txt b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/elasticsearch_bulk_request.txt
new file mode 100644
index 00000000..10260731
--- /dev/null
+++ b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/elasticsearch_bulk_request.txt
@@ -0,0 +1,52 @@
+
+POST products/_bulk
+{"index":{}}
+{"product_name": "iPhone 15 Pro", "price": 999.99, "description": "Latest flagship smartphone with titanium design, A17 Pro chip, and advanced camera system"}
+{"index":{}}
+{"product_name": "MacBook Pro 14-inch", "price": 1999.99, "description": "Professional laptop with M3 chip, Liquid Retina XDR display, and up to 22-hour battery life"}
+{"index":{}}
+{"product_name": "AirPods Pro 2nd Gen", "price": 249.99, "description": "Wireless earbuds with active noise cancellation, spatial audio, and USB-C charging case"}
+{"index":{}}
+{"product_name": "iPad Air", "price": 599.99, "description": "Versatile tablet with M1 chip, 10.9-inch Liquid Retina display, and Apple Pencil support"}
+{"index":{}}
+{"product_name": "Apple Watch Series 9", "price": 399.99, "description": "Advanced smartwatch with health monitoring, fitness tracking, and always-on Retina display"}
+{"index":{}}
+{"product_name": "Samsung Galaxy S24 Ultra", "price": 1199.99, "description": "Premium Android smartphone with S Pen, 200MP camera, and AI-powered features"}
+{"index":{}}
+{"product_name": "Dell XPS 13", "price": 1299.99, "description": "Ultra-portable laptop with Intel Core i7, 13.4-inch InfinityEdge display, and premium build"}
+{"index":{}}
+{"product_name": "Sony WH-1000XM5", "price": 399.99, "description": "Premium noise-canceling headphones with 30-hour battery and exceptional audio quality"}
+{"index":{}}
+{"product_name": "Nintendo Switch OLED", "price": 349.99, "description": "Hybrid gaming console with vibrant OLED screen, enhanced audio, and portable design"}
+{"index":{}}
+{"product_name": "Kindle Paperwhite", "price": 139.99, "description": "E-reader with 6.8-inch glare-free display, waterproof design, and weeks of battery life"}
+{"index":{}}
+{"product_name": "Google Pixel 8 Pro", "price": 999.99, "description": "AI-powered smartphone with advanced computational photography and 7 years of updates"}
+{"index":{}}
+{"product_name": "Microsoft Surface Pro 9", "price": 1099.99, "description": "2-in-1 laptop tablet with Intel Core processors, detachable keyboard, and Surface Pen support"}
+{"index":{}}
+{"product_name": "Dyson V15 Detect", "price": 749.99, "description": "Powerful cordless vacuum with laser dust detection and intelligent suction adjustment"}
+{"index":{}}
+{"product_name": "Fitbit Versa 4", "price": 199.99, "description": "Health and fitness smartwatch with GPS, heart rate monitoring, and 6+ day battery"}
+{"index":{}}
+{"product_name": "Bose QuietComfort 45", "price": 329.99, "description": "Wireless noise-canceling headphones with balanced sound and 24-hour battery life"}
+{"index":{}}
+{"product_name": "Tesla Model Y", "price": 47190.00, "description": "Electric SUV with autopilot, 330-mile range, and minimalist interior design"}
+{"index":{}}
+{"product_name": "Instant Pot Duo 7-in-1", "price": 99.99, "description": "Multi-use pressure cooker that replaces 7 kitchen appliances with smart programming"}
+{"index":{}}
+{"product_name": "LG OLED C3 55-inch TV", "price": 1499.99, "description": "4K OLED smart TV with perfect blacks, vibrant colors, and gaming-optimized features"}
+{"index":{}}
+{"product_name": "Vitamix A3500", "price": 549.99, "description": "Professional-grade blender with preset programs, self-cleaning, and 10-year warranty"}
+{"index":{}}
+{"product_name": "Herman Miller Aeron Chair", "price": 1395.00, "description": "Ergonomic office chair with breathable mesh, lumbar support, and 12-year warranty"}
+{"index":{}}
+{"product_name": "Canon EOS R5", "price": 3899.99, "description": "Professional mirrorless camera with 45MP sensor, 8K video, and advanced autofocus"}
+{"index":{}}
+{"product_name": "Sonos Arc Soundbar", "price": 899.99, "description": "Premium soundbar with Dolby Atmos, voice control, and seamless music streaming"}
+{"index":{}}
+{"product_name": "Peloton Bike+", "price": 2495.00, "description": "Interactive exercise bike with rotating HD touchscreen and live fitness classes"}
+{"index":{}}
+{"product_name": "Roomba j7+", "price": 849.99, "description": "Smart robot vacuum with object avoidance, self-emptying base, and app control"}
+{"index":{}}
+{"product_name": "KitchenAid Stand Mixer", "price": 379.99, "description": "Iconic stand mixer with 10-speed control, tilt-head design, and multiple attachments"}
diff --git a/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/index.html b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/index.html
new file mode 100644
index 00000000..273f624c
--- /dev/null
+++ b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/index.html
@@ -0,0 +1,193 @@
+
+
+
+
+
+ TechStore - Product Search
+
+
+
+ 🛍️ TechStore - Find Your Perfect Product
+
+
+
+
+
+
+
+
+ Search Results
+
+ 🔍 Enter a search term above to find products
+
+
+
+
+
+
+
+
+
+
diff --git a/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/inegst_data.py b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/inegst_data.py
new file mode 100644
index 00000000..6c601296
--- /dev/null
+++ b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/inegst_data.py
@@ -0,0 +1,54 @@
+import json
+import os
+
+from elasticsearch import Elasticsearch
+
+es_client = Elasticsearch(
+ hosts=[os.environ["ELASTICSEARCH_ENDPOINT"]],
+ api_key=os.environ["ELASTICSEARCH_API_KEY"],
+)
+
+PRODUCTS_INDEX = "products"
+
+
+def create_products_index():
+ try:
+ mapping = {
+ "mappings": {
+ "properties": {
+ "product_name": {"type": "text", "analyzer": "standard"},
+ "price": {"type": "float"},
+ "description": {"type": "text", "analyzer": "standard"},
+ }
+ }
+ }
+
+ es_client.indices.create(index=PRODUCTS_INDEX, body=mapping)
+ print(f"Index {PRODUCTS_INDEX} created successfully")
+ except Exception as e:
+ print(f"Error creating index: {e}")
+
+
+def load_products_from_ndjson():
+ try:
+ if not os.path.exists("products.ndjson"):
+ print("Error: products.ndjson file not found!")
+ return
+
+ products_loaded = 0
+ with open("products.ndjson", "r") as f:
+ for line in f:
+ if line.strip():
+ product_data = json.loads(line.strip())
+ es_client.index(index=PRODUCTS_INDEX, body=product_data)
+ products_loaded += 1
+
+ print(f"Successfully loaded {products_loaded} products into Elasticsearch")
+
+ except Exception as e:
+ print(f"Error loading products: {e}")
+
+
+if __name__ == "__main__":
+ create_products_index()
+ load_products_from_ndjson()
diff --git a/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/main.py b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/main.py
new file mode 100644
index 00000000..cb9fffe1
--- /dev/null
+++ b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/main.py
@@ -0,0 +1,129 @@
+import json
+import os
+from datetime import datetime
+from getpass import getpass
+from typing import Dict, List
+
+import uvicorn
+from elasticsearch import Elasticsearch
+from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
+from fastapi.responses import FileResponse
+from pydantic import BaseModel, Field
+
+app = FastAPI(title="Elasticsearch - FastAPI with websockets")
+
+
+os.environ["ELASTICSEARCH_ENDPOINT"] = getpass(
+ "Insert the Elasticsearch endpoint here: "
+)
+os.environ["ELASTICSEARCH_API_KEY"] = getpass("Insert the Elasticsearch API key here: ")
+
+es_client = Elasticsearch(
+ hosts=[os.environ["ELASTICSEARCH_ENDPOINT"]],
+ api_key=os.environ["ELASTICSEARCH_API_KEY"],
+)
+
+PRODUCTS_INDEX = "products"
+
+
+class Product(BaseModel):
+ product_name: str
+ price: float
+ description: str
+
+
+class SearchNotification(BaseModel):
+ session_id: str
+ query: str
+ timestamp: datetime = Field(default_factory=datetime.now)
+
+
+class SearchResponse(BaseModel):
+ query: str
+ results: List[Dict]
+ total: int
+
+
+# Store active WebSocket connections
+connections: List[WebSocket] = []
+
+
+@app.websocket("/ws")
+async def websocket_endpoint(websocket: WebSocket):
+ await websocket.accept()
+ connections.append(websocket)
+ print(f"Client connected. Total connections: {len(connections)}")
+
+ try:
+ while True:
+ await websocket.receive_text()
+
+ except WebSocketDisconnect:
+ connections.remove(websocket)
+ print(f"Client disconnected. Total connections: {len(connections)}")
+
+
+@app.get("/search")
+async def search_products(q: str, session_id: str = "unknown"):
+ # List of search terms that should trigger a notification
+ WATCH_LIST = ["iphone", "kindle"]
+
+ try:
+ query_body = {
+ "query": {
+ "bool": {
+ "should": [
+ {"match": {"product_name": q}},
+ {"match_phrase": {"description": q}},
+ ],
+ "minimum_should_match": 1,
+ }
+ },
+ "size": 20,
+ }
+
+ response = es_client.search(index=PRODUCTS_INDEX, body=query_body)
+
+ results = []
+ for hit in response["hits"]["hits"]:
+ product = hit["_source"]
+ product["score"] = hit["_score"]
+ results.append(product)
+
+ results_count = response["hits"]["total"]["value"]
+
+ # Only send notification if the search term matches
+ if q.lower() in WATCH_LIST:
+ notification = SearchNotification(
+ session_id=session_id, query=q, results_count=results_count
+ )
+
+ for connection in connections.copy():
+ try:
+ await connection.send_text(
+ json.dumps(
+ {
+ "type": "search",
+ "session_id": session_id,
+ "query": q,
+ "timestamp": notification.timestamp.isoformat(),
+ }
+ )
+ )
+ except:
+ connections.remove(connection)
+
+ return SearchResponse(query=q, results=results, total=results_count)
+
+ except Exception as e:
+ status_code = getattr(e, "status_code", 500)
+ return HTTPException(status_code=status_code, detail=str(e))
+
+
+@app.get("/")
+async def get_main_page():
+ return FileResponse("test.html")
+
+
+if __name__ == "__main__":
+ uvicorn.run(app, host="0.0.0.0", port=8000)
diff --git a/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/products.ndjson b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/products.ndjson
new file mode 100644
index 00000000..605af749
--- /dev/null
+++ b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/products.ndjson
@@ -0,0 +1,25 @@
+{"product_name": "iPhone 15 Pro", "price": 999.99, "description": "Latest flagship smartphone with titanium design, A17 Pro chip, and advanced camera system"}
+{"product_name": "MacBook Pro 14-inch", "price": 1999.99, "description": "Professional laptop with M3 chip, Liquid Retina XDR display, and up to 22-hour battery life"}
+{"product_name": "AirPods Pro 2nd Gen", "price": 249.99, "description": "Wireless earbuds with active noise cancellation, spatial audio, and USB-C charging case"}
+{"product_name": "iPad Air", "price": 599.99, "description": "Versatile tablet with M1 chip, 10.9-inch Liquid Retina display, and Apple Pencil support"}
+{"product_name": "Apple Watch Series 9", "price": 399.99, "description": "Advanced smartwatch with health monitoring, fitness tracking, and always-on Retina display"}
+{"product_name": "Samsung Galaxy S24 Ultra", "price": 1199.99, "description": "Premium Android smartphone with S Pen, 200MP camera, and AI-powered features"}
+{"product_name": "Dell XPS 13", "price": 1299.99, "description": "Ultra-portable laptop with Intel Core i7, 13.4-inch InfinityEdge display, and premium build"}
+{"product_name": "Sony WH-1000XM5", "price": 399.99, "description": "Premium noise-canceling headphones with 30-hour battery and exceptional audio quality"}
+{"product_name": "Nintendo Switch OLED", "price": 349.99, "description": "Hybrid gaming console with vibrant OLED screen, enhanced audio, and portable design"}
+{"product_name": "Kindle Paperwhite", "price": 139.99, "description": "E-reader with 6.8-inch glare-free display, waterproof design, and weeks of battery life"}
+{"product_name": "Google Pixel 8 Pro", "price": 999.99, "description": "AI-powered smartphone with advanced computational photography and 7 years of updates"}
+{"product_name": "Microsoft Surface Pro 9", "price": 1099.99, "description": "2-in-1 laptop tablet with Intel Core processors, detachable keyboard, and Surface Pen support"}
+{"product_name": "Dyson V15 Detect", "price": 749.99, "description": "Powerful cordless vacuum with laser dust detection and intelligent suction adjustment"}
+{"product_name": "Fitbit Versa 4", "price": 199.99, "description": "Health and fitness smartwatch with GPS, heart rate monitoring, and 6+ day battery"}
+{"product_name": "Bose QuietComfort 45", "price": 329.99, "description": "Wireless noise-canceling headphones with balanced sound and 24-hour battery life"}
+{"product_name": "Tesla Model Y", "price": 47190.00, "description": "Electric SUV with autopilot, 330-mile range, and minimalist interior design"}
+{"product_name": "Instant Pot Duo 7-in-1", "price": 99.99, "description": "Multi-use pressure cooker that replaces 7 kitchen appliances with smart programming"}
+{"product_name": "LG OLED C3 55-inch TV", "price": 1499.99, "description": "4K OLED smart TV with perfect blacks, vibrant colors, and gaming-optimized features"}
+{"product_name": "Vitamix A3500", "price": 549.99, "description": "Professional-grade blender with preset programs, self-cleaning, and 10-year warranty"}
+{"product_name": "Herman Miller Aeron Chair", "price": 1395.00, "description": "Ergonomic office chair with breathable mesh, lumbar support, and 12-year warranty"}
+{"product_name": "Canon EOS R5", "price": 3899.99, "description": "Professional mirrorless camera with 45MP sensor, 8K video, and advanced autofocus"}
+{"product_name": "Sonos Arc Soundbar", "price": 899.99, "description": "Premium soundbar with Dolby Atmos, voice control, and seamless music streaming"}
+{"product_name": "Peloton Bike+", "price": 2495.00, "description": "Interactive exercise bike with rotating HD touchscreen and live fitness classes"}
+{"product_name": "Roomba j7+", "price": 849.99, "description": "Smart robot vacuum with object avoidance, self-emptying base, and app control"}
+{"product_name": "KitchenAid Stand Mixer", "price": 379.99, "description": "Iconic stand mixer with 10-speed control, tilt-head design, and multiple attachments"}
\ No newline at end of file
diff --git a/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/requirements.txt b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/requirements.txt
new file mode 100644
index 00000000..ebeb6519
--- /dev/null
+++ b/supporting-blog-content/building-elasticsearch-apis-with-fastapi-websockets/requirements.txt
@@ -0,0 +1,5 @@
+fastapi==0.104.1
+uvicorn==0.24.0
+websockets==11.0.3
+pydantic==2.5.0
+elasticsearch==8.11.0
\ No newline at end of file