SaaS (Software as a Service) apps are everywhere — Netflix, Dropbox, Notion — and you can build your own even as a beginner. In this tutorial, we’ll build a simple SaaS web app in Python using Flask. You’ll get:
- Login & signup system
- Free vs Pro plan logic
- Protected SaaS feature
- HTML Templates
- Full Python code
Project Overview
We’ll build a Text Summarizer SaaS:
- Free users can summarize 1 text per day
- Pro users get unlimited summaries
- Upgrade button available
Project Structure
saas-app/
│
├── app.py
├── models.py
├── templates/
│ ├── base.html
│ ├── home.html
│ ├── signup.html
│ ├── login.html
│ ├── dashboard.html
│ ├── upgrade.html
Step 1 – Install Dependencies
mkdir saas-app
cd saas-app
pip install flask flask-login flask-bcrypt
models.py
from flask_login import UserMixin
from flask_bcrypt import generate_password_hash, check_password_hash
# In-memory database simulation
users = []
usage = {}
class User(UserMixin):
def __init__(self, id, email, password, plan="free"):
self.id = id
self.email = email
self.password_hash = generate_password_hash(password).decode('utf-8')
self.plan = plan
def check_password(self, password):
return check_password_hash(self.password_hash, password)
app.py
from flask import Flask, render_template, request, redirect, url_for
from flask_login import LoginManager, login_user, login_required, logout_user, current_user
from models import User, users, usage
app = Flask(__name__)
app.secret_key = "supersecretkey"
login_manager = LoginManager(app)
login_manager.login_view = "login"
@login_manager.user_loader
def load_user(user_id):
return users[int(user_id)]
@app.route("/")
def home():
return render_template("home.html")
@app.route("/signup", methods=["GET", "POST"])
def signup():
if request.method == "POST":
email = request.form["email"]
password = request.form["password"]
user = User(len(users), email, password)
users.append(user)
login_user(user)
return redirect(url_for("dashboard"))
return render_template("signup.html")
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
email = request.form["email"]
password = request.form["password"]
for user in users:
if user.email == email and user.check_password(password):
login_user(user)
return redirect(url_for("dashboard"))
return render_template("login.html")
@app.route("/logout")
@login_required
def logout():
logout_user()
return redirect(url_for("home"))
@app.route("/dashboard")
@login_required
def dashboard():
return render_template("dashboard.html", plan=current_user.plan)
@app.route("/summarize", methods=["POST"])
@login_required
def summarize():
text = request.form["text"]
user_id = current_user.id
if current_user.plan == "free":
usage[user_id] = usage.get(user_id, 0)
if usage[user_id] >= 1:
return redirect(url_for("upgrade"))
usage[user_id] += 1
summary = text[:50] + "..."
return render_template("dashboard.html", plan=current_user.plan, summary=summary)
@app.route("/upgrade")
@login_required
def upgrade():
return render_template("upgrade.html")
if __name__ == "__main__":
app.run(debug=True)
HTML Templates
Create a folder called templates
and add the files below.
templates/base.html
<!DOCTYPE html>
<html>
<head>
<title>Python SaaS Example</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body class="bg-light">
<nav class="navbar navbar-dark bg-dark px-3">
<a class="navbar-brand" href="/">Mini SaaS</a>
{% if current_user.is_authenticated %}
<a class="btn btn-danger" href="/logout">Logout</a>
{% else %}
<a class="btn btn-success" href="/login">Login</a>
{% endif %}
</nav>
<div class="container mt-4">
{% block content %}{% endblock %}
</div>
</body>
</html>
🏠 templates/home.html
{% extends "base.html" %}
{% block content %}
<h2>Welcome to Mini SaaS App</h2>
<p>Create summaries and upgrade anytime!</p>
<a href="/signup" class="btn btn-primary">Get Started</a>
{% endblock %}
✍️ templates/signup.html
{% extends "base.html" %}
{% block content %}
<h2>Sign Up</h2>
<form method="POST">
<input class="form-control mb-2" name="email" placeholder="Email" required>
<input class="form-control mb-2" type="password" name="password" placeholder="Password" required>
<button class="btn btn-primary">Sign Up</button>
</form>
{% endblock %}
🔐 templates/login.html
{% extends "base.html" %}
{% block content %}
<h2>Log In</h2>
<form method="POST">
<input class="form-control mb-2" name="email" placeholder="Email" required>
<input class="form-control mb-2" type="password" name="password" placeholder="Password" required>
<button class="btn btn-success">Log In</button>
</form>
{% endblock %}
📊 templates/dashboard.html
{% extends "base.html" %}
{% block content %}
<h2>Dashboard</h2>
<p>Your plan: <strong>{{ plan }}</strong></p>
<form method="POST" action="/summarize">
<textarea name="text" class="form-control mb-2" placeholder="Enter text to summarize" required></textarea>
<button class="btn btn-warning">Summarize</button>
</form>
{% if summary %}
<div class="alert alert-success mt-3">
{{ summary }}
</div>
{% endif %}
{% endblock %}
💳 templates/upgrade.html
{% extends "base.html" %}
{% block content %}
<h2>Upgrade to Pro</h2>
<p>Free users can only summarize once. Go Pro for unlimited usage!</p>
<button class="btn btn-success">Upgrade Now (Stripe Coming Soon)</button>
{% endblock %}
✅ Run the App
python app.py
Open: http://127.0.0.1:5000