You are currently viewing the new Anvil Editor Docs.
Switch to the Classic Editor Docs
You are currently viewing the Classic Editor Docs.
Switch to the new Anvil Editor Docs

Creating HTTP APIs

You can build an HTTP API for your app by decorating server functions with the @anvil.server.route decorator.

You can use this to give your app a REST API that non-Anvil apps can use to interface with it, in situations where you can’t use the Uplink.

Before you build an HTTP interface for your app, consider using the Uplink instead. It lets you make any Python code behave like an Anvil Server Module.

If you want other people/organisations to interface with your app, they can still use the Uplink safely with the Uplink client key.

The route decorator

The @anvil.server.route decorator makes your function callable over HTTP. It has one required argument - the path, e.g. /users/list.

import anvil.server

@anvil.server.route("/users/list")
def get_user_list():
  return [u['email'] for u in app_tables.users.search()]

Path parameters

You can think of URLs as having two parts: origin and path. Assume you have this URL:

https://<your-app-id>.anvil.app/users/1234

The origin is https://<your-app-id>.anvil.app/, this tells Anvil how to route requests to your app.

The path is /users/1234. This is what is passed into your @anvil.server.route functions.

The path may contain one or more path parameters, denoted by the : character, e.g. /users/:id.

import anvil.server

@anvil.server.route("/users/:id")
def get_user(id):
  return f"You requested user {id}"

In the example above, if you navigate to https://<your-app-id>.anvil.app/users/42, you will receive a response of You requested user 42:

You can also use path parameters in the middle of a path (/users/:id/history) or use multiple path parameters in the same path (/users/:user_id/history/:item_id).

Query strings

The query string is the part of the URL after the question mark (?), such as in this URL:

https://<your-app-id>.anvil.app/users/42?x=foo

It consists of key/value pairs key=value. If there are multiple key/value pairs, they are separated by &, for example key1=value1&key2=value2.

Query-string parameters will be passed to your function as keyword arguments. In this example, we use Python’s ** notation to capture all query string parameters in the params variable:

import anvil.server

@anvil.server.route("/users/:id")
def get_user(id, **params):
  return f"You requested user {id} with params {params}"

In the example above, if you navigate to https://<my-app-id>.anvil.app/users/42?x=foo, you will receive a response of You requested user 42 with params {'x': 'foo'}.

Optional keyword arguments

To configure your HTTP endpoints more, @anvil.server.route takes some optional keyword arguments:

  • methods specifies which HTTP methods this endpoint supports (the default is ['GET','POST'])
  • enable_cors adds CORS HTTP headers (Access-Control-Allow-Origin: *) to your response when set to True. By default, we set CORS headers to permit requests from any web address where your app can be reached (eg xyz.anvil.app, my-custom-domain.com, etc).
  • cross_site_session is described in Cross-site Security.
  • require_credentials and authenticate_users are described in Authentication.

Path restrictions

Your routes cannot respond to paths that begin with /_/. This prefix is reserved for internal Anvil use.

If your app contains a Startup Form or Startup Module, then your routes cannot respond to the plain path /.

The http_endpoint decorator

Anvil also supports decorating your functions with @anvil.server.http_endpoint. The http_endpoint decorator works the same as the route decorator, except that the path of the endpoints it defines are prefixed with /_/api. This can be useful for separating your machine-facing URLs from your human-facing URLs.

So, for example, if you define a function:

@anvil.server.http_endpoint("/users/:id")
def get_user(id, **params):
  ...

…then that endpoint will be available at https://<my-app>.anvil.app/_/api/users/42.

http_endpoint endpoints cannot return FormResponses.

Getting the URL for your API

Getting it manually

If your app is published, your API is at

https://<your-app>.anvil.app/...

or at

https://your-custom-domain.com/...

if you have a custom domain.

If your app is private, the endpoints will be at

https://<your-app-id>.anvil.app/<your private access key>/...

Getting it automatically

Sometimes you will want to generate URLs that point to your HTTP endpoints without having to hard-code the origin of your app. Instead of writing:

endpoint_url = "https://my-app.anvil.app/users"

You can call anvil.server.get_app_origin():

endpoint_url = anvil.server.get_app_origin() + "/users"

This has the advantage of returning whichever origin the user is currently connected on, i.e. if you have published a version but you are running your development version in the Anvil editor, get_api_origin() will return a temporary private link to your development version, so you can test your APIs before publishing them!

If you’re running in the editor, get_app_origin() might return a URL that’s private or not permanent. To get the origin for the published version of your app, call get_app_origin('published').

If you’re using a custom domain, get_app_origin will sometimes still return the .anvil.app domain, if it cannot verify the DNS settings for your custom domain.

If you wish to programmatically construct URLs containing your custom domain, hard-code a constant containing your custom domain and construct URLs from that.

The request object

HTTP requests have far more information associated with them than just path and query-string parameters. This information can be accessed through the anvil.server.request object, which is a global variablenote containing information about the request currently being processed.

import anvil.server

@anvil.server.route("/users/:id")
def get_user(id):
  ip = anvil.server.request.remote_address
  return f"You requested user {id} from IP {ip}"

The request object has the following attributes:

  • path - The path of this HTTP request.
  • method - The method of this HTTP request, e.g. GET, POST, etc.
  • query_params - The query-string parameters passed with this request, as a dictionary.
  • form_params - The form parameters passed with this request, as a dictionary.
  • origin - The URL origin of this HTTP request.
  • headers - Headers passed with this request, as a dictionary.
  • remote_address - The IP address of the source of this request.
  • body - The body of this HTTP request, as an anvil.Media object.
  • body_json - For requests with Content-Type: application/json, this is the decoded body as a dictionary. Otherwise None.
  • username - For authenticated requests (see Authentication), returns the provided username. Otherwise None.
  • password - For authenticated requests (see Authentication), returns the provided password. Otherwise None.
  • user - For authenticated requests, returns the row from the Users table representing the authenticated user.
Footnote: Technically, anvil.server.request is thread-local. (^ go back)

Returning a response

Functions decorated with @anvil.server.route can return:

  • strings (which will be returned with a Content-Type of text/plain)
  • anvil.Media objects (which will be returned with their attached Content-Type)
  • any JSON-able object like a plain list or dict (which will be returned with Content-Type application/json).
  • an anvil.server.FormResponse object, which will serve the client side of your app, loading the specified Form.
import anvil.server

@anvil.server.http_endpoint("/foo")
def serve_content():
  # This response will have Content-Type application/json
  return {"key": "value"}

HttpResponse object

If you need more control over the response, you can return an anvil.server.HttpResponse object (API Docs), providing a custom status code and response body:

import anvil.server

@anvil.server.route("/foo")
def serve_content(**p):
  return anvil.server.HttpResponse(200, "Body goes here")

These can be used for all the standard HTTP response codes that the browser expects.

For instance, here is a redirection response which causes the user’s browser to take them to a different webpage:

import anvil.server

@anvil.server.route("/wikipedia")
def serve_content(**p):
  response = anvil.server.HttpResponse(302, "Redirecting to Wikipedia...")
  response.headers['Location'] = "https://www.wikipedia.org"
  return response

You can use this method to redirect users to another part of your app, after they have triggered some action by hitting a particular HTTP endpoint.

To add custom headers to your response, just add them to the headers dictionary, an attribute of the HttpResponse object.

FormResponse object

If, instead of serving a raw HTTP response, you want to serve your app’s user interface, you can return a FormResponse object. To create a FormResponse, pass the name of the target form, and any arguments.

For example:

import anvil.server

@anvil.server.route("/my-page")
def serve_my_page(**p):
  # This assumes there is a form called MyPageForm in your app:
  return anvil.server.FormResponse('MyPageForm')

You also pass additional arguments to the form’s constructor, by passing them to the FormResponse constructor. This works with both positional and keyword arguments.

For example, if you want https://my-app.anvil.app/users/<email> to serve the page for a user with that email address, you could do something like this:

import anvil.server
from anvil.server import app_tables

# This code assumes there is "users" table in your app, and a
# form called UserPage that can display a row from that table

@anvil.server.route("/users/:email")
def serve_user_page(email, **p):
  user = app_tables.users.get(email=email)
  if user is not None:
    return anvil.server.FormResponse('UserPage', user=user)
  else:
    return anvil.server.HttpResponse(404, "No such user")

Testing HTTP endpoints

If you haven’t yet published your app (or if you’ve published a version that people are using), it’s useful to be able to test the HTTP endpoints of the version you’re developing.

To do this, use a Debug Environment to get a private URL that refers to the version of your app you have open in the editor – a bit like the Run button, but accessible from outside the Anvil Editor. This allows you to flip between writing code and testing it with a program like curl or Postman.


Do you still have questions?

Our Community Forum is full of helpful information and Anvil experts.