Learn how to build a full-stack app with Anvil

In this tutorial, we’ll take a tour of Anvil and build a simple feedback form. We’ll start from a blank page, and build up to a web app with database storage, email functionality and deployed on the web - all in nothing but Python.

The final app will look something like this:

The techniques you’ll learn in this tutorial are fundamental to building any Anvil app, and mastering them will give you a good understanding of how Anvil works.

You don't need any prior experience with Anvil or web development to complete this tutorial.

All you'll need is a little bit of Python. If you’re new to Python, here are some resources to help you get started.


In this tutorial, you’ll:

  1. Build your user interface using Anvil’s drag-and-drop editor
  2. Create a database to store feedback from users
  3. Write server-side Python to save feedback to the database
  4. Write client-side Python to submit user feedback to the database
  5. Make your app send email notifications when someone leaves feedback
  6. Optional: Challenge yourself and reinforce what you’ve learned

Step 1 - Build your user interface

Let’s start by creating your app.

Create an app

Log in to Anvil and click ‘Create a new app’. Choose the Material Design theme.

Location of the Create App button

First, name the app. Click on the name at the top of the screen and give it a name.

Rename your app by clicking on the title

Add the first components

Next, we will construct the UI by dragging-and-dropping components from the Toolbox into the page.

The Theme Components part of the Toolbox contains components that are specific to the current app’s Theme. Here, you’ll find the Card component:

Location of the Card component in the Toolbox

Location of the Card component in the Toolbox

Drop a Card Card icon, and a Label Label icon into the page as demonstrated below:

Anvil Design View with a UI consisting of a Card and a Label

Change component properties

Select the Label you just added to the page. Then modify the text to ‘Feedback Form’ by double clicking the Label in the designer.

Changing the text of the Label

Below the Toolbox on the right, you’ll find the Properties Panel where you can edit the styling and behaviour of your components. Under Appearance expand the More Appearance Properties section and change the role to Headline.

Changing the 'role' property in the Properties Panel

Add field labels

Our feedback form will ask our users for their name, email address, and to leave some feedback.

We’ll use Labels for our input prompts. Drop three Labels Label icon into the page, and change their text properties to:

  • Name:
  • Email:
  • Feedback:
Dropping three Labels onto the page

Add input components

Drop a TextBox TextBox icon into the page next to our “Name:” label. Then change the component name to name_box by clicking the component name in the Object Palette. Repeat this with another TextBox TextBox icon beside our “Email:” label and change its name to email_box.

Finally, add a TextArea TextArea icon underneath our “Feedback:” label and rename it to feedback_box.

Dropping two TextBoxes and a TextArea onto the page

We’re changing the name of each user input component, to make it easier to identify them from code.

Add submit button

Finally, drop a Button Button icon into the page. In the Properties Panel:

  • Change the name from ‘button_1’ to ‘submit_button’
  • Change the text from ‘button_1’ to ‘Submit’
  • Change the role to ‘primary-color’.
Adding a Button, and changing the text, role, and name using the Properties Panel

Congratulations, you’ve just built a User Interface in Anvil! Your users can now enter feedback into your app.

In the next step, you’ll create a database table to store the feedback.


Step 2 - Create your database

We’ll use Data Tables to store feedback from our users. Data Tables are an out-of-the-box option for data storage in Anvil, and they’re backed by PostgreSQL.

Add a Data Table to store your feedback

In the Sidebar Menu on the left, click the database icon Database Icon.

Opening the Data Tables service

Click on ‘+ Add Table’ and name it ‘feedback’.

Renaming the table

Renaming the table

Set up your table

Now set up your “feedback” table, with the following columns:

  • name (Text column)
  • email (Text column)
  • feedback (Text column)
  • created (Date and Time column)

Here’s how to do that:

  1. Add a column for the name of the person submitting feedback, by clicking ‘+ New Column’. Call this column ’name’ and under Column Type select Text. Click ‘Ok’.
  2. Keep adding columns until your Data Table has the structure we described above. Column titles are case sensitive, so let’s stick to lower case.
Creating a Data Table

Your Data Table should look something like this:

The final Data Table

The finished table

Nice work! Your Data Table is set up and ready to use. Next, you’ll write some server-side code to store the feedback in it.


Step 3 - Write server-side Python

Anvil’s Server Modules are a full server-side Python environment. Server Modules cannot be edited or seen by the user, so we can trust them to do what we tell them. This is why we’ll use a Server Module to write to the Data Table.

Create a Server Module

Create a Server Module by selecting the App Browser app icon in the sidebar menu and clicking ‘+ Add Server Module’.

Adding a Server Module

Adding a Server Module

Store data using a server function

We’ll write a server function to add a new row to the ‘feedback’ Data Table we just created. We’ll use the add_row method to do this.

Add this function to your Server Module:

@anvil.server.callable
def add_feedback(name, email, feedback):
  app_tables.feedback.add_row(
    name=name, 
    email=email, 
    feedback=feedback, 
    created=datetime.now()
  )

The @anvil.server.callable decorator allows us to call this function from client code.

We’re using the datetime library to set the ‘created’ column to the current date and time, so you’ll also need to import the datetime class at the top of your Server Module:

from datetime import datetime

Great! We now have a server function that will add a new row to our Data Table when it’s called.

Let’s go ahead and link it up with our UI.


Step 4 - Write client-side Python

We want to store information in our Data Table when users click the ‘Submit’ button. This means calling the add_feedback server function when our users click the Submit Button.

We’ll use Events for this.

Set up an event handler

Anvil components can raise events. For example, when a Button is clicked, it raises the ‘click’ event.

Click on ‘Form1’ in the App Browser to go back to your UI.

Click on the ‘Submit’ Button, and click on click event -> in the Object Palette.

Configuring a click event handler for a Button using the Properties Panel

You will be taken to the Form Editor’s ‘Split’ view, where you’ll see the Code Editor. This is where you write your client-side Python code that runs in the browser.

You’ll see a submit_button_click method on your Form that looks like this:

  @handle("submit_button", "click")
  def submit_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    pass

This is the Python method that runs when the ‘Submit’ Button is clicked. It is decorated with @handle("submit_button", "click"). This tells Anvil that the decorated function will run when the submit_button fires the click event.

For example, to display a simple popup with the text ‘You clicked the button’, edit your submit_button_click function as follows:

  @handle("submit_button", "click")
  def submit_button_click(self, **event_args):
    # Display a popup that says 'You clicked the button'
    alert("You clicked the button")

Run your app

To see it in action, click the ‘Run’ button at the top of the screen:

Location of the 'Run' button

Click the Submit button and your popup should appear!

Capture user inputs

When someone clicks Submit on our feedback form, we want to store a name, email address, and some feedback for that person in our Data Table.

We can read data from our input components by reading their properties. For example, we can read the text in our TextBoxes using their text property.

Edit your submit_button_click function to look like this:

  @handle("submit_button", "click")
  def submit_button_click(self, **event_args):
    # Set 'name' to the text in the 'name_box'
    name = self.name_box.text
    # Set 'email' to the text in the 'email_box'
    email = self.email_box.text
    # Set 'feedback' to the text in the 'feedback_box'
    feedback = self.feedback_box.text

Great! Now we’ve captured the name, email, and feedback that we want to store in our Data Table.

We want to call our add_feedback server function when the button is clicked, and pass in the user inputs. We made our add_feedback server function available to our client-side code by decorating it as @anvil.server.callable.

This means we can call it from our client-side code using anvil.server.call('add_feedback', <arguments>)

Let’s call our add_feedback server function from our submit_button_click function:

  @handle("submit_button", "click")
  def submit_button_click(self, **event_args):
    name = self.name_box.text
    email = self.email_box.text
    feedback = self.feedback_box.text
    # Call your 'add_feedback' server function
    # pass in name, email and feedback as arguments
    anvil.server.call('add_feedback', name, email, feedback)

Display a notification

We’ll also display a temporary popup using a Notification to let our users know their feedback has been submitted.

Edit your submit_button_click function to look like this:

  @handle("submit_button", "click")
  def submit_button_click(self, **event_args):
    name = self.name_box.text
    email = self.email_box.text
    feedback = self.feedback_box.text
    anvil.server.call('add_feedback', name, email, feedback)
    # Show a popup that says 'Feedback submitted!'
    Notification("Feedback submitted!").show()

Clear our user inputs

There’s one final step left, and that’s to clear our user inputs each time someone submits feedback.

We’ll do this in a separate function to keep our code nice and clean.

Add this method to your Form, underneath your submit_button_click() method:

  def clear_inputs(self):
    # Clear our three text boxes
    self.name_box.text = ""
    self.email_box.text = ""
    self.feedback_box.text = ""

Finally, let’s call this from our submit_button_click method, so that we clear our inputs each time feedback is submitted.

Edit your submit_button_click function to look like this:

  @handle("submit_button", "click")
  def submit_button_click(self, **event_args):
    name = self.name_box.text
    email = self.email_box.text
    feedback = self.feedback_box.text
    anvil.server.call('add_feedback', name, email, feedback)
    Notification("Feedback submitted!").show()
    # Call your 'clear_inputs' method to clear the boxes
    self.clear_inputs()

Run your app

Time to Run your app! Click the ‘Run’ button at the top of the screen, fill in a name, email address, and some feedback and click Submit.

Then, stop your app, and go to your Data Tables. You’ll see you have a new row of feedback in your Data Table!

Location of the run button

Congratulations! You’ve created an app to gather and store user feedback.

We’ll take it one step further: let’s make it email you whenever somebody enters feedback.


Step 5 - Send Emails

We’ll use the Email Service to send you an email each time someone leaves feedback.

Add the Email Service

Let’s start by adding the Email Service to our app. Select the blue ‘+’ button in the sidebar menu to open the list of available services. Then, click on the ‘Email’.

Adding a service

Add a service

Adding the Email Service

Select the Email Service

Now you’ve enabled the Email Service, you can send an email just by calling anvil.email.send.

Call the send function

Go to your Server Module, and modify your add_feedback function to look like this. Remember to change the address to your email address! We added this function in Step 3.

@anvil.server.callable
def add_feedback(name, email, feedback):
  app_tables.feedback.add_row(
    name=name, 
    email=email, 
    feedback=feedback, 
    created=datetime.now()
  )
  # Send yourself an email each time feedback is submitted
  anvil.email.send(#to="noreply@anvil.works", # Change this to your email address!
                   subject=f"Feedback from {name}",
                   text=f"""
  A new person has filled out the feedback form!

  Name: {name}
  Email address: {email}
  Feedback:
  {feedback}
  """)

Test your function

We’ll test our add_feedback function using Anvil’s built-in REPL. On the right of the editor’s Bottom Panel, select the “Launch Server REPL” button REPL icon .

This will open a server console which is a Python interpreter, very similar to what you’d use on your own machine. To test our function, we’ll import the function from our server module and then run our function feeding it some dummy data:

from .ServerModule1 import add_feedback
add_feedback("Ryan", "test@email.com", "Anvil is super cool!")
Using your function with the server REPL.

You’ll notice Anvil’s built-in autocomplete helping you find the server module and function.

Hit enter and you’ll see a new row of feedback appear in your Data Table. An email will also be sent to the address you registered with on Anvil!

Publish your app

You can easily publish you app online for anyone to use. Click the ‘Publish’ button at the top right of the editor, then select ‘Publish this app’ and use the public URL provided or enter your own.

Publishing an app and changing its URL

The publish button highlighted at the top of the Editor

Fill in a name, email address, and some feedback and click Submit. You’ll receive an email letting you know someone has filled out your feedback form.

Using the deployed app

Using the deployed app

Congratulations!

And that’s it. You’ve just built a working database-backed feedback app in Anvil.

You can also use the following link to clone the finished app and explore it yourself:

In the next step, you can challenge yourself to reinforce what you’ve learnt and learn more!

Step 6 - Optional: Challenge yourself

This part is optional! The best way to learn any new technology is to challenge your understanding. Why not challenge yourself to reinforce what you’ve learnt and learn more?

Improve the feedback form

A common question when asking for feedback is “How likely you would be to recommend us to a friend or colleague?”. Add this question to your feedback form app with a DropDown component that gives your users the options “Very unlikely”, “Unlikely”, “Likely” and “Very likely”.

An example of the app with a dropdown

An example of the app with a dropdown

Don’t forget to add a column to your database to store the response, and update the client-side and server-side code to send the response from the client to the database.

Need help with the challenge? Why not create a post on the Anvil forum?

With this challenge finished, you now have a solid foundation in Anvil.


What next?

Head to the Anvil Learning Centre for more tutorials, or head to our examples page to see how to build some complex apps in Anvil.