Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deploying an Angular 4 frontend with a Django Rest Framework backend on Heroku

I have built the front end using Angular 4 (using the angular CLI) and the backend using Django and the Django Rest Framework.

The development environment is set up in such a way that the static assets, HTML,JS etc are all part of the Angular app. The angular app runs on a server spun up by the CLI at localhost:4200 and and it communicates with the rest framework backend through a series of HTTP calls through CORS to the DRF API which is live at another localhost:8000 only to obtain and serve information.

How do I go about deploying this as a production-ready application on Heroku? The heroku guides illustrate how to deploy a Django app separately or an Angular app separately (running on a node server). Do i deploy them as two separate instances or do I combine them. If so , how do I go about doing that?

like image 704
user8587692 Avatar asked Sep 10 '17 13:09

user8587692


People also ask

Can Django and Angular be used together?

Yes, creating API's using Django/DRF is sufficient to communicate with an Angular app. Below is an illustration of a basic architecture of a Django Angular application. API endpoints in Django will be served via the urls.py files using the DRF (Django Rest Framework.

Can heroku host Angular app?

As I mentioned earlier, Heroku offers support for NodeJS, so we can use Express to serve up our Angular application. Let's start by adding Express and Path to our project: npm i express path .


1 Answers

This solution doesn't match the request of how to do this on Heroku, but is done at request of @pdfarhad.

So first about what I used to host, my domain name was registered with Godaddy, and that pointed to a server (droplet) I created at Digitalocean. Noting that the DNS servers on godaddy were pointing to digitaloceans, ns1.digitalocean.com, ns2.digitalocean.com and ns3.digitalocean.com. Then on digitalocean, on the networking tab, created two A records that both point to the server I created, one being example.com, and the other being api.example.com.

With that done, when you create a new droplet, the password for the root user will be emailed, then do:

# ssh root@<IPADDRESSOFSERVER>
# sudo apt-get update
# sudo adduser jupiar
# sudo usermod -aG sudo jupiar
# su - jupiar
$ sudo apt-get install nginx

At this point, you should be able to navigate to your IPADDRESS and see the nginx landing page, you might have to wait a while for godaddy and digitalocean to pass typing example.com, I think half a day passed until the name servers were all synced up and stuff.

Now, I just set up passwordless ssh, on my local machine:

$ ssh-keygen (no passphrase)
$ cat ~/.ssh/id_rsa.pub (then copy this)

And now on the server:

$ mkdir ~/.ssh
$ chmod 700 ~/.ssh
$ nano ~/.ssh/authorized_keys (paste the rsa you copied)
$ chmod 600 ~/.ssh/authorized_keys
$ sudo nano /etc/ssh/sshd_config
    | Make sure:
    | PasswordAuthentication no
    | PubkeyAuthentication yes
    | ChallengeResponseAuthentication no
$ sudo systemctl reload sshd
$ exit

Now you should be able to ssh into your server without a password.

I use anaconda python because I do alot of datascience, so:

$ wget https://repo.continuum.io/archive/Anaconda3-5.0.0.1-Linux-x86_64.sh
$ bash https://repo.continuum.io/archive/Anaconda3-5.0.0.1-Linux-x86_64.sh
    | installed to /home/jupiar/anaconda3, auto append path to .bashrc
$ . .bashrc
$ source .bashrc

Now, because in the past I had some trouble with running linux supervisor with python virtualenvs, I just install everything globally. uwsgi is mostly in C, so you need some packages so as to compile it.

$ sudo apt-get install build-essential python-dev python3-dev
$ pip install uwsgi

Now, making a git repository, you can make one on the server, but i prefer to use github, because it provides alot of useful tools, and a good place to collaborate between contributers on the project, so make a private repository on github, then on your local machine: (the third line is because I use Mac)

MAKE A NEW FOLDER, and put everything in there

$ echo "# example" >> README
$ git init
$ find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch
$ echo ".DS_Store" >> .gitignore
$ git add .
$ git commmit -m "first commit"
$ git remote add origin https://github.com/<GITUSERNAME>/<REPONAME>.git
$ git push -u origin master

so, again on the local machine, set up a new angular 4 app with scss styling and skipping making it a git repo, because it wont show properly on github, repo inside repo etc... :

$ ng new frontend --style=scss --skip-git

And, we will keep the app as it is for now, just remember the server will need a dist folder, and you can do something like this in the frontend folder, using ahead-of-time...:

$ ng build --aot -prod

Now on local machine again, create the Django Rest Framework backend:

$ conda create -n exampleenv python=3.6 anaconda
$ source activate exampleenv
(exampleenv)$ pip install django
(exampleenv)$ pip install djangorestframework
(exampleenv)$ pip install django-cors-headers
(exampleenv)$ django-admin startproject backend
(exampleenv)$ cd backend
(exampleenv)$ django-admin startapp api
(exampleenv)$ python manage.py migrate
(exampleenv)$ python manage.py createsuperuser

Now, just to get a minimal rest framework to work, we need to:

In settings.py add to INSTALLED APPS:

'rest_framework',
'corsheaders',
'api',

In settings.py add to top of MIDDLEWARE:

'corsheaders.middleware.CorsMiddleware',

In settings.py add:

CORS_ORIGIN_ALLOW_ALL = True

In settings.py add:

ALLOWED_HOSTS = ['<DIGITALOCEANSERVER-IP>', '*', 'localhost', '127.0.0.1']

in backend/urls.py:

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/', include('api.urls')),
]

in api/models.py:

from django.db import models

# Create your models here.

class testModel(models.Model):
    word = models.CharField("word", max_length=20)

in api/serializers.py:

from rest_framework import serializers
from .models import testModel

class testSerializer(serializers.ModelSerializer):
    word = serializers.CharField(max_length=20)

    class Meta: 
        model = testModel
        fields = ('id', 'word')

in api/urls.py:

from django.conf.urls import url, include
from rest_framework.urlpatterns import format_suffix_patterns
from .views import testView

urlpatterns = [ 
    url(r'^test/', testView.as_view()),
]

in api/views.py:

from rest_framework import generics
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from django.shortcuts import render
from .models import testModel
from.serializers import testSerializer

# Create your views here.

class testView(APIView):
    def get(self, request):
        return Response({'succeded?', 'yes'})

Now with all that saved, it should work if you:

(exampleenv)$ python manage.py runserver
navigate to :8000/api/test/

With both an angular 4 frontend and DRF backend done, we can push the changes to github, by now that will be a LOT of changes ;)

Now, lets get our frontend and backend onto the server, log into the server and good idea now is to add the ssh keys from your server (and local machine) if you havnt already, to github. On the server we can do this via:

$ ssh-keygen -t rsa -b 4096 -C "<EMAIL ADDRESS>"
$ eval "$(ssh-agent -s)"
Agent pid ....
$ ssh-add ~/.ssh/id_rsa
    Enter passphrase for /home/jupair/.ssh/id_rsa:
    Identity added: /home/jupiar/.ssh/id_rsa (/home/jupiar/.ssh/id_rsa)

$ mkdir example
$ cd example
$ git init
$ git pull [email protected]:<GITHUBUSERNAME>/<REPONAME>.git

(At some point here, you might have errors with using https or ssh) if you are using ssh keys, you need to use the ssh repo name from github (on github, the clone or download button will give you these links), and on the local machine too you may need to set-url to use the ssh name):

[on local machine]
$ git remote set-url origin [email protected]:<GITHUBUSERNAME>/<REPONAME>.git

Okay, now on your server, you should have the frontend and backend folders in your project folder, angular is very easy, on the server:

$ cd /etc/nginx/sites-available
$ sudo rm default
$ sudo nano frontend.conf

and place something like this inside:

server {
        listen 80;
        listen [::]:80;

        root /home/jupiar/example/frontend/dist;
        index index.html index.htm index.nginx-debian.html;

        server_name example.com;

        location / {
                try_files $uri $uri/ =404;
        }
}

Now we need to link that file into sites enabled:

$ sudo ln -s /etc/nginx/sites-available/frontend.conf /etc/nginx/sites-enabled/frontend.conf
$ cd /etc/nginx/sites-enabled
$ sudo rm default
$ sudo systemctl restart nginx.service

Heading over to example.com we should now see the angular app working,

Okay, now to make the backend servable, is a little bit more tricky:

$ cd /etc/nginx/sites-available
$ sudo nano backend.conf

and place something like this:

server {
    listen 80;
    listen [::]:80;

    server_name api.example.com;

    location /static/admin {
        alias /home/jupair/anaconda3/lib/python3.6/site-packages/django/contrib/admin/static/admin;
    }

    location /static/rest_framework {
        alias /home/jupiar/anaconda3/lib/python3.6/site-packages/rest_framework/static/rest_framework;
    }

    location / {
        proxy_pass http://127.0.0.1:9000/;
        proxy_set_header    Host                $host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $remote_addr;
        proxy_set_header    X-Forwarded-Proto   $scheme;
        proxy_redirect      off;

    }
}

Now, link that file to sites-enabled:

$ sudo ln -s /etc/nginx/sites-available/backend.conf /etc/nginx/sites-enabled/backend.conf

Now to set up superviser to use uwsgi to start the django app

$ sudo apt-get install supervisor
$ sudo nano /etc/supervisor/conf.d/backend_api.conf

And inside, have something like:

[program:backend_api]
command = /home/jupiar/anaconda3/bin/uwsgi --http :9000 --wsgi-file /home/jupiar/example/backend/backend/wsgi.py
directory = /home/jupiar/example/backend
user = jupiar
autostart = true
autorestart = true
stdout_logfile = /var/log/backnd_api.log
stderr_logfile = /var/log/backend_api_err.log

Now, you will need to run:

$ sudo supervisorctl reread
$ sudo supervisorctl update
$ sudo supervisorctl restart backend_api
$ sudo systemctl restart nginx.service

Now, heading over to api.example.com/api/test/ should give you the django rest framework response of suceeded: true.

From now on, you can just use a custom shell script whenever you want to make changes live, that would be like:

cd /home/jupiar/example
git reset --hard (sometimes you may need to run this)
git pull [email protected]/<GITUSERNAME>/<PROJECTNAME>.git
sudo supervisorctl restart backend_api
sudo systemctl restart nginx.service

And thats pretty much it, I believe everything is there from how I remember doing it, any questions or if something is wrong/doesn't work for you, please comment and let me know :)

like image 189
jupiar Avatar answered Oct 08 '22 01:10

jupiar