Serverless GraphQL API on Azure using Python

GraphQL is one of the most popular query and manipulation languages for APIs. There are various libraries to build GraphQL API in different programming languages. With the popularity of serverless architecture on cloud infrastructure, serverless GraphQL API is becomming more and more common. Azure does not offer any fully managed services such as AWS AppSync, so Azure function is currently a very well accepted solution for GraphQL API on Azure. This post provides the details to create serverless GraphQL API on Azure using Python

Prerequisites

  • Understanding of web APIs and its role in a web architecture
  • Understanding of GraphQL fundamentals
  • Experience with Python programming language (Python3)
  • Understanding of Azure Functions and experience building it using Python

Schema-first vs Code-first

There are mainly two approaches to build GraphQL API, schema-first and code-first. In schema-first approach, schema is created using SDL (Schema Definition Language) first and then you write resolvers in the programming language of your choice. Compared to that, you do not define the SDL schema in the code-first approach. Instead, you write it as code in the programming language of your choice. Ariadne and Graphene are popular Python libraries for building GraphQL API using schema-first and code-first approach respectively. This post provides the details for building serverless GraphQL API on Azure for both the approaches using Ariadne and Graphene

WSGI vs ASGI

WSGI (Web Server Gateway Interface) and ASGI (Asynchronous Server Gateway Interface) are the two standards to interface between a Python application and a Python webserver. These standards play an important role for a web application built in Python. ASGI is a successor to WSGI and provides the backward compatibility to replace existing applications in WSGI. This post provides code details for both WSGI and ASGI

The API implementation

Create Azure function for Python

func init GraphQLServer-AzureFunc --python

Create HTTP trigger inside the function so that you can generate GraphQL API endpoint

func new --name GraphQLAPI --template "HTTP trigger" --authlevel "function"Code language: JavaScript (javascript)

Run the function to check that HTTP trigger works,

func start

Schema-first approach and WSGI or ASGI standard

Install Ariadne to use schema-first approach and WSGI or ASGI standard. This will allow you to import the modules and classes from Ariadne

pip install ariadne

Create Schemas folder under the GraphQL API where you can locate schema files

Serverless GraphQL API on Azure, folder structure

Create schema.graphql file in the folder. This file will have GraphQL types, queries, mutations, etc. This folder can include multiple .graphql files to distribute the schema

type Query {
        hello: String!
        helloThere: String!
    }Code language: CSS (css)

Create resolvers.py file under GraphQLAPI. This file will include Python code to process queries and mutations

Serverless GraphQL API on Azure, folder structure
from ariadne import QueryType

query = QueryType()

@query.field("hello")
async def resolve_hello(_, info):
    return "Hello!"

@query.field("helloThere")
async def resolve_helloThere(_, info):
    return "Hello There!"

resolvers = [query]Code language: JavaScript (javascript)

If you choose ASGI standard for the API, use async keyword in the function definition. This snippet has basic schema and resolver to provide general understanding. You can add more complex schema and connect to a back-end database. I have developed few APIs in both Typescript and Python. Please refer to my another post to optimize resolvers using info object

Go to __init__.py and remove existing code from the main function. Add the following code in __init__.py to use ASGI standard

import azure.functions as func
from azure.functions import AsgiMiddleware

from ariadne import make_executable_schema, load_schema_from_path
from ariadne.Asgi import GraphQL
from GraphQLAPI.resolvers import resolvers

schema = make_executable_schema(
    load_schema_from_path('./GraphQLAPI/Schemas/'), 
    resolvers)
app = GraphQL(schema, debug=True)

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    return AsgiMiddleware(app).handle(req, context)Code language: JavaScript (javascript)

Or add the following code to __init__.py to use WSGI standard

import azure.functions as func
from azure.functions import WsgiMiddleware

from ariadne import make_executable_schema, load_schema_from_path
from ariadne.wsgi import GraphQL
from GraphQLAPI.resolvers import resolvers

schema = make_executable_schema(
    load_schema_from_path('./GraphQLAPI/Schemas/'), 
    resolvers)
app = GraphQL(schema, debug=True)

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    return WsgiMiddleware(app).handle(req, context)Code language: JavaScript (javascript)

Please go to following GitHub repository for the source code,

https://github.com/vizeit/GraphQLServer-Ariadne-AzureFunc

Code-first approach and ASGI standard

Install Graphene and Starlette for Graphene to use code-first approach and ASGI standard

pip install graphene starlette-graphene3

Create Schemas folder under the GraphQLAPI to modularize the code

Serverless GraphQL API on Azure, folder structure

Create Python files for GraphQL types, queries, mutations, etc. This example includes the code in Queries.py

from graphene import ObjectType, String

class Query(ObjectType):
    
    hello = String(name=String(default_value="stranger"))
    goodbye = String()

    def resolve_hello(root, info, name):
        return f'Hello {name}!'

    def resolve_goodbye(root, info):
        return 'See ya!'

Go to __init__.py and remove existing code from the main function and add the following code

import azure.functions as func
from azure.functions import AsgiMiddleware

from graphene import Schema
from starlette_graphene3 import GraphQLApp, make_graphiql_handler
from .Schemas.Queries import Query

schema = Schema(query=Query)
app = GraphQLApp(schema, on_get=make_graphiql_handler())

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    return AsgiMiddleware(app).handle(req, context)
Code language: JavaScript (javascript)

Please go to following GitHub repository for the source code,

https://github.com/vizeit/GraphQLServer-Graphene-AzureFunc

Execute the function locally after building the function with schema-first or code-first approach

func start

Invoke the trigger URL to load the GraphiQL playground so that you can test the queries