When you are a founder, CEO, CTO or other stakeholders for a startup company and you are in charge of developing a software product, to be specific, a web application product. The first challenge you face is to choose the proper technoloy stack for the product. A good choice can lead the product to success. However, most non-technical founders are puzzled when presented with so many technology choices for developing their ideas. And they come with a fear that committing to the wrong language or framework will have serious consequences down the road. Today we will talk about one of the web technoloy stacks which is pretty suitable for startups.
So how can we decide whether the tecnholoy stack is the proper one?
From the product perspetive, we want:
- time: the product can be delivered on time
- cost: the product should be delivered within budget
- quality: the product should maintain high quality
- scope: in fact, we want to develop as many features as possible in a short time as we are startups
From the developer perspective, to acheieve the goal above, a powerful developer community will be required.
The modern web application is mainly divided into 2 parts:
- client side: the frontend for the application, could be a web app, mobile app or a desktop app
- server side: the backend for the application, host the application, deal with the request and data
The client side and backend side communicate via APIs.
Here are some popular technology stack.
-
Frontend technology:
- basic: html, css, javascript
- framework: bootstrap(css), vue(js), angular(js), react(js)
-
Backend technology:
- language: javascript, python, ruby, java, .net, php
- framework: Express(js), Django(python), Rail on ruby(ruby), Spring(java), ASP.NET(.net), Laravel(php)
- database: MySQL, PostgreSQL, MongoDB
As we have said, today we will talk about the technology stack choice: Django + React
- Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. It is released in 2005.
- React is a JavaScript library for building user interfaces, backed by Facebook. It is released in 2013.
The 2 framework both are open source and have powerful developer communities. There is a lot of tools and library to use. You can develop and deploy a tested high quality application quickly with them.
- Backend: Django + Django REST Framework
- Frontend:
- React: Web app
- React Native: Mobile app
- Electron: Destkop app
- Database:
- MySQL/PostgreSQL/MongoDB
- Deployment:
- Docker to develop
- Heroku to deploy
- Continus Integration/Continue Delivery
With the technology stack above, you can make you application online in a day.
- create the proejct directory
mkdir shedafullstackdemo
cd ./shedafullstackdemo
mkidr backend
- environment requirement: python3, pip3
- Install virtualenv and create a virtual environment. Virtualenv is used to create a specific virtual env for the proejct. It will make it easier to manage your project.
cd ./backend
pip3 install virtualenv # install package virtualenv
virtualenv --python=python3 env # create virtual environment
source env/bin/activate # activate the env
- Install Django and Create a project
pip3 install django
django-admin startproejct todo .
- Setup Django settings
- seperate requirements for different environment into seperate files, so we can manage the requirements for different environment(dev, staging, production) more easily.
mkdir requirements
pip3 freeze > requirements/base.txt
cd requirements
echo '-r base.txt' > dev.txt
echo '-r base.txt' > prod.txt
echo '-r base.txt' > test.txt
- seperate settings for different environment into seperate files.
cd ./todo
mkdir settings && cd ./settings && touch __init__.py
mv ../settings.py base.py
- create the other 2 setting files for development and production:
dev.py prod.py
- set BASE_DIR for django project to the root directory
- origin
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
- change to
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(PROJECT_DIR)
- origin
- then later you can specifiy the setting for different environment via different file
- to run with specific environment:
python3 manage.py runserver --settings=todo.settings.xxx
- to run with specific environment:
- run the django application and create the superuser
python3 manage.py runserver
python3 manage.py createsuperuser
- then you can check the
http://localhost:80000
- seperate requirements for different environment into seperate files, so we can manage the requirements for different environment(dev, staging, production) more easily.
Django Rest Framework allows you to do rapid development with Django.
- Install Django Rest Framework
pip3 install djangorestframework
- Set up the settings
- add rest framework into
INSTALLED_APPS
INSTALLED_APPS = [ ... 'rest_framework', ]
- install swagger to make the APIs web browserable
pip3 install django-rest-swagger
- add rest_framework_swagger into
INSTALLED_APPS
INSTALLED_APPS = [ ... 'rest_framework-swagger', ] REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', ), 'DEFAULT_AUTHENTICATION': ( 'rest_framework.authentication.SessionAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ), 'COERCE_DECIMAL_TO_STRING': False }
- edit
urls.py
to make it workfrom django.urls import path from rest_framework import routers from rest_framework_swagger.views import get_swagger_view schema_view = get_swagger_view(title='Sheda Full Stack Demo') router = routers.DefaultRouter(trailing_slash=True) urlpatterns = [ ... path('docs/', schema_view), ... ]
- then run the application and check
http://localhost:8000/docs/
, you should be able to check the APIs
- add rest framework into
Django have the in-built test module to make the test very easy
Except the test, we also need to control the code speficition to make sure our code clean, readable and maintainable.
We can use tox
altogether with coverage
, flake8
, isort
, pytest
to reach that.
- Install packages:
pip3 install tox coverage flake8 isort
- add tox, flake8, isort, coverage into
requirements/text.txt
- create configuation files
tox.ini
andsetup.cfg
(check the backend folder) - run the test:
tox
- hint: sometimes when we run tox, it will tell us the library not installed, in fact, that is because the depencies for test need to be reinstalled. we can delete the folder .tox and run
tox
again
- hint: sometimes when we run tox, it will tell us the library not installed, in fact, that is because the depencies for test need to be reinstalled. we can delete the folder .tox and run
Then we setup the frontend part for the demo with react
- Environment requirement
- node, npm
- install react:
npm install -g create-react-app
- init the react project:
create-react-app frontend
- Config structure
- run eject:
cd ./frontend && npm install eject
- it will create the config structure for us
- run eject:
-
Django part
- Install webpack loader:
pip3 install django-webpack-loader
- edit settings:
WEBPACK_LOADER = { 'DEFAULT': { 'BUNDLE_DIR_NAME': 'bundles/', 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.dev.json'), } }
- add template and index page
cd ./backend && mkdir templates
- add templates settings
TEMPLATES = [ { # ... other settings 'DIRS': [os.path.join(BASE_DIR, "templates"), ], # ... other settings }, ]
- add
index.html
in template{% load render_bundle from webpack_loader %} <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" /> <title>Ponynote</title> </head> <body> <div id="root"> </div> {% render_bundle 'main' %} </body> </html>
- config
urls.py
from django.urls import path from django.views.generic import TemplateView from rest_framework import routers urlpatterns = [ # ... path('', TemplateView.as_view(template_name="index.html")) # ... ]
- add production setting in
backend/settings/prod.py
from base import * STATICFILES_DIRS = [ os.path.join(BASE_DIR, "assets"), ] WEBPACK_LOADER = { 'DEFAULT': { 'BUNDLE_DIR_NAME': 'bundles/', 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.prod.json'), } }
- add bundles file to allow production js setting:
mkdir -p assets/bundles
- Install webpack loader:
-
React part
- install
webpack-bundle-tracker
:npm install webpack-bundle-tracker --save-dev
- edit
/config/paths.js
module.exports = { // ... other values statsRoot: resolveApp('../backend'), }
- edit development configuration
- edit
frontend/config/webpack.config.dev.js
- add
const publicPath = 'http://localhost:3000/';
- add
const publicUrl = 'http://localhost:3000/';
- edit module exports part:
const BundleTracker = require('webpack-bundle-tracker'); module.exports = { entry: [ // ... require.resolve('webpack-dev-server/client') + '?http://localhost:3000', require.resolve('webpack/hot/dev-server'), ], plugins: [ new BundleTracker({path: paths.statsRoot, filename: 'webpack-stats.dev.json'}), ], }
- add
- edit
webpackDevServer.conf.js
// ... host: host, headers: { 'Access-Control-Allow-Origin': '*' }, overlay: false, // ...
- edit
- edit production configuration
- edit
frontend/config/paths.js
module.exports = { // ... appBuild: resolveApp('../assets/bundles/'), };
- edit
frontend/config/webpack.config.prod.js
const BundleTracker = require('webpack-bundle-tracker'); const publicPath = "/static/bundles/"; const cssFilename = 'css/[name].[contenthash:8].css'; module.exports = { // ... output: { // ... filename: 'js/[name].[chunkhash:8].js', chunkFilename: 'js/[name].[chunkhash:8].chunk.js', }, module: { // ... rules: [ { oneOf: [ // ... { options: { limit: 10000, name: 'media/[name].[hash:8].[ext]', }, }, { // ... options: { name: 'media/[name].[hash:8].[ext]', }, }, ], }, ], }, plugins: [ // ... new BundleTracker({path: paths.statsRoot, filename: 'webpack-stats.prod.json'}), ], }
- edit
- install
-
run it
- Dev:
python manage.py runserver
yarn start
- Prod:
yarn run build
python manage.py runserver --settings=todo.settings.prod
- Dev:
We we do the development, especially cooperate with others, it will be very annoying to setup the development environment.
As we can see from above, to make it work, we need to install pretty much libraries and packages, and not sure whether it will work in the end or not.
Docker can help use avoid problems like that. After we set up the docker, we only need to run docker-compose up
and then we can start concentrate on the developing, no need to distract by environment problem.
For this application, we need 2 volumes, one is to host postgresql and store data, the other one is to store code. We also need to set up 2 working images for Django and React. Last but not least, we need add .dockerignore to ensure the code is clean.
We can check the docker configuration in the root directory:
- Dockerfile_django: image to host Django
- Dockerfile_react: image to host react
- docker-compose.yml: architecture
- .dockerignore: ignore no need folders and files
We want to be able to continus delivery, so the product can respond to the market feedback quickly. Things will be pretty easy when we use heroku to deploy and host the application.
- Install heroku and login:
- how to install heroku
heroku login
To deploy the Django application via heroku, we will need to change backend settings
- Profile: contain the command to start the application
- runtime.txt: specify the working environment for the application
- requirements.txt: in backend direcotry and will be refered to requirements/prod.txt, heroku can only know the application is a Django application via this file.
- install gunicorn:
pip install gunicorn # used to host the application
- config it:
web: gunicorn todo.wsgi --log-file -
- config the static folder
- install whitenoise:
pip install whitenoise
- add to settings
MIDDLEWARE = [ # ... 'whitenoise.middleware.WhiteNoiseMiddleware', ] STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'assets'), ) STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATIC_URL = '/static/' STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
- install whitenoise:
And then deploy it, the deployment scripts is in the repo root directory.
Our demo link is demo
- Be careful when using the relationship database. As startup, in fact, not really know what the market wants, so we might need to change the data structure frequently. It can not be avoided, so we need to keep this in mind from the beginning of the project.
- MongoDB could be a good choice for a early stage start up as we need to change the data structure frequently.
- All the frontend and backend things are just a tool to make your product can be accessed by the customers. What really matters is the data behind it.