Deploy a Python serverless function on ZEIT Now
Juan Olvera
Posted on September 24, 2019
Let's write an API endpoint using a Python serverless function that will give us DNS records in JSON format from a given domain. We will send a GET request with a query parameter named "domain", i.e.,
GET https://localhost:3000/api?domain="jolvera.dev"
The tools we are going to use are a ZEIT account, npm, now-cli and a browser. I will assume that you already have npm and a browser installed.
The API that we are going to write is actually how I started the prototype of DNS check. DNS check was born out of the necessity to explain clients and co-workers what's happening with a domain when we make a DNS change. Tools already exist that provide DNS lookup information, but most of them aren't simple enough, in my opinion.
We only need two dependencies for this project, Bottle and dnspython.
Create the Python function
The first step is to create a function that returns the DNS records (A, CNAME, MX, for example.) of a given domain name using dnspython.
import dns.resolver
ids = [
"A",
"NS",
"CNAME",
"SOA",
"MX",
"TXT",
"AAAA",
]
def resolve(domain: str):
result = []
for record in ids:
try:
answers = dns.resolver.query(domain, record)
data = [{"record": record, "value": rdata.to_text()} for rdata in answers]
for item in data:
result.append(item)
except Exception as e:
print(e)
return result
print(resolve('jolvera.dev'))
Before creating any files in our computer, let's try this function in Repl.it. If everything is okay you will get something like:
[
{
'record': 'A',
'value': '3.19.25.128'
},
{
'record': 'A',
'value': '3.19.23.166'
},
{
'record': 'NS',
'value': 'b.zeit-world.org.'
},
{
'record': 'NS',
'value': 'e.zeit-world.net.'
},
{
'record': 'NS',
'value': 'f.zeit-world.com.'
},
{
'record': 'NS',
'value': 'a.zeit-world.co.uk.'
},
{
'record': 'MX',
'value': '10aspmx1.migadu.com.'
},
{
'record': 'TXT',
'value': '"v=spf1 a mx include:spf.migadu.com ~all"'
}
]
Try with different domains, make sure it works, then proceed to create the API endpoint.
Convert the function to a Serverless function
The serverless platform we are going to use is ZEIT Now. They make serverless development extremely easy and projects deployments a joy.
To follow this example, create an account at zeit.co and install Now globally in your system by running npm i -g now
, and with that you are ready to go.
Folder structure
Create a folder and call it dnscheck
then create another folder inside called api
.
$ mkdir dnscheck
$ mkdir dnscheck/api
This folder structure is all you need.
Virtual environment
Python best practices say we need a virtual environment, so we don't install our dependencies globally in the system (Like what we did with npm i -g now
but in the Python world).
In this example, I'm going to use virtualenv
, but you can use Poetry or Pipenv as well. I chose virtualenv
here to keep things simple.
Inside the dnscheck/api
folder, create a new virtual environment.
$ cd dnscheck/api
(dnscheck/api) $ virtualenv venv
Then, activate the virtual environment.
(dnscheck/api) $ ./venv/bin/activate
With this, we have our virtual environment ready to go, and we can start installing dependencies that are only available in our project. We start by installing dnspython
and bottle
.
(dnscheck/api) $ pip install bottle dnspython
Then we create a requirements.txt
file that lists all the dependencies we are using. Now uses this to install the dependencies while it's deploying.
(dnscheck/api) $ pip freeze > requirements.txt
API endpoint with Bottle
Inside the api/
folder, create a file named index.py
. Inside write the following code.
from bottle import Bottle, request
import dns.resolver
app = Bottle()
ids = [
"A",
"NS",
"CNAME",
"SOA",
"MX",
"TXT",
"AAAA",
]
@app.get('/api')
def api():
domain = request.query.get('domain')
result = resolve(domain)
return dict(data=result)
def resolve(domain: str):
result = []
for record in ids:
try:
answers = dns.resolver.query(domain, record)
data = [{"record": record, "value": rdata.to_text()}
for rdata in answers]
for item in data:
result.append(item)
except Exception as e:
print(e)
return result
This code listens for GET requests in http://localhost:3000/api
and tries to grab a query parameter named domain
, so to be able to get the data we have to send a get request to http://localhost:3000/api?domain=jolvera.dev or simply open the URL in the browser.
To test it locally, inside the dnscheck/
folder run now dev
,
(dnscheck/api) $ cd ..
(dnscheck) $ now dev
if everything is okay, you should get the development URL.
> Ready! Available at http://localhost:3000
If so, go to your browser and visit http://localhost:3000/api?domain=jolvera.dev and you should see a response in JSON like shown in the following screenshot.
Deploy to Now
Everything is ready to deploy our serverless function to Now and share it to the world!
Inside the dnscheck/
folder run now
(dnscheck) $ now
and If everything goes okay, we will get a URL like https://dnscheck-example-2k7pyj3cr.now.sh/, in my case I got https://dnscheck-post-example.jolvera.now.sh/, and you can test my domain going to https://dnscheck-post-example.jolvera.now.sh/api?domain=jolvera.dev.
Conclusion
We were able to prototype a small idea into a workable API and share it on the web with ease. What we did in this post was the premise of my project and the train of thought I had to get something working. After I got the example working, I kept trying new ideas on it and added domain validation, a Next.js frontend and more.
Being able to prototype an idea into an API with these few tools encourage me to try new things more often. Once you get a first working example you will get motivate to keep going and build lots of things.
You can check my finished project dnscheck.app in these two repositories:
Posted on September 24, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.