Flaxon 0.3.0 and Teloce-Py 0.3.0 are available on PyPI. Release instructions are maintained on this site; source links identify the framework snapshot. Check version and upgrade guidance.
GraphQL API Example
This example demonstrates a complete Flaxon GraphQL API with:
- Queries
- Mutations
- GraphQL types with resolvers
- GraphiQL playground
Running the Example
Create a new Flaxon project:
flaxon new graphql-example
cd graphql-example
Install dependencies:
pip install flaxon
Create app.py with the code below.
Run:
flaxon run app:app --reload
The CLI handles host/port automatically (defaults to 127.0.0.1:8000) —
app.py just needs to define app at module level. No app.run(...) call
or if __name__ == "__main__" block is needed (there is no app.run()
method on the Flaxon app object).
Application Code
app.py
from flaxon import Flaxon
from flaxon.graphql import GraphQLSchema, ObjectType, Field, List
from flaxon.graphql.scalars import ID
app = Flaxon("graphql-example", debug=True)
# --------------------
# Data Storage
# --------------------
users = [{"id": 1, "name": "Alice", "email": "alice@example.com"}]
posts = [{"id": 1, "title": "First Post", "content": "Hello GraphQL", "author_id": 1}]
user_id_counter = 2
post_id_counter = 2
# --------------------
# GraphQL Types
# --------------------
# ObjectType(name, fields_dict) is a plain instance built from a dict of
# Field(...) objects — it is NOT meant to be subclassed. There is no
# built-in String/Int scalar; use plain Python types (str, int, float) as
# field types, or the real scalars (ID, DateTime, Decimal, JSON, UUID,
# URL, Email) from flaxon.graphql.scalars.
#
# fields dicts are mutable after construction, which lets us resolve the
# User <-> Post forward reference below.
def resolve_user_posts(parent, args, context, info):
return [p for p in posts if p["author_id"] == parent["id"]]
# Build Post first, without "author" (it depends on UserType, defined next).
PostType = ObjectType("Post", {
"id": Field(ID),
"title": Field(str),
"content": Field(str),
"authorId": Field(ID, resolver=lambda parent, args, ctx, info: parent["author_id"]),
})
UserType = ObjectType("User", {
"id": Field(ID),
"name": Field(str),
"email": Field(str),
"posts": Field(List(PostType), resolver=resolve_user_posts),
})
def resolve_post_author(parent, args, context, info):
return next((u for u in users if u["id"] == parent["author_id"]), None)
# Patch the forward reference in now that UserType exists.
PostType.fields["author"] = Field(UserType, resolver=resolve_post_author)
PostType.fields["author"].name = "author"
# --------------------
# Queries
# --------------------
# Root-level fields (on Query/Mutation) MUST declare a resolver — there is
# no parent object to fall back to at the root, unlike nested fields on a
# dict (e.g. "id"/"name" above resolve automatically from the dict keys).
def resolve_hello(parent, args, context, info):
return f"Hello {args.get('name', 'World')}!"
def resolve_users(parent, args, context, info):
return users
def resolve_posts(parent, args, context, info):
return posts
Query = ObjectType("Query", {
"hello": Field(str, args={"name": str}, resolver=resolve_hello),
"users": Field(List(UserType), resolver=resolve_users),
"posts": Field(List(PostType), resolver=resolve_posts),
})
# --------------------
# Mutations
# --------------------
def resolve_create_user(parent, args, context, info):
global user_id_counter
user = {"id": user_id_counter, "name": args["name"], "email": args["email"]}
user_id_counter += 1
users.append(user)
return user
def resolve_create_post(parent, args, context, info):
global post_id_counter
post = {
"id": post_id_counter,
"title": args["title"],
"content": args["content"],
# GraphQL int literals arrive as strings in resolvers — cast explicitly.
"author_id": int(args["authorId"]),
}
post_id_counter += 1
posts.append(post)
return post
Mutation = ObjectType("Mutation", {
"createUser": Field(
UserType,
args={"name": str, "email": str},
resolver=resolve_create_user,
),
"createPost": Field(
PostType,
args={"title": str, "content": str, "authorId": int},
resolver=resolve_create_post,
),
})
# --------------------
# GraphQL Setup
# --------------------
# enable_graphql(schema, url, enable_playground) wires up POST /graphql
# plus a GraphiQL playground at /graphql/graphiql. It does not take a
# subscription_backend argument — subscriptions aren't wired up this way
# in this framework and need manual integration with SubscriptionManager
# and websockets (out of scope for this example).
schema = GraphQLSchema(query=Query, mutation=Mutation)
app.enable_graphql(schema, url="/graphql")
@app.get("/")
async def home():
return {
"message": "Welcome to Flaxon GraphQL",
"endpoint": "/graphql",
"playground": "/graphql/graphiql",
}
Run the example with flaxon run app:app --reload.
Testing the API
GraphiQL Playground
Open:
http://localhost:8000/graphql/graphiql
Queries
Hello
query {
hello(name: "Flaxon")
}
Get Users
query {
users {
id
name
email
posts {
id
title
}
}
}
Get Posts
query {
posts {
id
title
content
author {
name
}
}
}
Mutations
Important: this parser does not treat commas between arguments as insignificant whitespace the way standard GraphQL does. Separate arguments with spaces or newlines only — a comma causes a parse error (
Unexpected character: ,).
Create User
mutation {
createUser(name: "Bob" email: "bob@example.com") {
id
name
email
}
}
Create Post
mutation {
createPost(title: "New Post" content: "GraphQL Example" authorId: 1) {
id
title
author {
name
}
}
}
Using curl
Query
curl -X POST http://localhost:8000/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ hello(name: \"Flaxon\") }"}'
Mutation
curl -X POST http://localhost:8000/graphql \
-H "Content-Type: application/json" \
-d '{"query":"mutation { createUser(name: \"Bob\" email: \"bob@example.com\") { id name } }"}'
Production Improvements
Recommended next steps:
- Add GraphQL authentication (see
flaxon.security.login_required) - Add database integration
- Add custom scalar types beyond the built-ins (
ID,DateTime,Decimal,JSON,UUID,URL,Email) - Wire up subscriptions manually via
flaxon.graphql.subscriptions.SubscriptionManager - Deploy with a production ASGI server
Use this capability in a full-stack app
Flaxon serves Python APIs and Teloce interfaces in one project. Keep validation, authorization and privileged operations on the server; components call those endpoints and present their results. Follow the full-stack lessons and Teloce integration reference to connect this capability to pages, components and application modules.