A beginner-friendly URL shortener built with Duck Framework.
Pure Python. No JavaScript. Real-time UI powered by Lively.
Snip is a simple web app that turns long, ugly URLs into short clean links.
You paste a URL → click Shorten → get a short link you can share.
It was built to demonstrate how Duck Framework works in a real project — forms, real-time UI updates, database storage, and redirects — all in pure Python.
- How a Duck project is structured
- How to build interactive UI components using Lively (no JavaScript needed)
- How to use Django's database (ORM) inside a Duck project without enabling full Django mode
- How forms work in Duck using the
Formcomponent - How to handle URL routing and redirects in Duck
Duck is a Python web framework that lets you build full web applications — including interactive, real-time UIs — without writing any JavaScript. The part that handles real-time updates is called Lively.
Think of it like this: instead of writing JavaScript to make a button do something, you write a Python function. Duck takes care of sending the update to the browser for you over a WebSocket connection.
Lively is Duck's built-in real-time UI engine. When you click the "Shorten" button in this app, Lively sends the event to the server, runs your Python handler, and updates only the parts of the page that changed — all without reloading the page.
Django is only being used for one thing — its database engine (the ORM). It handles creating and querying the SQLite database that stores your shortened URLs.
⚠️ Important: You do NOT need to setUSE_DJANGO = Trueinweb/settings.py.
USE_DJANGOtells Duck to run Django as a full backend server and proxy requests to it.
That is not what we are doing here. We are only borrowing Django's database layer.
Duck itself serves all the pages and handles all the requests.
url-shortener/
├── web/
│ ├── main.py # Starts the Duck server
│ ├── settings.py # Duck configuration
│ ├── urls.py # URL routes (/, /s/<code>)
│ ├── views.py # Page and redirect views
│ │
│ ├── ui/
│ │ ├── components/
│ │ │ ├── shorten_form.py # The form with input + button + result
│ │ │ └── stats_bar.py # Live stats (total links, total clicks)
│ │ └── pages/
│ │ └── home.py # The homepage
│ │
│ └── backend/
│ └── django/
│ └── duckapp/
│ ├── core/
│ │ └── models.py # ShortURL database model
│ └── duckapp/
│ └── settings.py # Django database configuration
│
└── etc/
└── ssl/ # SSL certificates (for HTTPS)
| File | What it does |
|---|---|
web/main.py |
Entry point — runs the Duck server |
web/settings.py |
Duck settings — port, debug mode, blueprints etc. |
web/urls.py |
Registers the two routes: / and /s/<short_code> |
web/views.py |
home() renders the page, redirect_short_url() handles redirects |
web/ui/pages/home.py |
Builds the full homepage layout |
web/ui/components/shorten_form.py |
The interactive form component |
web/ui/components/stats_bar.py |
Live counters for total links and clicks |
web/backend/django/duckapp/core/models.py |
The ShortURL database model |
python --versionYou should see something like Python 3.10.x or higher. If not, download Python from python.org.
pip install duckframeworkDuck Framework includes everything you need to run the server.
Django is needed here only for the database. Don't worry about installation, Django is installed automatically when Duck is installed.
You need to have the project locally on your machine. You can do this by cloning the project (or just downloading the project).
**Use the following command to clone the project: **
git clone https://github.com/duckframework/url-shortener.gitNext, navigate to the project:
cd url-shortenerMake sure you are inside the url-shortener folder (the one that contains web/) before running any commands.
Before running the app for the first time, you need to create the database tables. Run these two commands from inside the url-shortener folder:
duck django makemigrations core
duck django migrateWhat just happened?
makemigrations coretold Django to look atweb/backend/django/duckapp/core/models.pyand generate instructions for creating the database table.
migrateactually created the table in the SQLite database file (db.sqlite3).
Inside the project, run:
python web/main.pyOr alternatively:
duck runserverOpen your browser and go to:
http://localhost:8000
You should see the Snip homepage. Paste any URL into the input and click Shorten.
- You type a URL into the input field and click Shorten
- Duck's Lively system sends the form data to the server over a WebSocket — no page reload
- The
handle_shortenmethod inShortenFormruns on the server:- It validates the URL format
- Creates a new
ShortURLrecord in the database with a random 6-character code - Builds the full short URL using
resolve("home")
- Lively updates the result box and stats bar live in the browser
When someone visits http://localhost:8000/s/aB3xZ9:
- Duck matches the route
/s/<short_code>inweb/urls.py redirect_short_url()inviews.pylooks upaB3xZ9in the database- It increments the click counter atomically
- It redirects the visitor to the original URL
The stats bar at the top shows total links shortened and total clicks across all URLs. Every time you shorten a URL, the stats bar refreshes automatically — Lively patches only the number text, nothing else on the page changes.
Right now short URLs look like http://localhost:8000/s/aB3xZ9.
When you deploy this to a real server, update the environment variable like DEBUG, DOMAIN & PORT.
By default, the web/main.py is dynamic, meaning, it can run in production environments. To enter production mode, just set the environment variable DEBUG to nothing.
Use file
deploy.shfor easy deployment.
Note: In this project the base URL is actually resolved dynamically using
resolve("home")inshorten_form.py. If you need to override the domain explicitly, update thehandle_shortenmethod inweb/ui/components/shorten_form.py.
You haven't run the migrations yet. Run these commands:
duck django makemigrations core
duck django migrateDuck is not installed. Run:
pip install duckframeworkThis usually means Django cannot find the core app. Check that INSTALLED_APPS in web/backend/django/duckapp/duckapp/settings.py contains exactly:
"web.backend.django.duckapp.core",Also make sure you are running makemigrations core (with the app name) and not just makemigrations.
Another program is using port 8000. Either stop that program, or change the port in web/main.py:
app = App(port=8080, addr="0.0.0.0", domain="localhost")- Pages extend
Page(or a base page class). You build the layout inon_create()and always callsuper().on_create()first. - Components work the same way — extend a component, override
on_create(), callsuper(), build your children. - Lively event binding uses
.bind("event", handler, update_targets=[...]). Theupdate_targetslist tells Duck which components to re-render after the handler runs. - Forms use the
Formcomponent. Bind to thesubmitevent and your handler receivesform_inputsas a dict. Give each input aname=prop so it appears in that dict. - Routing uses
path()andre_path()inweb/urls.py. Duck supports<param>converters directly inpath(). - Shortcuts — use
duck.shortcutsfor common operations:to_response(),redirect(),not_found404(),resolve(),static().
- Duck Framework — Python web framework with reactive UI
- Django ORM — database layer only
- SQLite — lightweight database, no setup required
This project is part of the Duck Framework showcase. See more at duckframework.xyz.