title: Opening a WebSocket connection between Angular 2+ client and Django backend
body: <p>I am trying to open a websocket connection from my angular 2+ app with my Django backend using Django Channels.
  I went through this tutorial: <a href="https://www.youtube.com/watch?v=RVH05S1qab8" rel="nofollow noreferrer">https://www.youtube.com/watch?v=RVH05S1qab8</a>
  and managed to get everything working with the javascript portion written inline in a Django html template. But I am having issues simply opening a websocket connection when I migrated the front-end chat form to a separate angular 2 app.
  Both font-end and backends are hosted locally.</p>
  
  <p><strong>Front End - Chat Form Template</strong></p>
  
  <pre><code>&lt;div *ngFor="let message of thread.messages"&gt;
    &lt;div&gt;{{message.message}}&lt;/div&gt;
  &lt;/div&gt;
  
  &lt;form #formData [formGroup]="form"&gt;
    &lt;div class="form-group"&gt;
      &lt;input formControlName="messageInput" #msgInput type="text" class="form-control" id="newMessage" placeholder="Type a message"&gt;
    &lt;/div&gt;
    &lt;button (click)="onSubmit($event)" type="submit" class="btn btn-primary"&gt;Send&lt;/button&gt;
  &lt;/form&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p><strong>Front End - Chat Form Component</strong></p>
  
  <pre><code>  otherUser: User;
    me: User;
    threadId: number;
    thread: Thread;
    endpoint: string;
    socket: WebSocket;
  
    form = new FormGroup({
      messageInput: new FormControl("")
    });
  
    get messageInput() {
      return this.form.get('messageInput');
    }
  
  
    getThreadById(id: number) {
      this._chatService.getThreadById(id).subscribe(thread =&gt; {
        console.log("response: thread found", thread);
        this.thread = thread;
      },
        error =&gt; {
          console.log("error", error);
        });
    }
  
    getEndpoint() {
      let loc = window.location
      let wsStart = "ws://"
      if (loc.protocol == "https:") {
        wsStart = 'wss://'
      }
      this.endpoint = wsStart + loc.host + loc.pathname;
      return this.endpoint;
    }
  
  
    constructor(private _activatedRoute: ActivatedRoute) {
    }
  
    ngOnInit() {
      this._activatedRoute.params.subscribe(params =&gt; { this.threadId = params['id']; });
      if (this.threadId) {
        this.getThreadById(this.threadId);
      }
      this.getEndpoint();
      this.createSocket();
    }
  
    createSocket() {
      this.socket = new WebSocket(this.endpoint);
      console.log("endpoint ", this.endpoint);
  
      this.socket.onopen = (e) =&gt; {
        console.log("open", e);
      }
  
      this.socket.onmessage = (e) =&gt; {
        console.log("message", e);
        let chatDataMsg = JSON.parse(e.data);
        this.thread.messages.push(chatDataMsg.message);
      }
  
      this.socket.onerror = (e) =&gt; {
        console.log("error", e);
      }
  
      this.socket.onclose = (e) =&gt; {
        console.log("close", e);
      }
  
    }
  
    onSubmit($event) {
      let msgText = this.messageInput.value;
      let finalData = {
        'message': msgText
      };
      let chatMessage: ChatMessage = new ChatMessage(this.thread, this.me, new Date(), msgText);
      this.socket.send(JSON.stringify(finalData))
      this.form.reset();
    }
  </code></pre>
  
  <p><strong>Django Backend Project Settings</strong></p>
  
  <pre><code>ASGI_APPLICATION = "joole.routing.application"
  
  CHANNEL_LAYERS = {
      "default": {
          "BACKEND": "channels_redis.core.RedisChannelLayer",
          "CONFIG": {
              "hosts": [("localhost", 6379)],
              #"hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')]
          },
      },
  }
  
  
  ALLOWED_HOSTS = ['joole-api.herokuapp.com', '.herokuapp.com', '127.0.0.1']
  </code></pre>
  
  <p><strong>Django Websocket Routing</strong></p>
  
  <pre><code>from channels.routing import ProtocolTypeRouter, URLRouter
  from django.conf.urls import url
  from channels.auth import AuthMiddlewareStack
  from channels.security.websocket import AllowedHostsOriginValidator, OriginValidator
  from chat.consumers import ChatConsumer
  
  application = ProtocolTypeRouter({
      'websocket': AllowedHostsOriginValidator(
          AuthMiddlewareStack(
              URLRouter(
                  [
                      url(r"^messages/(?P&lt;id&gt;[\w.@+-]+)/$", ChatConsumer)
                  ]
              )
          )
      )
  })
  </code></pre>
  
  <p><strong>Django Project Urls</strong></p>
  
  <pre><code>from django.urls import path, include
  from django.conf import settings
  
  urlpatterns = [
      path('messages/', include('chat.urls')),
  ]
  </code></pre>
  
  <p><strong>Django WebSocket Consumer</strong></p>
  
  <pre><code>import asyncio
  import json
  from users.models import CustomUser, Employee
  from channels.consumer import AsyncConsumer
  from channels.db import database_sync_to_async
  from .models import Thread, ChatMessage
  
  
  class ChatConsumer(AsyncConsumer):
  
      async def websocket_connect(self, event):
          print("CONNECTED", event)
  
          thread_id = self.scope['url_route']['kwargs']['id']
          thread_obj = await self.get_thread(thread_id)
          self.thread_obj = thread_obj
          chat_room = f"thread_{thread_obj.id}"
          self.chat_room = chat_room
          await self.channel_layer.group_add(
              chat_room,
              self.channel_name
          )
          await self.send({
              "type": 'websocket.accept'
          })
  
      async def websocket_receive(self, event):
          print("receive", event)
          front_text = event.get('text', None)
          if front_text is not None:
              loaded_dict_data = json.loads(front_text)
              msg = loaded_dict_data.get('message')
              user = self.scope['user']
              username = 'default'
              if user.is_authenticated:
                  username = user.email
              myResponse = {
                  'message': msg,
                  'username': user.email
              }
              await self.create_chat_message(user, msg)
              # broadcasts the message event to be sent
              await self.channel_layer.group_send(
                  self.chat_room,
                  {
                      "type": "chat_message",
                      "text": json.dumps(myResponse)
                  }
              )
  
      async def chat_message(self, event):
          # send the actual message event
          print("message", event)
          await self.send({
              "type": "websocket.send",
              "text": event["text"]
          })
  
      async def websocket_disconnect(self, event):
          print("disconnected", event)
  
      @database_sync_to_async
      def get_thread(self, id):
          return Thread.objects.get(id=id)
  
      @database_sync_to_async
      def create_chat_message(self, me, msg):
          thread_obj = self.thread_obj
          return ChatMessage.objects.create(thread=thread_obj, user=me, message=msg)
  
  </code></pre>
  
  <p><strong>Django Chat Models</strong></p>
  
  <pre><code>class Thread(models.Model):
      first = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_first')
      second = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_second')
      updated = models.DateTimeField(auto_now=True)
      timestamp = models.DateTimeField(auto_now_add=True)
  
      @property
      def room_group_name(self):
          return f'chat_{self.id}'
  
      def broadcast(self, msg=None):
          if msg is not None:
              broadcast_msg_to_chat(msg, group_name=self.room_group_name, user='admin')
              return True
          return False
  
  class ChatMessage(models.Model):
      thread = models.ForeignKey(Thread, null=True, blank=True, on_delete=models.SET_NULL, related_name="messages")
      user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='sender', on_delete=models.CASCADE)
      message = models.TextField()
      timestamp = models.DateTimeField(auto_now_add=True)
  </code></pre>
  
  <p><strong>Django Chat Urls</strong></p>
  
  <pre><code>from django.urls import include, path
  from rest_framework import routers
  from .views import ThreadViewSet, ChatMessageViewSet
  
  router = routers.DefaultRouter()
  router.register('thread', ThreadViewSet)
  router.register('chatMessage', ChatMessageViewSet)
  
  urlpatterns = [
      path('', include(router.urls)),
  ]
  </code></pre>
  
  <p><strong>Expected Output</strong>
  I was expecting the "CONNECTED" message with the event information printed on the console according to the websocket_connect method in the consumers class. Similarly a "open" message with the event fired in my browser's console from the Angular 2+ chat.component.ts.</p>
  
  <p><strong>Actual Output</strong></p>
  
  <p>I immediately get this warning message on the browser console:</p>
  
  <p><em>WebSocket connection to 'ws://localhost:4200/sockjs-node/344/ux0z32ma/websocket' failed: WebSocket is closed before the connection is established.</em></p>
  
  <p>After about 2 minutes of waiting...
  <a href="https://i.stack.imgur.com/8cOqa.jpg" rel="nofollow noreferrer">Attached image shows what is automatically output on the console.</a></p>