Replicating GraphQL using REST, Piccolo, and FastAPI
GraphQL is a really powerful approach to building APIs. It allows clients to specify exactly what data they want. Contrast this with most REST APIs, where a given endpoint typically returns the data in the same structure each time.
The advantages of being able to request exactly the data we need are:
- Less data needs to be transferred over the network.
- By giving the client more flexibility, it's less likely a backend engineer will have to make arbitrary changes to the API (for example, adding / removing fields).
- Potentially less load on the API server, if it only has to return what's needed, and not additional data.
The downside though is GraphQL is quite a big investment in terms of setup and learning. Also, a lot of companies already have REST APIs. What if we can replicate some of the advantages of GraphQL using REST? Enter Piccolo and FastAPI.
PiccoloCRUD
Piccolo has a class called PiccoloCRUD which basically makes a super endpoint from a Piccolo table. As the name suggests, it supports all of the CRUD operations, and some really powerful filtering.
We recently made some big improvements - namely, being able to request specific fields, and even doing joins. It also integrates seamlessly with FastAPI, so the endpoint has automatic Swagger docs.
It's as simple as this:
from fastapi import FastAPI
from piccolo_api.crud.endpoints import PiccoloCRUD
from piccolo_api.fastapi.endpoints import FastAPIWrapper
from movies.tables import Movie
app = FastAPI()
FastAPIWrapper(
"/movies/",
app,
PiccoloCRUD(Movie, read_only=True, exclude_secrets=True, max_joins=1),
)
Here is the schema we're using:
from piccolo.columns import (
ForeignKey,
Integer,
Real,
Varchar,
)
from piccolo.table import Table
class Director(Table):
name = Varchar(length=300, null=False)
net_worth = Integer(secret=True, help_text="In millions")
class Movie(Table):
name = Varchar(length=300)
rating = Real(help_text="The rating on IMDB.")
director = ForeignKey(references=Director)
You can get the entire source code on GitHub.
Trying it out
If we query the endpoint, we get a response like:
GET /movies/
{
"rows": [
{
"id": 1
"name": "Star Wars: A New Hope",
"rating": 8.6,
"director": 1
}
]
}
Now let's try fetching a subset of fields, using the __visible_fields
parameter:
GET /movies/?__visible_fields=name,director.name
{
"rows": [
{
"name": "Star Wars: A New Hope",
"director": {
"name": "George Lucas"
}
}
]
}
Note how we got a nested object when we specified director.name
as a field name, as it belongs to a related table. Piccolo performs the necessary joins under the hood.
You can also try this out via FastAPI's Swagger docs:
Security
You'll notice in the table definition that we designated the Director.net_worth
column as secret=True
. What this means is the value returned by the API is only ever null
.
It means we can shield sensitive information from clients if we want to.
We can also limit the number of joins which are allowed, using the max_joins
parameter on PiccoloCRUD
. This prevents clients from doing queries which are overly complex, and would potentially slow down our API.
Conclusions
I hope this illustrates how powerful PiccoloCRUD
is, and how we're able to build something with very little code which approximates what GraphQL can do.
It's a great way of rapidly building an API, which could save on some bandwidth too!
Posted on: 3 Dec 2021
Have any comments or feedback on this post? Chat with us on GitHub.