DEV Community

Daisy Auma
Daisy Auma

Posted on

The Python Web Stack You've Never Heard Of: Building Apps Without Frontend Code

I recently interviewed with Anvil, and despite not getting the role, I found what they do to be really cool. As a Python developer, I’ve hit the same wall countless times: I can wrangle data, build ML models, automate workflows, but the moment someone says “put a web UI on that,” I’m stuck learning React, wrestling with JavaScript tooling, and coordinating between frontend and backend.

Anvil takes a contrarian approach: what if you could write your entire web application including the client-side code in Python? No JavaScript required. But here’s the technical puzzle: browsers only understand JavaScript. So how does this actually work?

Architecture Overview

Anvil’s stack consists of four main layers that work together to enable full-stack Python development:

Client Layer: Python code that runs in the browser, compiled to JavaScript via Skulpt 
Server Layer: Standard Python with full ecosystem access, running on Anvil’s servers 
Communication: RPC over WebSockets connecting client and server seamlessly 
Database: Built-in PostgreSQL (Data Tables) or connect to external databases

There’s also an optional fifth component called the Uplink, which lets you connect local Python scripts or Jupyter notebooks to your web app (more on this later).

Anvil architecture

The key innovation here is a unified language across the entire stack. No context switching between Python and JavaScript, no REST API boilerplate, no separate frontend and backend codebases.

The Technical Magic: Client-Side Python

Here’s the fundamental problem: web browsers only execute JavaScript. For decades, the solution has been clear: learn JavaScript frameworks, coordinate between frontend and backend teams, manage two entirely different codebases.

Anvil’s solution relies on Skulpt, a JavaScript implementation of Python that runs in the browser. When you write Python code for the client-side, it gets compiled to JavaScript at runtime. This means your Python code is actually executing in the browser, handling events, manipulating the DOM, and updating the UI.

Here’s what this looks like in practice:

# This Python code runs IN THE BROWSER
from anvil import *

class MyForm(MyFormTemplate):
  def button_click(self, **event_args):
    # Client-side event handling in Python
    alert("Hello from the browser!")

    # Call server seamlessly (looks like a regular function call)
    result = anvil.server.call('process_data', self.text_box.text)
    self.label.text = result
Enter fullscreen mode Exit fullscreen mode

This is genuinely running as Python in your browser. The button\_click method handles a UI event, displays an alert, calls a server function, and updates a label, all without writing a single line of JavaScript.

Benefits:

  • Familiar Python syntax and patterns with no mental context switching

  • Seamless client-server communication where RPC calls look like regular Python functions

  • Unified codebase with one language and one set of conventions

  • Rapid development without frontend/backend coordination overhead

The crucial insight is understanding where code actually runs. UI event handlers execute in the browser (Python via Skulpt), but heavy data processing, machine learning, and pandas operations run on the server in native Python. Database queries happen server-side. The architecture naturally guides you toward the right separation of concerns.

Real Code Example

Let’s build a mini data dashboard that demonstrates how everything fits together:

# === SERVER MODULE (server.py) ===
import pandas as pd
import plotly.express as px
import anvil.server

@anvil.server.callable
def analyze_sales(uploaded_file):
  # Full Python ecosystem available here
  df = pd.read_csv(uploaded_file)
  df = df.groupby('category')['revenue'].sum().reset_index()

  fig = px.bar(df, x='category', y='revenue', 
               title='Sales by Category')
  return fig

@anvil.server.callable  
def save_to_database(data):
  # Built-in database access
  app_tables.sales.add_row(
    category=data['category'],
    amount=data['amount'],
    date=datetime.now()
  )
  return "Saved successfully"


# === CLIENT MODULE (Form1.py) ===
from anvil import *
import anvil.server

class Form1(Form1Template):
  def upload_change(self, file, **event_args):
    # This runs in the browser
    self.progress.visible = True

    # Call server function - seamless RPC
    chart = anvil.server.call('analyze_sales', file)
    self.plot.figure = chart

    self.progress.visible = False

  def save_button_click(self, **event_args):
    data = {
      'category': self.dropdown.selected_value,
      'amount': self.text_box.text
    }
    result = anvil.server.call('save_to_database', data)
    alert(result)
Enter fullscreen mode Exit fullscreen mode

What’s happening here:

  1. File upload is handled client-side (Python in browser via Skulpt)

  2. Heavy processing with pandas happens server-side with full Python capabilities

  3. RPC calls look like regular Python function calls (no HTTP requests to construct)

  4. No JSON serialization code (Python objects are serialized automatically)

  5. Security is built-in: server functions decorated with @anvil.server.callable form your API boundary

Notice there’s no REST API definition, no endpoint configuration, no fetch calls with headers and error handling. The anvil.server.call() function handles all of that transparently.

Integration & Ecosystem

What Works Where

Server-side, you have access to the full Python ecosystem. Install any pip package: pandas, scikit-learn, requests, TensorFlow. This is native Python execution.

Client-side is more limited. Plotly works for visualizations, and you have access to a subset of the standard library, but compute-heavy libraries like NumPy won’t run in the browser. This limitation actually makes sense architecturally, heavy computation belongs on the server anyway.

JavaScript libraries can be wrapped and called from Python using Anvil’s JavaScript integration features. You’re not completely cut off from the JavaScript ecosystem when you need it.

Anvil includes built-in integrations for common services: Stripe for payments, Google and Microsoft for authentication and APIs, and email sending. These work out of the box with minimal configuration.

The Uplink: The Secret Weapon

The Uplink is perhaps Anvil’s most unique feature. It lets you connect existing Python code running anywhere; your laptop, a Jupyter notebook, a GPU server to your web application.

# Your local Jupyter notebook or script
import anvil.server
anvil.server.connect("YOUR_UPLINK_KEY")

@anvil.server.callable
def run_ml_model(data):
  # Use your local GPU, existing code, trained models
  return model.predict(data)
Enter fullscreen mode Exit fullscreen mode

Your web app calls this like any server function, but it executes on your machine. This is brilliant for connecting data science workflows to web interfaces without rewriting everything. Your Jupyter notebook becomes a backend service.

When to Use This

Choose Anvil when:

  • Your team is Python-heavy but frontend-light

  • Building internal tools, dashboards, or admin panels

  • Rapidly prototyping data applications

  • Connecting Jupyter notebooks or data science code to web UIs

  • You value development speed over pixel-perfect custom UI

  • Creating CRUD applications where Python’s full ecosystem matters more than complex client-side interactions

The Bottom Line

Anvil makes a specific technical bet: for certain types of applications, the productivity gains from using a unified language outweigh the performance overhead and ecosystem constraints of client-side Python.

This isn’t about replacing React or Vue. It’s about offering an alternative path for Python developers who view web UIs as a necessary interface layer rather than the core of what they’re building. When your value is in data processing, business logic, or machine learning models, Anvil lets you wrap those capabilities in a web interface without learning an entirely new stack.

The platform is part of a broader trend of Python expanding beyond its traditional backend role.

Is this the future of web development? Probably not. But for specific use cases (particularly in data science, internal tooling, and rapid prototyping) it’s a genuinely productive alternative that more Python developers should know about.

This article may not be perfect and I would appreciate any feedback!

Buy me a coffee: https://buymeacoffee.com/daisyauma

Top comments (0)