Live Chat

We'll need to share your messages (and your email address if you're logged in) with our live chat provider, Drift. Here's their privacy policy.

If you don't want to do this, you can email us instead at contact@anvil.works.

Blog

Hand-drawn web apps
3rd March 2019

Xkcd-style sketched Web apps

This morning, I was reminded of Jake VanderPlas’s delightful xkcd theme for Matplotlib by Hacker News. As well as being fun to look at, sketch graphs are a great way of signalling “this is a prototype”, or “only the trend here is important”. My immediate thought was, “wouldn’t it be great if we could sketch out web apps as easily?”. Clearly, I wasn’t the only one:

wodenokoto says: "Is there a bootstrap or css theme to go along, so my PoC web-app can match the style of my proof of concept graphs?"

So, on my train ride into London today, I built an XKCD-style sketch theme for building apps in Anvil. Here’s what it looks like:

Animation of building an app with the XKCD sketch theme

This theme is ready to use yourself:

Copy app: XKCD-style theme

Or read on to see how it works…

Step 1: Write some CSS

Normally, you don’t need anything except Python to build an app in Anvil. But if we want, we can drop down and customise the look and feel of our page with HTML and CSS.

Start with a new “Custom HTML” app, so the theme.css in our Assets starts out blank. Let’s give ourselves a more hand-drawn feel:

@import url('//fonts.googleapis.com/css?family=Patrick+Hand|Patrick+Hand+SC')

body {
  font-family: "Patrick Hand", sans-serif;
  font-size: 18px;
}


input.form-control, textarea.form-control, select.form-control, .btn {
  box-shadow: none;
  border: 2px solid black;
  border-radius: 15px 255px 15px 225px / 225px 15px 255px 15px;
}

/* Make the buttons look a bit more flat */
.btn {
  background-image: none;
  padding: 5px 12px;
}

.btn:active, .btn:hover, .btn:focus {
  border-color: black;
  background-image: none;
  background-color: #e3e3e3;
  outline: none;
}

Step 2: Cards and Headings

As well as primitives like “text”, “input box”, or “drop-down”, applications have higher-level UI elements that should look consistent. For example, we might want to group our components into “cards”. Likewise, we want all our headings to match each other.

We can make these UI elements available in the Toolbox, so we can use them with the visual designer:

Creating a card with a heading

To do this, we don’t create new components; we create new “roles” for existing components. A “Card” is a panel component (ColumnPanel) with a border and drop-shadow; and a “Heading” is a Label component with a different font and bigger text.

We use the Roles editor to create roles called card and heading (applicable to ColumnPanels and Labels respectively), and make them available in the Toolbox. Then we just need to write the CSS:

.anvil-role-card {
  border: 2px solid black;
  padding: 5px;
  border-radius: 125px 10px 20px 185px / 25px 205px 205px 25px;
  box-shadow: 2px 2px 5px 0 rgba(0,0,0,.2);
}

.anvil-role-heading .label-text {
  font-family: "Patrick Hand SC", sans-serif;
  font-size: 26px;
  padding: 13px 0 0;
}

Step 3: HTML scaffolding

Often pages have common structure - a title bar, some navigation links, etc. Rather than building this every time with components, our theme can provide HTML scaffolding. This scaffolding can include drag-and-drop points for creating special elements like titles or navigation links. It then typically has space for a standard ColumnPanel to lay out the main page content.

We edit standard-page.html (also in our Assets) to define a “title” slot and a “nav-links” slot, with placeholders for each. We use anvil-drop-slot= attributes to make drop zones: if you drop components onto these elements, they will be placed in the designated slot:

<div class="title-bar">
  <div class="title">
    <div class="placeholder" anvil-if-slot-empty="title" anvil-drop-slot="title">
      Drop a title here
    </div>
    <div anvil-slot="title"></div>
  </div>

  <div class="nav-links">
    <div anvil-slot="nav-links"></div>
    <div class="placeholder" anvil-if-slot-empty="nav-links"
         anvil-drop-slot="nav-links">
      Drop a FlowPanel here
    </div>
  </div>
  <div style="clear:both"></div>
</div>

And finally, we set up the default drop behaviour. If the mouse isn’t directly over a drop zone, we drop into the nearest container:

<div anvil-drop-default anvil-drop-container=".anvil-container"></div>

And there we have it!

We now have an Anvil theme for putting together xkcd-style web apps, super quickly and with nothing but Python. Combine it with the xkcd matplotlib style (it’s now built into matplotlib, and you can use that with Anvil) to create beautifully sketchy-looking applications:

You can see this app’s source code too:

Copy app: Plotting Example

To find out more about Anvil, check out our tutorials. For the gory details on how to create your own custom themes that work with Anvil’s visual designer, check out the reference docs.

Double-Entry Bookkeeping for Engineers
28th February 2019

Do you really understand your accounts? Could you build your books from scratch?

If you run a business, you’ve probably heard of double-entry bookkeeping. It’s “the most influential work in the history of capitalism”. We “may not have had the Industrial Revolution without it”. It’s kind of a big deal.

But, especially if you’re a technical founder, you might not actually understand it. You’re not alone: I conducted a survey of early-stage founders at our co-working space in Cambridge, with companies from 2 to 16 people in size. About 75% of founders said they didn’t understand bookkeeping! It seems that many founders are just mashing buttons in Quickbooks and hoping for the best.

But we’re hackers – we can do better than that! If you can write code, you can understand double-entry bookkeeping. And we’re going to prove it, by building an accounting app from scratch. All you need is a little bit of Python.

Part 1: The basics

There are two rules of double-entry bookkeeping:

  1. Every financial category in your business is represented by an account.

  2. Every financial transaction in your business can be represented as a transfer between accounts.

Let’s say my startup has $2,000 in the bank. I then spend $1,500 of that on a laptop. If I was just looking at the money in my bank account, I’d think that money just disappeared. But it didn’t disappear – it turned into a laptop!

We can record our new laptop in a separate account (we’ll call it “Fixed Assets”). Here’s how that looks:

Buying a laptop decreases Cash in Bank from $2,000 to $500, but increases Fixed Assets from $0 to $1,500

We can see that the total value of our company’s assets hasn’t changed: We’ve just traded $1,500 worth of cash for $1,500 worth of laptop.

The purchase is entered twice (hence “double entry”): it removes value from Cash in Bank, and adds it to Fixed Assets. The entries must always balance, so no value has disappeared – it’s just been moved around.

(We should note that we’re taking a very database-y view of our accounts here. An accountant wouldn’t talk about “moving value around”. Instead they would say, “we applied a $1,500 credit to Cash in Bank and a $1,500 debit to Fixed Assets”1. We’re programmers, so I’m going to keep it simple and just talk about adding and subtracting numbers. From time to time, I’ll provide translations into accountant-speak, like this one. If you’re confused, start by ignoring everything in italics – once you understand how it all works, you can come back for the Accounting Terms Decoder Ring.)

Let’s build it!

I think we understand enough to start building our accounting system. Let’s fire up Anvil, create a new app, and set up the built-in database (Data Tables) to represent our accounting history:

  • We make an Accounts table, with just a name column.
  • The Journal Entries table does the heavy lifting. It records the date and amount of every change made to every account.
  • To make it easier to check that every transaction is balanced, we group all the balancing entries for a transaction together: We make a table of Transactions with a description for each transaction, and then we link each journal entry to the transaction it’s part of.

Here’s our database, with two balancing journal entries representing a single transaction (buying a laptop):

Schema of Journal Entries table

Now we have a database, we can store transactions in it. Here’s the code to create a new transaction:

def add_new_transaction(date, from_account, to_account, amount, description):
	txn = app_tables.transactions.add_row(description=description, date=date)

	app_tables.journal_entries.add_row(
		change=+amount, account=to_account, transaction=txn, date=date
	)
	app_tables.journal_entries.add_row(
		change=-amount, account=from_account, transaction=txn, date=date
	)

For example, here some code we could run to record that laptop purchase:

  cash_in_bank = app_tables.accounts.get(name="Cash in Bank")
  fixed_assets = app_tables.accounts.get(name="Fixed Assets")

  add_new_transaction(date=date.today(),
                      from_account=cash_in_bank, to_account=fixed_assets,
                      amount=1500, description="Purchased laptop")

Now we can make a web interface for entering transactions. We use Anvil’s visual designer to create a form for entering the transaction details. Then, when the user clicks Add Entry, we call our add_new_transaction() function:

Design your UI and then edit your code

To see the source code to our app so far, click here to open it in the Anvil editor:

Copy app: Recording Transactions


Part 2: Dealing with the Real World

OK, so that’s the easy part – and this is where most introductions to double entry bookkeeping stop. But we still have a problem: How do we interact with the outside world?

As we keep saying, every transaction is balanced – it sums to zero. But a company is not a closed system! Sometimes, you spend money on something and it’s gone. If I spend $500 a month on delicious seasonal treats, that money really has left the company. Likewise, if I make something and sell it, I really have brought new money into the company.

But in the system we’ve designed so far, there’s no way to represent that. I could make an account representing “Things Meredydd Ate”, and record my Mini Egg habit by transferring $500 each month from Cash in Bank to Things Meredydd Ate. But as far as our database is concerned, the asset value of the company has not changed: I have just been steadily reallocating our assets from cash to chocolate.

This is clearly not an accurate representation of our business. What can we do to fix it?

“Balance Sheet” vs “Profit and Loss”

The answer is to separate our bookkeeping records: we create special accounts that represent “outside the company”, and treat them differently from things our company owns. We name them after the financial statements they’re used for:

  • Balance sheet accounts are the accounts we’ve already met. They represent things we own (assets, such as cash in a bank) or things we owe (liabilities, like a loan we will have to pay back – these have negative value).

    For any point in time, we can work out the value of each balance-sheet account on that date. This lets us display everything we owned or owed on that date: This is the company’s balance sheet.

  • Profit and loss accounts are accounts that represent the outside world. Transactions with these accounts represent income and expenses – that is to say, value flowing into or out of the company. For example, when we sell something, we can think of it as a transfer of money from “Sales” to a balance sheet account like “Cash in Bank”. When we spend money on Mini Eggs and I promptly eat them, that’s a transfer to “Expenses”.

    It doesn’t make sense to keep a running total of these (how much money does “the rest of the world” have, anyway?). We want to answer questions like, “how much did we spend on Mini Eggs web hosting this month?”

    For any period of time (eg “last year”), what we want to know is how much money the company has gained or lost from outside, and which P&L accounts it flowed from or to. We add up all transactions with every profit-and-loss account that occurred between the specified dates (eg from January 1st last year to January 1st this year), to get the profit and loss statement for that time period.

If you sum up all the accounts on a balance sheet on a given date, you know how much your company was worth (its “book value” on that date). If you calculate a profit and loss statement between two dates, you know how much profit or loss you made over that time period. Because all transactions balance, the total profit (or loss) between two dates is equal to the change in total book value between those two dates.

So we can see how much our company is worth right now (that’s the balance sheet for today), and how much it was worth last year (balance sheet for one year ago). And we can see how we got from there to here: where all those gains and losses came from (that’s the profit and loss statement from a year ago until today).

And because we’re using double-entry, this is an accurate way to measure profit! For example, buying new laptops for the team might deplete your bank account, but the company didn’t suddenly become unprofitable that month. The P&L lets you see through the noise in your bank account, and understand your actual financial position.

Implementing this in our app

Let’s add this distinction to our bookkeeping system. We add a new boolean column to the Accounts table, to record whether something is a balance-sheet or a profit-and-loss account. We’ll add a new account, “Sales”, which is not a balance-sheet account:

Adding a "balance sheet" column to the Accounts table

We’ll put some sample data in to demonstrate. Let’s say we sold $2000 worth of widgets the week before we bought our laptop. We record that as a transfer from Sales (outside the company), to Cash in Bank:

Sample data indicating sales income before buying laptop

Notice that because we’ve gained value from the outside world, the change to the Sales account (profit and loss) is actually negative! This balances the positive change to Cash in Bank (balance sheet).

(Again, professional accountants would use different words here – they’d say “we applied a $2,000 credit to Sales, and a $2,000 debit to Cash in Bank”2 – but the arithmetic is the same.)

Building a Balance Sheet

We can now add up each type of account separately, and calculate two important reports:

The Balance Sheet shows all our assets and liabilities at a given point in time. The total is the company’s “book value”:

A Balance Sheet

Here’s the code that calculates the balance of an account on a given date. We just add up all that account’s journal entries, up to the specified date:

def get_balance_for_account(account, date):
  balance = 0

  for entry in app_tables.journal_entries.search(account=account,
                                                 date=q.less_than(date)):
    balance += entry['change']

  return balance

Now we can calculate the balance for every balance-sheet account in our system:

def get_balance_sheet(date):
  return [{'name': account['name'], 'balance': get_balance_for_account(account, date)}
          for account in app_tables.accounts.search(balance_sheet=True)]

This produces a list of dictionaries, which is easy to display in a Data Grid. And that’s how we produce the balance sheet screen you see above.

Building a Profit and Loss Statement

Secondly, there’s the Profit and Loss Statement, which shows how much value the company gained from, or lost to, each P&L account over a given period:

A Profit and Loss statement

And here’s the code that calculates it for each account. It’s very similar to the balance-sheet code – after all, it’s just summing up all the changes to an account over a time period:

def get_profit_for_account(account, from_date, to_date):
  profit = 0

  for entry in app_tables.journal_entries.search(account=account,
                                                 date=q.between(from_date, to_date)):
    # Subtract, because any money going out of a P&L account
    # is a gain for us, and any money going in is a loss for us.
    profit -= entry['change']

  return profit

But there is one important difference. Profit and Loss accounts represent “outside the company”, and we want to count how much we’ve gained from P&L accounts – profit should be positive! Because journal entries always balance, we’ve already seen that a gain from Sales represents a negative change to the Sales account. So, to calculate our profit, we start at zero and subtract every change to the Profit and Loss account.

(An accountant would say this differently. They’d say, “when we display Profit and Loss accounts, we display credit balances as positive”. I personally find those words confusing, but the underlying arithmetic is the same: We add up everything in a Profit and Loss account, then flip the sign before we display it, so that negative [aka “credit”] numbers are displayed as positive.)

Source Code

That’s it! We’ve built a web-based accounting system with just a few lines of Python. We can enter transactions, and generate both balance sheets and profit-and-loss statements. In this article, we’ve been concentrating on the data-handling code – but thanks to Anvil, making a web-based user interface was the easy part.

Click here to open the app in the Anvil editor, read the source code, and try it out yourself:

Copy app: Accounting System

Closing Thoughts

Should you use this code to manage your startup? Probably not. Use a full-featured accounting system like Quickbooks: it can import transactions from your bank, knows about tax regimes, handles multiple currencies, and much more. And you can still drive your accounting records with code – these systems have powerful APIs.

Of course, out-of-the-box tools can’t do everything. Your business will have processes you want to optimise, and common tasks you want to turn into one-click affairs. We build all our internal workflow tools with Anvil – we’ve written accounting tools that use the Quickbooks API to query transactions, record expenses and raise invoices. And now you understand how double-entry bookkeeping works, you’re ready to do the same thing!

What will you build?

Learn about Anvil


Many thanks to our awesome accountants, Lucy and David Parry of Parry & Parry, who taught me everything in this post. Any mistakes in here are assuredly mine and not theirs. If you’re looking for accounting services in and around Cambridge or London, we recommend them heartily.


Epilogue: Examples

Still wondering how to represent a particular financial event? Here are a few examples to show how we use double-entry bookkeeping to represent what’s happening:

  • What happens when I issue an invoice?

    Businesses often sell things “on credit”: We give the customer a product, and issue them an invoice. Some time later, the customer pays the invoice.

    How to represent this? Well, if we’ve issued an invoice, the customer owes us money – so that’s an asset! We record these assets in their own balance-sheet account: “Accounts Receivable”.

    When we make a sale, we transfer value from the Sales account (that’s Profit and Loss, because we’re gaining value from the outside world) into Accounts Receivable. Later, when the customer pays the invoice, that’s a transfer from Accounts Receivable to Cash in Bank.

    Note how we made the profit when we made the sale, not when the money reached our bank account. But in between making the sale and receiving cold hard cash, we could track how much money we were owed by our customers. This gives us a lot more visibility into the business than just looking at our bank account.

  • What happens if someone doesn’t pay an invoice?

    If you’ve given up on ever getting money from someone, you write off the debt – it turns out this asset wasn’t worth as much as we had thought. This is a loss, so we record this as a transfer from Accounts Receivable to Sales. (The usual term for this is a “bad debt”.)

  • What happens when my laptop wears out?

    Again, we can represent this as a transfer from the balance-sheet to a P&L account. We typically model equipment as having a “useful economic life”, and spread its cost over that period. We call this “depreciating”. So if we depreciate our $1,500 laptop over 3 years, that means at the end of each year we transfer $500 from Fixed Assets (balance-sheet) to Depreciation (profit-and-loss). After three years, according to our accounting records, the laptop is now worthless.

    This neatly expresses that “we need to buy new laptops from time to time” is an ongoing issue. We don’t take a surprise loss every three years when we replace our laptops; we spread it out over the equipment’s useful life.


Footnotes:

  1. Yes, accountants use “credit” for subtracting (negative numbers), and “debit” for adding (positive numbers). This might sound backwards to you. After all, when you deposit cash into your bank account, they “credit” you when they increase your balance, right? This is because your bank statement is written from the bank’s point of view. Every dollar in your bank account is a dollar the bank owes you. This means it’s a liability to them: it has negative value, so it’s a “credit”. To avoid this confusion, we’ll just talk about adding and subtracting numbers. 

  2. Yes, we used “credit” and “debit” correctly here. Yes, it’s confusing. If you’re lost, just ignore the italicised sections and read the code – you’ll be fine. 

Data Tables now have a rich query language
26th of February 2019

We’ve just upgraded Anvil to make data storage much more powerful.

Data Tables already give you a Python-based system for storing and retrieving data. There’s also a graphical interface to make designing databases even quicker.

This upgrade gives you a library of query operators. You pass them to the search() method when you access your data.

To get all restaurants rated higher than 2 stars:

app_tables.restaurants.search(
  rating=q.greater_than(2)
)

To get all menu items that include the string ‘pizza’:

app_tables.menu.search(
  dish_name=q.ilike('%pizza%')
)

To perform an intelligent search within natural language text:

app_tables.reviews.search(
  review_text=q.full_text_match('Easy to find')
)

You can combine query operators together to build complex queries when you need them. To find good restaurants in London, or any outside of London:

app_tables.restaurants.search(
  q.any_of(
    location=q.not_('London'),
    rating=q.greater_than(2),
  )
)

It’s live right now. Copy an example app to your account and try out some queries of your own. To find out more, have a look at the tutorial.

Whatever you’re building right now, we hope this helps you make it even better.

Upcoming Events
29th of January 2019

We’re on tour!

We’ll be speaking, giving workshops and talking about Anvil at a bunch of meetups and conferences in the next few months. Come and join us, learn more about Anvil, and maybe even take home some swag.

Invite Anvil to your event

Do you run a meetup group or conference? We’d love to present or run a workshop for you! Please drop us a line: events@anvil.works.

Wherever you are in the world, we can do remote presentations by video link, so do get in touch.

"IndyPy loved having Meredydd speak to our group. Very engaging, even across the Atlantic!"

— Calvin Hendryx-Parker, organiser of IndyPy

Upcoming Events

Here’s what’s in the calendar in the next couple of months:

  • Jan 29th: Sheffield, UK (Sheffield Python)
    Workshop: Come and try Anvil yourself, with friendly hands-on assistance.

  • Feb 5th: London, UK (TechHub Demo Night)
    Demo: See what Anvil can do, and how quickly! Meet the founders and ask questions afterwards.

  • Feb 21st: Oxford, UK (Oxford Python)
    Tech Talk: I’ll be demonstrating Anvil, and talking about how it works under the hood.

  • Mar 5th: Cambridge, UK (CamPUG)
    Tech Talk: I’ll be talking about how Anvil works under the hood.

  • Mar 19th: Cardiff, UK (PyDiff)
    Workshop: Come and try Anvil yourself, with friendly hands-on assistance.

  • Mar 22nd-24th: Bratislava, Slovakia (PyCon SK)
    We will be speaking about Anvil at PyCon SK, and leading a hands-on workshop so you can try it yourselves.

  • May 3rd-5th: Cleveland, OH, USA (PyCon)
    We’re already confirmed as sponsors for PyCon 2018 – find us in the exhibition hall, and meet up with other Anvil users.

  • May 24th-26th: Vilnius, Lithuania (PyCon LT)
    Come find us at PyCon Lithuania! We’ll be giving a talk and leading a hands-on workshop.

Escape Hatches and Ejector Seats
15th January 2019

When you need to escape an abstraction, how violent is your exit?

The Web is a hugely complicated system. If you’re writing a typical web app, your data will be in about 6 different forms:

  • Rows in an SQL table
  • Objects in your server code (eg Python)
  • JSON payloads on HTTP endpoints
  • Objects in Javascript
  • HTML DOM elements
  • Text on the screen

Translating these layers is tedious and repetitive, so we’ve invented a ton of frameworks to help us:

  • ORMs translate SQL rows into objects
  • REST frameworks turn objects into HTTP endpoints
  • Templating engines turn Javascript objects into HTML DOM elements
  • …and so on.

Each of these frameworks is a new layer of abstraction.

Every problem in computer science can be solved by another layer of indirection...
— David Wheeler
...except for the problem of too many layers of indirection.
— Anon.

All abstractions “leak”, requiring you to understand what’s happening underneath. Web frameworks leak all the time – you can’t effectively use Angular or React, for example, without understanding the DOM.

But today we’re asking about a worse situation – what happens when your abstraction just can’t do what you need?

Option 1: Go down with the ship

Still from the film Titanic, of lower class passengers being trapped behind metal gates

Let’s say you’ve written your app as a Windows desktop application, and now you need to use it on the web. Too bad – you’re going down with the ship. Your only choices are to rewrite your app on a new platform, or sit tight and hope that feature isn’t so critical after all.

My condolences.

Option 2: The Ejector Seat

If you’re lucky, your abstraction is more helpful than that.

Example: Create-react-app is a neat way to start a web app with React. It’s a black box that replaces your build manager, a Javascript transpiler, a minifier, and all the other build tools you need for modern web development.

Of course, sooner or later you’ll hit the limits of what create-react-app can do: you’ll need something it doesn’t support. The authors have thought of this situation, and there’s a button you can push: EJECT.

Picture of ejector seat firing

Ejecting from create-react-app opens up the black box. It writes out a set of build scripts representing the current state of your app. Now you can edit those scripts yourself – the hard way.

But create-react-app has given up. It protected you for a while – but as soon as you needed one advanced feature, you’ve been dropped in the Mojave desert with a parachute, a first-aid kit, and a copy of the Webpack manual.

Good luck!

It’s better than going down with the ship, of course. But should you have to discard an abstraction entirely when you touch its limits?

Option 3: The Escape Hatch

Man climbing out of a submarine escape hatch

It’s much more helpful to acknowledge that your system will always be incomplete, and make it usable even then. Ideally, your users can use complex external tools for the tricky corners of their project, without making the other 95% of their work more complicated. I call this pattern an “escape hatch”, rather than an “ejector seat” – you don’t need to jump all the way out at once.

The Rust programming language is a great example. Rust is a memory-safe low-level language: The compiler enforces rules that make sure your code can’t access memory it shouldn’t. But there are some things you can’t express within those rules. So Rust has an escape hatch, called unsafe. You mark a block of code unsafe, and you can break those rules. A few lines later, you return to writing the rest of your app – with compiler-guaranteed memory safety.

Anvil is an abstraction over all of web development. We provide a visual UI designer (no HTML/CSS), and you write your front-end and back-end code in Python (no JS, no REST frameworks). It’s even got an optional Python-native database (no SQL). This enables a data scientist, a sysadmin, or a developer in a hurry to build full-stack web apps with nothing but Python.

This is a pretty ambitious goal: The Web is a huge platform, with a large set of existing libraries, and the browser vendors add new features all the time. Let’s face it: Our new abstraction isn’t going to cover it all, any time soon.

We could have provided an ejector seat: “Export this Anvil app as a React+Flask app”. This, bluntly, would suck. The moment you edited the generated code, you’d lose all the benefits of Anvil’s abstractions!

So instead, we built escape hatches into every part of Anvil. For example:

  • If you want finer control over appearance, you can leave the drag-and-drop designer and edit the HTML template it uses. And then… flip right back to the drag-and-drop designer, where you can drop components into your new page layout.

  • If you need a browser feature we don’t support yet, you’ll need to use Javascript. You can call from Python to Javascript and back again. And then… wrap that code up as a Python component your colleagues can use. (Here’s an example using the Web Location API.)

  • If you want to use HTTP after all, that’s fine! You can consume HTTP APIs or make your own.

  • If you want to run code outside our serverless environment, it’s easy. Just install our Uplink library on your server, and run your code there. And then… call that code from your app, exactly the same as calling serverless code. Your server code can use all of Anvil’s server-side APIs. (Here’s an example with a Raspberry Pi.) Of course, if you need everything on site, you can get an on-site installation.

  • If you want to edit your code outside Anvil, just check it out in Git! Every Anvil app is a Git repository. Pull, make some changes, push it back. You can automate this, and integrate with whatever code review or CI/CD system you like. And then… jump right back into Anvil, and edit your app there.


The web is a complex platform – often too complex – but every single feature is there for a reason.

Are you building an abstraction? If so, think about your escape hatches: How can your users exploit features of the underlying platform, without ejecting into the wild blue yonder?

Learn More

Get the best out of Anvil with our free email course.