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
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
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
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