Logo

dev-resources.site

for different kinds of informations.

Building a Real-Time Flask and Next.js Application with Redis, Socket.IO, and Docker Compose

Published at
12/21/2024
Categories
flask
docker
redis
socketio
Author
aixart
Categories
4 categories in total
flask
open
docker
open
redis
open
socketio
open
Author
6 person written this
aixart
open
Building a Real-Time Flask and Next.js Application with Redis, Socket.IO, and Docker Compose

In this blog, we’ll walk through the process of integrating WebSocket functionality using Socket.IO, adding caching support with Redis, and hosting a full-stack application built with Flask and Next.js using Docker Compose. By the end, you’ll have a scalable, real-time web application architecture ready to deploy.


Tech Stack Overview

  • Backend: Flask with extensions like Flask-SocketIO for real-time communication, Flask-Session for session management, and SQLAlchemy for database interactions.
  • Frontend: Next.js with client-side Socket.IO for real-time content synchronization.
  • Database: PostgreSQL for persistent data storage.
  • Cache: Redis for caching and quick data access.
  • Hosting: Docker Compose for containerized deployment.

Step 1: Backend Setup with Flask and Redis

Flask Backend Structure

Here’s the structure of our Flask backend:

  1. Initialize Flask and Extensions:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_socketio import SocketIO
from flask_session import Session
import redis, os

app = Flask(__name__)
app.config["SECRET_KEY"] = "your_secret_key"
app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql://username:password@postgres:5432/dbname"
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_USE_SIGNER"] = True
app.config["SESSION_KEY_PREFIX"] = "session:"
app.config["SESSION_REDIS"] = redis.StrictRedis(host=os.getenv('REDIS_HOST', 'redis'), port=6379, db=0)

# Initialize extensions
socketio = SocketIO(app, cors_allowed_origins="*")
db = SQLAlchemy(app)
Session(app)
Enter fullscreen mode Exit fullscreen mode
  1. Add Real-Time WebSocket Functionality:
from flask_socketio import emit, join_room, leave_room

@socketio.on('join_room')
def handle_join_room(data):
    room = data.get('contentId')
    join_room(room)
    emit('user_joined', {'msg': 'A new user has joined the room'}, to=room)

@socketio.on('edit_content')
def handle_edit_content(data):
    content_id = data.get('contentId')
    content = data.get('content')
    emit('content_update', {'content': content}, to=content_id, skip_sid=request.sid)

@socketio.on('leave_room')
def handle_leave_room(data):
    room = data.get('contentId')
    leave_room(room)
    emit('user_left', {'msg': 'A user has left the room'}, to=room)
Enter fullscreen mode Exit fullscreen mode
  1. Session and Redis Configuration:
import redis

cache = redis.StrictRedis(host=os.getenv('REDIS_HOST', 'redis'), port=6379, db=0)
Enter fullscreen mode Exit fullscreen mode
  1. Environment Configuration (.env file):
SECRET_KEY=your_secret_key
SQLALCHEMY_DATABASE_URI=postgresql://postgres:postgres@postgres:5432/postgres
SQLALCHEMY_TRACK_MODIFICATIONS=False

APP_URL=AWS EC2 IP address

HOST=redis
Enter fullscreen mode Exit fullscreen mode

Step 2: Frontend Setup with Next.js and Socket.IO

Client-Side Socket.IO Setup

  1. Install Socket.IO Client:
npm install socket.io-client
Enter fullscreen mode Exit fullscreen mode
  1. Create a Socket Instance:
// socket.js
import { io } from "socket.io-client";

const SOCKET_URL = process.env.NEXT_PUBLIC_SOCKET_URL;

const socket = io(SOCKET_URL, {
  autoConnect: false,
});

export default socket;
Enter fullscreen mode Exit fullscreen mode
  1. Integrate TipTap Editor with Real-Time Updates:
"use client";

import socket from "@/shared/socket";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { useEffect, useState } from "react";

const Tiptap = ({ content = "", contentId }) => {
  const [isSaving, setIsSaving] = useState(false);
  const editor = useEditor({
    extensions: [StarterKit],
    content,
    onUpdate: ({ editor }) => {
      const updatedContent = editor.getHTML();
      if (contentId) {
        setIsSaving(true);
        socket.emit("edit_content", { contentId, content: updatedContent });
        setTimeout(() => setIsSaving(false), 1000);
      }
    },
  });

  useEffect(() => {
    if (contentId) {
      if (!socket.connected) socket.connect();
      socket.emit("join_room", { contentId });

      socket.on("content_update", (data) => {
        if (editor && data.content !== editor.getHTML()) {
          editor.commands.setContent(data.content);
        }
      });

      return () => {
        socket.emit("leave_room", { contentId });
        socket.off("content_update");
      };
    }
  }, [contentId, editor]);

  return <EditorContent editor={editor} />;
};

export default Tiptap;
Enter fullscreen mode Exit fullscreen mode
  1. Environment Configuration (.env file):
NEXT_PUBLIC_API_URL=http://localhost:5000/api
NEXT_PUBLIC_SOCKET_URL=http://localhost:5000

DATABASE_NAME=postgres
DATABASE_USER=postgres
DATABASE_PASSWORD=postgres
Enter fullscreen mode Exit fullscreen mode

Step 3: Docker Compose for Deployment

  1. \** Configuration**:
version: "3.8"

services:
  nginx:
    image: nginx:alpine
    container_name: nginx_proxy
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - backend
      - website
    networks:
      - app-network

  website:
    build:
      context: ./website
      dockerfile: Dockerfile
      args:
        NEXT_PUBLIC_SOCKET_URL: ${NEXT_PUBLIC_SOCKET_URL}
    container_name: website
    environment:
      - NEXT_PUBLIC_SOCKET_URL=${NEXT_PUBLIC_SOCKET_URL}
    expose:
      - "3000"
    networks:
      - app-network

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: backend
    expose:
      - "5000"
    networks:
      - app-network

  redis:
    image: redis:alpine
    container_name: redis
    expose:
      - "6379"
    networks:
      - app-network

  postgres:
    image: postgres:14-alpine
    container_name: postgres
    environment:
      - POSTGRES_DB=${DATABASE_NAME}
      - POSTGRES_USER=${DATABASE_USER}
      - POSTGRES_PASSWORD=${DATABASE_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  postgres_data:
Enter fullscreen mode Exit fullscreen mode
  1. Nginx Configuration for Proxy:
server {
  listen 80;
  server_name _;

  location / {
    proxy_pass http://website:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  location /api/ {
    proxy_pass http://backend:5000;
    proxy_set_header Host $host;
  }

  location /socket.io/ {
    proxy_pass http://backend:5000/socket.io/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Running the Application

  1. Build and start the services:
docker-compose up --build
Enter fullscreen mode Exit fullscreen mode
  1. Access the application:
    • Frontend: http://localhost
    • Backend API: http://localhost/api

**GitHub : https://github.com/aixart12/colobrate-editor

flask Article's
30 articles in total
Favicon
Deploy your Flask API on GCP Cloud Run 🚀
Favicon
RESTful GET and POST Requests: A Beginners Guide
Favicon
Flask Routes vs Flask-RESTful Routes
Favicon
Bringing Together Containers & SQL
Favicon
Creating a Local Environment to Operate GCS Emulator from Flask
Favicon
Optimising Flask Dockerfiles: Best Practices for DevOps and Developers
Favicon
A beginners guide to Constraints and Validations in Flask, SQLAlchemy
Favicon
Deploying Flask-based Microservices on AWS with ECS Service Connect
Favicon
FastAPI + Uvicorn = Blazing Speed: The Tech Behind the Hype
Favicon
CRUD With Flask And MySql #2 Prepare
Favicon
CRUD With Flask And MySql #1 Introduction
Favicon
Building an Anemia Detection System Using Machine Learning đźš‘
Favicon
Como usar WebSockets em Flask (How to use WebSockets in Flask)
Favicon
Setup Celery Worker with Supervisord on elastic beanstalk via .ebextensions
Favicon
How to create a simple Flask application
Favicon
Flask
Favicon
Building and Testing the Gemini API with CI/CD Pipeline
Favicon
Crossing the Line before the Finish Line. Also the line before that.
Favicon
Mastering Python Async IO with FastAPI
Favicon
Webinar Sobre Python e InteligĂŞncia Artificial Gratuito da Ebac
Favicon
Is Flask Dead? Is FastAPI the Future?
Favicon
422 Error with @jwt_required() in Flask App Deployed on VPS with Nginx
Favicon
WSGI vs ASGI: The Crucial Decision Shaping Your Web App’s Future in 2025
Favicon
Building a Real-Time Flask and Next.js Application with Redis, Socket.IO, and Docker Compose
Favicon
Carla Simulator 2 : Welcome to the Ride 🚗🏍️
Favicon
Python: A Comprehensive Overview in One Article
Favicon
Understanding Authentication: Session-Based vs. Token-Based (and Beyond!)
Favicon
Building RESTful APIs with Flask
Favicon
Validatorian
Favicon
LumaFlow

Featured ones: