HTML templates with Google Cloud Functions
Tags: python, cloudfunctions, serverless, jinja2
Published: 2019-05-23 21:01:29

---
title: HTML templates with Google Cloud Functions
published: true
description: In this post, I'll show you the best way to render HTML templates inside your Cloud Functions with a variety of methods.
tags: #python #cloudfunctions #serverless #jinja2
---

A Google Cloud Function can be called in response to multiple types of events, and of the most useful events to use is an HTTP request. This allows you to make a `GET`, `POST` or other HTTP request to a URL provided by your Cloud Function, and have it run in response to the given request.

When you activate your Cloud Function via HTTP, you can also optionally return a response, much like a HTTP server would. In the simplest examples, this is usually just a string:

```python
def hello(request):
    return "Hello, World!"
```

This creates an HTTP response that looks like this:

```
HTTP/1.0 200 OK
Content-Length: 13
Content-Type: text/html; charset=utf-8

Hello, World!
```

You'll notice here that the `Content-Type` is `text/html`, but the response is not HTML at all, it's just plaintext. What if we wanted to add some formatting?

# Adding some formatting

Let's say we wanted to make part of our response **bold**. We could do this by adding some HTML tags:


```python
def hello(request):
    return "Hello, <b>World!</b>"
```

Which would give us the following response:

```
HTTP/1.0 200 OK
Content-Length: 20
Content-Type: text/html; charset=utf-8

Hello, <b>World!</b>
```

This would probably render correctly in our browsers, but only because modern browsers have an amazing propensity for dealing with invalid HTML. To make this actually a valid HTML response, we would need to change our function to be:

```python
def hello(request):
    return """
<!DOCTYPE html>
<head>
  <title>Saying Hello</title>
</head>
<body>
  Hello, <b>World!</b>
</body>
"""
```

This is starting to get a bit long, so perhaps we would make it a separate variable:

```python
INDEX_TEMPLATE = """
<!DOCTYPE html>
<head>
  <title>Saying Hello</title>
</head>
<body>
  Hello, <b>World!</b>
</body>
"""

def hello(request):
    return INDEX_TEMPLATE
```

# Passing variables and controlling flow

If we want to pass variables into our template, we could just do it with `.format`:

```python
INDEX_TEMPLATE = """
<!DOCTYPE html>
<head>
  <title>Saying Hello</title>
</head>
<body>
  Hello, <b>{place}!</b>
</body>
"""

def hello(request):
    return INDEX_TEMPLATE.format(place="World")
```

What if we had multiple places we wanted to say hello to, on separate lines?

```python
def hello(request):
    return INDEX_TEMPLATE.format(places=["World", "dev.to"])
```

At this point, our "homemade" template would start break down, and we'd need to start getting creative in order to make it continue to work.


# Inline templates with Cloud Functions

Instead, let's use some existing tools. Since under the hood Cloud Functions is using Flask, this means that the [`render_template_string`](http://flask.pocoo.org/docs/1.0/api/#flask.render_template) function is available to us. This lets us render a [Jinja2](http://jinja.pocoo.org/docs/) template in a similar way as our custom solution, but also lets us do loops, if statements, etc:

```python
from flask import render_template_string

INDEX_TEMPLATE = """
<!DOCTYPE html>
<head>
  <title>Saying Hello</title>
</head>
<body>
  {% for place in places %}
  Hello, <b>{{ place }}!</b><br>
  {% endfor %}
</body>
"""

def hello(request):
    return render_template_string(
        INDEX_TEMPLATE, places=["World", "dev.to"]
    )
```

This would produce a response such as:

```
HTTP/1.0 200 OK
Content-Length: 140
Content-Type: text/html; charset=utf-8

<!DOCTYPE html>
<head>
  <title>Saying Hello</title>
</head>
<body>
  Hello, <b>World!</b><br>
  Hello, <b>dev.to!</b><br>
</body>
```

# Templates directory

Finally, perhaps your template is quite long and you don't want it to exist in the same file as your function, you can put it in a directory called `templates` and use the [`render_template`](http://flask.pocoo.org/docs/1.0/api/#flask.render_template) function instead.

Your source files should look something like this:

```
├── main.py
├── requirements.txt
└── templates
    └── hello.html
```

With `hello.html` being the same as our previous example:

```html
<!DOCTYPE html>
<head>
  <title>Saying Hello</title>
</head>
<body>
  {% for place in places %}
  Hello, <b>{{ place }}!</b><br>
  {% endfor %}
</body>
```

And the function configured like this:

```python
from flask import render_template

def hello(request):
    return render_template('hello.html', places=["World", "dev.to"])
```

And that's it! From here, you can use all of Jinja2's features that you normally would in any other template.