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.

Creating HTTP APIs

You can build an HTTP API for your app by decorating server functions with the @anvil.server.http_endpoint 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. 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 http_endpoint decorator

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

import anvil.server

@anvil.server.http_endpoint("/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/_/api/users/1234

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

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

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

import anvil.server

@anvil.server.http_endpoint("/users/:id")
def get_user(id):
  return "You requested user %" % id

In the example above, if you navigate to https://<your-app-id>.anvil.app/_/api/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/_/api/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.http_endpoint("/users/:id")
def get_user(id, **params):
  return "You requested user %s with params %s" % (id, params)

In the example above, if you navigate to https://<my-app-id>.anvil.app/_/api/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.http_endpoint 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 the “Security and cross-site sessions” section below
  • require_credentials and authenticate_users are described in the “Authentication” section below.

Getting the URL for your API

Getting it manually

Your API is at

https://<your-app-id>.anvil.app/_/api...

or at

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

if you have a custom domain.

If your app is private, the endpoints will be at

https://<your-app-id>.anvil.app/_/private_api/<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.net/_/api/users"

You can write:

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

This has the advantage of returning whichever origin the user is currently connected on, i.e. it will return your custom domain correctly.

You can also get the origin of the whole app to generate links for a browser:

app_url = anvil.server.get_app_origin()

If you’re using a custom domain, get_app_origin will still return the .anvil.app domain - it guarantees to give you something that works regardless of DNS settings.

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.http_endpoint("/users/:id")
def get_user(id):
  ip = anvil.server.request.remote_address
  return "You requested user %s from IP %s" % (id, 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 below), returns the provided username. Otherwise None.
  • password - For authenticated requests (see below), 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.http_endpoint 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).
import anvil.server

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

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

import anvil.server

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

To add custom headers to your response, add them to the headers dictionary, which is an attribute of the HttpResponse object:

import anvil.server

@anvil.server.http_endpoint("/foo")
def serve_content():
  response = anvil.server.HttpResponse(200, "Body goes here")
  response.headers["X-Custom-Header"] = "Custom value"

  return response