Skip to content

Commit 491ccbd

Browse files
committed
Added a new example using Falcon under the /examples/falcon_sqlalchemy directory.
1 parent 33d5b74 commit 491ccbd

23 files changed

+3291
-0
lines changed

examples/falcon_sqlalchemy/README.md

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
# demo-graphql-sqlalchemy-falcon
2+
3+
## Overview
4+
5+
This is a simple project demonstrating the implementation of a GraphQL server in Python using:
6+
7+
- [SQLAlchemy](https://github.com/zzzeek/sqlalchemy).
8+
- [Falcon](https://github.com/falconry/falcon).
9+
- [Graphene](https://github.com/graphql-python/graphene).
10+
- [Graphene-SQLAlchemy](https://github.com/graphql-python/graphene-sqlalchemy).
11+
- [Gunicorn](https://github.com/benoitc/gunicorn).
12+
13+
The objective is to demonstrate how these different libraries can be integrated.
14+
15+
## Features
16+
17+
The primary feature offered by this demo are:
18+
19+
- [SQLAlchemy](https://github.com/zzzeek/sqlalchemy) ORM against a local SQLite database. The ORM is super simple but showcases a many-to-many relationship between `authors` and `books` via an `author_books` association table.
20+
- [Falcon](https://github.com/falconry/falcon) resources to serve both [GraphQL](https://github.com/facebook/graphql) and [GraphiQL](https://github.com/graphql/graphiql).
21+
22+
> The [Falcon](https://github.com/falconry/falcon) resources are slightly modified versions of the ones under [https://github.com/alecrasmussen/falcon-graphql-server](https://github.com/alecrasmussen/falcon-graphql-server) so all credits to [Alec Rasmussen](https://github.com/alecrasmussen).
23+
24+
- Basic [GraphQL](https://github.com/facebook/graphql) schema automatically derived from the [SQLAlchemy](https://github.com/zzzeek/sqlalchemy) ORM via [Graphene](https://github.com/graphql-python/graphene) and [Graphene-SQLAlchemy](https://github.com/graphql-python/graphene-sqlalchemy).
25+
- API setup via [Falcon](https://github.com/falconry/falcon) with the whole thing served via [Gunicorn](https://github.com/benoitc/gunicorn).
26+
27+
## Usage
28+
29+
All instructions and commands below are meant to be run from the root dir of this repo.
30+
31+
### Prerequisites
32+
33+
You are strongly encouraged to use a virtualenv here but I can be assed writing down the instructions for that.
34+
35+
Install all requirements through:
36+
37+
```
38+
pip install -r requirements.txt
39+
```
40+
41+
### Sample Database
42+
43+
The sample SQLite database has been committed in this repo but can easily be rebuilt through:
44+
45+
```
46+
python -m demo.orm
47+
```
48+
49+
at which point it will create a `demo.db` in the root of this repo.
50+
51+
> The sample data are defined under `data.py` while they're ingested with the code under the `main` sentinel in `orm.py`. Feel free to tinker.
52+
53+
### Running Server
54+
55+
The [Gunicorn](https://github.com/benoitc/gunicorn) is configured via the `gunicorn_config.py` module and binds by default to `localhost:5432/`. You can change all gunicorn configuration options under the aforementioned module.
56+
57+
The server can be run through:
58+
59+
```
60+
gunicorn -c gunicorn_config.py "demo.demo:main()"
61+
```
62+
63+
The server exposes two endpoints:
64+
65+
- `/graphql`: The standard GraphQL endpoint which can receive the queries directly (accessible by default under [http://localhost:5432/graphql](http://localhost:5432/graphql)).
66+
- `/graphiql`: The [GraphiQL](https://github.com/graphql/graphiql) interface (accessible by default under [http://localhost:5432/graphiql](http://localhost:5432/graphiql)).
67+
68+
### Queries
69+
70+
Here's a couple example queries you can either run directly in [GraphiQL](https://github.com/graphql/graphiql) or by performing POST requests against the [GraphQL](https://github.com/facebook/graphql) server.
71+
72+
#### Get an author by ID
73+
74+
Query:
75+
76+
```
77+
query getAuthor{
78+
author(authorId: 1) {
79+
nameFirst,
80+
nameLast
81+
}
82+
}
83+
```
84+
85+
Response:
86+
87+
```
88+
{
89+
"data": {
90+
"author": {
91+
"nameFirst": "Robert",
92+
"nameLast": "Jordan"
93+
}
94+
}
95+
}
96+
```
97+
98+
#### Get an author by first name
99+
100+
```
101+
query getAuthor{
102+
author(nameFirst: "Robert") {
103+
nameFirst,
104+
nameLast
105+
}
106+
}
107+
```
108+
109+
Response:
110+
111+
```
112+
{
113+
"data": {
114+
"author": {
115+
"nameFirst": "Robert",
116+
"nameLast": "Jordan"
117+
}
118+
}
119+
}
120+
```
121+
122+
### Get an author and their books
123+
124+
Query:
125+
126+
```
127+
query getAuthor{
128+
author(nameFirst: "Brandon") {
129+
nameFirst,
130+
nameLast,
131+
books {
132+
title,
133+
year
134+
}
135+
}
136+
}
137+
```
138+
139+
Response:
140+
141+
```
142+
{
143+
"data": {
144+
"author": {
145+
"nameFirst": "Brandon",
146+
"nameLast": "Sanderson",
147+
"books": [
148+
{
149+
"title": "The Gathering Storm",
150+
"year": 2009
151+
},
152+
{
153+
"title": "Towers of Midnight",
154+
"year": 2010
155+
},
156+
{
157+
"title": "A Memory of Light",
158+
"year": 2013
159+
}
160+
]
161+
}
162+
}
163+
}
164+
```
165+
166+
#### Get books by year
167+
168+
Query:
169+
170+
```
171+
query getBooks{
172+
books(year: 1990) {
173+
title,
174+
year
175+
}
176+
}
177+
```
178+
179+
Response:
180+
181+
```
182+
{
183+
"data": {
184+
"books": [
185+
{
186+
"title": "The Eye of the World",
187+
"year": 1990
188+
},
189+
{
190+
"title": "The Great Hunt",
191+
"year": 1990
192+
}
193+
]
194+
}
195+
}
196+
```
197+
198+
#### Get books and their authors by their title
199+
200+
Query:
201+
202+
```
203+
query getBooks{
204+
books(title: "A Memory of Light") {
205+
title,
206+
year,
207+
authors {
208+
nameFirst,
209+
nameLast
210+
}
211+
}
212+
}
213+
```
214+
215+
Response:
216+
217+
```
218+
{
219+
"data": {
220+
"books": [
221+
{
222+
"title": "A Memory of Light",
223+
"year": 2013,
224+
"authors": [
225+
{
226+
"nameFirst": "Robert",
227+
"nameLast": "Jordan"
228+
},
229+
{
230+
"nameFirst": "Brandon",
231+
"nameLast": "Sanderson"
232+
}
233+
]
234+
}
235+
]
236+
}
237+
}
238+
```
239+
240+
#### Get number of books by cover-artist
241+
242+
Query:
243+
244+
```
245+
query getCountBooksByCoverArtist{
246+
stats {
247+
countBooksByCoverArtist {
248+
coverArtist,
249+
countBooks
250+
}
251+
}
252+
}
253+
```
254+
255+
Response:
256+
257+
```
258+
{
259+
"data": {
260+
"stats": {
261+
"countBooksByCoverArtist": [
262+
{
263+
"coverArtist": null,
264+
"countBooks": 1
265+
},
266+
{
267+
"coverArtist": "Darrell K. Sweet",
268+
"countBooks": 12
269+
},
270+
{
271+
"coverArtist": "Michael Whelan",
272+
"countBooks": 1
273+
}
274+
]
275+
}
276+
}
277+
}
278+
```
279+
280+
#### Add new author
281+
282+
Query:
283+
284+
```
285+
mutation createAuthor{
286+
createAuthor(author: {
287+
nameFirst: "First Name",
288+
nameLast: "Last Name"
289+
}) {
290+
author {
291+
authorId
292+
nameFirst
293+
nameLast
294+
}
295+
}
296+
}
297+
```
298+
299+
Response:
300+
301+
```
302+
{
303+
"data": {
304+
"createAuthor": {
305+
"author": {
306+
"authorId": "3",
307+
"nameFirst": "First Name",
308+
"nameLast": "Last Name"
309+
}
310+
}
311+
}
312+
}
313+
```

examples/falcon_sqlalchemy/demo.db

24 KB
Binary file not shown.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# coding=utf-8
2+
3+
from demo import api
4+
from demo import data
5+
from demo import demo
6+
from demo import orm
7+
from demo import orm_base
8+
from demo import resources
9+
from demo import schema
10+
from demo import schema_types
11+
from demo import utils
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# coding=utf-8
2+
3+
import sqlalchemy.orm
4+
import falcon
5+
import graphene
6+
7+
from demo.resources import ResourceGraphQlSqlAlchemy
8+
from demo.resources import ResourceGraphiQL
9+
10+
11+
def create_app(
12+
schema: graphene.Schema,
13+
scoped_session: sqlalchemy.orm.scoped_session,
14+
do_enable_graphiql: bool,
15+
):
16+
# Create the API.
17+
app = falcon.API()
18+
19+
app.add_route(
20+
uri_template="/graphql",
21+
resource=ResourceGraphQlSqlAlchemy(
22+
schema=schema,
23+
scoped_session=scoped_session,
24+
)
25+
)
26+
27+
if do_enable_graphiql:
28+
app.add_route(
29+
uri_template="/graphiql/",
30+
resource=ResourceGraphiQL(
31+
path_graphiql="graphiql",
32+
)
33+
)
34+
app.add_route(
35+
uri_template="/graphiql/{static_file}",
36+
resource=ResourceGraphiQL(
37+
path_graphiql="graphiql",
38+
)
39+
)
40+
41+
return app

0 commit comments

Comments
 (0)