NelsonLabs
Flask/Project: URL Shortener API

Project: URL Shortener API

Build a URL shortener API β€” a service that takes a long URL, returns a short code, and redirects users who visit the short URL. Clean, practical, and demonstrates the full Flask stack.

URL shortener β€” complete Flask app
python
import string
import random
from flask import Flask, request, jsonify, redirect, abort
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///shortener.db"
db = SQLAlchemy(app)


class Link(db.Model):
    id         = db.Column(db.Integer, primary_key=True)
    code       = db.Column(db.String(10), unique=True, nullable=False)
    long_url   = db.Column(db.Text, nullable=False)
    clicks     = db.Column(db.Integer, default=0)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    def to_dict(self):
        return {
            "code":       self.code,
            "short_url":  f"http://localhost:5000/{self.code}",
            "long_url":   self.long_url,
            "clicks":     self.clicks,
            "created_at": self.created_at.isoformat(),
        }


def generate_code(length=6):
    """Generate a random alphanumeric code."""
    chars = string.ascii_letters + string.digits
    while True:
        code = "".join(random.choices(chars, k=length))
        if not Link.query.filter_by(code=code).first():
            return code


# POST /shorten β€” create a short URL
@app.route("/shorten", methods=["POST"])
def shorten():
    data = request.get_json()
    if not data or "url" not in data:
        return jsonify({"error": "url is required"}), 400

    long_url = data["url"]
    if not long_url.startswith(("http://", "https://")):
        return jsonify({"error": "url must start with http:// or https://"}), 400

    # Use custom code or generate one
    code = data.get("code") or generate_code()
    if Link.query.filter_by(code=code).first():
        return jsonify({"error": "Code already in use"}), 409

    link = Link(code=code, long_url=long_url)
    db.session.add(link)
    db.session.commit()

    return jsonify(link.to_dict()), 201


# GET /<code> β€” redirect to the long URL
@app.route("/<code>")
def redirect_link(code):
    link = Link.query.filter_by(code=code).first_or_404()
    link.clicks += 1
    db.session.commit()
    return redirect(link.long_url)


# GET /stats/<code> β€” get stats for a link
@app.route("/stats/<code>")
def link_stats(code):
    link = Link.query.filter_by(code=code).first_or_404()
    return jsonify(link.to_dict())


if __name__ == "__main__":
    with app.app_context():
        db.create_all()
    app.run(debug=True)

What you learned in this course

  • β€”What Flask is and when to choose it over Django
  • β€”Routing with decorators and URL parameters
  • β€”Jinja2 templates and template inheritance
  • β€”Request and response objects
  • β€”Static file serving
  • β€”Forms with Flask-WTF and validation
  • β€”Database integration with Flask-SQLAlchemy
  • β€”Blueprints for application structure