-
Notifications
You must be signed in to change notification settings - Fork 408
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
903 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
.. _ref_quickstart_fastapi_connecting: | ||
|
||
========================== | ||
Connecting to the database | ||
========================== | ||
|
||
.. edb:split-section:: | ||
Before diving into the application, let's take a quick look at how to connect to the database from your code. We will intialize a client and use it to make a simple, static query to the database, and log the result to the console. | ||
|
||
.. note:: | ||
|
||
Notice that the ``create_async_client`` function isn't being passed any connection details. With |Gel|, you do not need to come up with your own scheme for how to build the correct database connection credentials and worry about leaking them into your code. You simply use |Gel| "projects" for local development, and set the appropriate environment variables in your deployment environments, and the ``create_async_client`` function knows what to do! | ||
|
||
.. edb:split-point:: | ||
.. code-block:: python | ||
:caption: ./test.py | ||
import gel | ||
import asyncio | ||
async def main(): | ||
client = gel.create_async_client() | ||
result = await client.query_single("select 'Hello from Gel!';") | ||
print(result) | ||
asyncio.run(main()) | ||
.. code-block:: sh | ||
$ python test.py | ||
Hello from Gel! | ||
.. edb:split-section:: | ||
In Python, we write EdgeQL queries directly as strings. This gives us the full power and expressiveness of EdgeQL while maintaining type safety through Gel's strict schema. Let's try inserting a few ``Deck`` objects into the database and then selecting them back. | ||
|
||
.. edb:split-point:: | ||
.. code-block:: python-diff | ||
:caption: ./test.py | ||
import gel | ||
import asyncio | ||
async def main(): | ||
client = gel.create_async_client() | ||
- result = await client.query_single("select 'Hello from Gel!';") | ||
- print(result) | ||
+ await client.query(""" | ||
+ insert Deck { name := "I am one" } | ||
+ """) | ||
+ | ||
+ await client.query(""" | ||
+ insert Deck { name := "I am two" } | ||
+ """) | ||
+ | ||
+ decks = await client.query(""" | ||
+ select Deck { | ||
+ id, | ||
+ name | ||
+ } | ||
+ """) | ||
+ | ||
+ for deck in decks: | ||
+ print(f"ID: {deck.id}, Name: {deck.name}") | ||
+ | ||
+ await client.query("delete Deck") | ||
asyncio.run(main()) | ||
.. code-block:: sh | ||
$ python test.py | ||
Hello from Gel! | ||
ID: f4cd3e6c-ea75-11ef-83ec-037350ea8a6e, Name: I am one | ||
ID: f4cf27ae-ea75-11ef-83ec-3f7b2fceab24, Name: I am two |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
.. edb:env-switcher:: | ||
========== | ||
Connecting | ||
========== | ||
========================== | ||
Connecting to the database | ||
========================== | ||
|
||
.. toctree:: | ||
:maxdepth: 3 | ||
:hidden: | ||
|
||
nextjs | ||
fastapi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
.. _ref_quickstart_fastapi_inheritance: | ||
|
||
======================== | ||
Adding shared properties | ||
======================== | ||
|
||
.. edb:split-section:: | ||
One common pattern in applications is to add shared properties to the schema that are used by multiple objects. For example, you might want to add a ``created_at`` and ``updated_at`` property to every object in your schema. You can do this by adding an abstract type and using it as a mixin for your other object types. | ||
|
||
.. code-block:: sdl-diff | ||
:caption: dbschema/default.gel | ||
module default { | ||
+ abstract type Timestamped { | ||
+ required created_at: datetime { | ||
+ default := datetime_of_statement(); | ||
+ }; | ||
+ required updated_at: datetime { | ||
+ default := datetime_of_statement(); | ||
+ }; | ||
+ } | ||
+ | ||
- type Deck { | ||
+ type Deck extending Timestamped { | ||
required name: str; | ||
description: str; | ||
cards := ( | ||
select .<deck[is Card] | ||
order by .order | ||
); | ||
}; | ||
- type Card { | ||
+ type Card extending Timestamped { | ||
required order: int64; | ||
required front: str; | ||
required back: str; | ||
required deck: Deck; | ||
} | ||
} | ||
.. edb:split-section:: | ||
Since you don't have historical data for when these objects were actually created or modified, the migration will fall back to the default values set in the ``Timestamped`` type. | ||
|
||
.. code-block:: sh | ||
$ gel migration create | ||
did you create object type 'default::Timestamped'? [y,n,l,c,b,s,q,?] | ||
> y | ||
did you alter object type 'default::Card'? [y,n,l,c,b,s,q,?] | ||
> y | ||
did you alter object type 'default::Deck'? [y,n,l,c,b,s,q,?] | ||
> y | ||
Created /home/strinh/projects/flashcards/dbschema/migrations/00004-m1d2m5n.edgeql, id: m1d2m5n5ajkalyijrxdliioyginonqbtfzihvwdfdmfwodunszstya | ||
$ gel migrate | ||
Applying m1d2m5n5ajkalyijrxdliioyginonqbtfzihvwdfdmfwodunszstya (00004-m1d2m5n.edgeql) | ||
... parsed | ||
... applied | ||
.. edb:split-section:: | ||
Update the ``get_decks`` query to sort the decks by ``updated_at`` in descending order. | ||
|
||
.. code-block:: python-diff | ||
:caption: main.py | ||
@app.get("/decks", response_model=List[Deck]) | ||
async def get_decks(): | ||
decks = await client.query(""" | ||
select Deck { | ||
id, | ||
name, | ||
description, | ||
cards := ( | ||
select .cards { | ||
id, | ||
front, | ||
back | ||
} | ||
order by .order | ||
) | ||
} | ||
+ order by .updated_at desc | ||
""") | ||
return decks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,13 @@ | ||
.. edb:env-switcher:: | ||
=========== | ||
Inheritance | ||
=========== | ||
======================== | ||
Adding shared properties | ||
======================== | ||
|
||
.. toctree:: | ||
:maxdepth: 3 | ||
:hidden: | ||
|
||
nextjs | ||
fastapi | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
.. _ref_quickstart_fastapi_modeling: | ||
|
||
================= | ||
Modeling the data | ||
================= | ||
|
||
.. edb:split-section:: | ||
The flashcards application has a simple data model, but it's interesting enough to utilize many unique features of the |Gel| schema language. | ||
|
||
Looking at the mock data in the example JSON file ``./deck-edgeql.json``, you can see this structure in the JSON. There is a ``Card`` class that describes a single flashcard, which contains two required string properties: ``front`` and ``back``. Each ``Deck`` object has zero or more ``Card`` objects in a list. | ||
|
||
.. code-block:: python | ||
from pydantic import BaseModel | ||
class CardBase(BaseModel): | ||
front: str | ||
back: str | ||
class Card(CardBase): | ||
id: str | ||
class DeckBase(BaseModel): | ||
name: str | ||
description: Optional[str] = None | ||
class Deck(DeckBase): | ||
id: str | ||
cards: List[Card] | ||
.. edb:split-section:: | ||
Starting with this simple model, add these types to the :dotgel:`dbschema/default` schema file. As you can see, the types closely mirror the JSON mock data. | ||
|
||
Also of note, the link between ``Card`` and ``Deck`` objects creates a "1-to-n" relationship, where each ``Deck`` object has a link to zero or more ``Card`` objects. When you query the ``Deck.cards`` link, the cards will be unordered, so the ``Card`` type needs an explicit ``order`` property to allow sorting them at query time. | ||
|
||
By default, when you try to delete an object that is linked to another object, the database will prevent you from doing so. We want to support removing a ``Card``, so we define a deletion policy on the ``cards`` link that allows deleting the target of this link. | ||
|
||
.. code-block:: sdl-diff | ||
:caption: dbschema/default.gel | ||
module default { | ||
+ type Card { | ||
+ required order: int64; | ||
+ required front: str; | ||
+ required back: str; | ||
+ }; | ||
+ | ||
+ type Deck { | ||
+ required name: str; | ||
+ description: str; | ||
+ multi cards: Card { | ||
+ constraint exclusive; | ||
+ on target delete allow; | ||
+ }; | ||
+ }; | ||
}; | ||
.. edb:split-section:: | ||
Congratulations! This first version of the data model's schema is *stored in a file on disk*. Now you need to signal the database to actually create types for ``Deck`` and ``Card`` in the database. | ||
|
||
To make |Gel| do that, you need to do two quick steps: | ||
|
||
1. **Create a migration**: a "migration" is a file containing a set of low level instructions that define how the database schema should change. It records any additions, modifications, or deletions to your schema in a way that the database can understand. | ||
|
||
.. note:: | ||
|
||
When you are changing existing schema, the CLI migration tool might ask questions to ensure that it understands your changes exactly. Since the existing schema was empty, the CLI will skip asking any questions and simply create the migration file. | ||
|
||
2. **Apply the migration**: This executes the migration file on the database, instructing |Gel| to implement the recorded changes in the database. Essentially, this step updates the database structure to match your defined schema, ensuring that the ``Deck`` and ``Card`` types are created and ready for use. | ||
|
||
.. code-block:: sh | ||
$ uvx gel migration create | ||
Created ./dbschema/migrations/00001-m125ajr.edgeql, id: m125ajrbqp7ov36s7aniefxc376ofxdlketzspy4yddd3hrh4lxmla | ||
$ uvx gel migrate | ||
Applying m125ajrbqp7ov36s7aniefxc376ofxdlketzspy4yddd3hrh4lxmla (00001-m125ajr.edgeql) | ||
... parsed | ||
... applied | ||
.. edb:split-section:: | ||
Take a look at the schema you've generated in the built-in database UI. Use this tool to visualize your data model and see the object types and links you've defined. | ||
|
||
.. edb:split-point:: | ||
.. code-block:: sh | ||
$ uvx gel ui | ||
.. image:: images/schema-ui.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,12 @@ | ||
.. edb:env-switcher:: | ||
======== | ||
Modeling | ||
======== | ||
================= | ||
Modeling the data | ||
================= | ||
|
||
.. toctree:: | ||
:maxdepth: 3 | ||
:hidden: | ||
|
||
nextjs | ||
|
||
|
||
fastapi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
.. _ref_quickstart_fastapi: | ||
|
||
========== | ||
Quickstart | ||
========== | ||
|
||
Welcome to the quickstart tutorial! In this tutorial, you will update a FastAPI | ||
backend for a Flashcards application to use |Gel| as your data layer. The | ||
application will let users build and manage their own study decks, with each | ||
flashcard featuring customizable text on both sides - making it perfect for | ||
studying, memorization practice, or creating educational games. | ||
|
||
Don't worry if you're new to |Gel| - you will be up and running with a working | ||
FastAPI backend and a local |Gel| database in just about **5 minutes**. From | ||
there, you will replace the static mock data with a |Gel| powered data layer in | ||
roughly 30-45 minutes. | ||
|
||
By the end of this tutorial, you will be comfortable with: | ||
|
||
* Creating and updating a database schema | ||
* Running migrations to evolve your data | ||
* Writing EdgeQL queries | ||
* Building an app backed by |Gel| | ||
|
||
|
||
Features of the flashcards app | ||
------------------------------ | ||
|
||
* Create, edit, and delete decks | ||
* Add/remove cards with front/back content | ||
* Clean, type-safe schema with |Gel| | ||
|
||
Requirements | ||
------------ | ||
|
||
Before you start, you need: | ||
|
||
* Basic familiarity with Python and FastAPI | ||
* Python 3.8+ on a Unix-like OS (Linux, macOS, or WSL) | ||
* A code editor you love | ||
|
||
Why |Gel| for FastAPI? | ||
---------------------- | ||
|
||
* **Type Safety**: Catch data errors before runtime | ||
* **Rich Modeling**: Use object types and links to model relations | ||
* **Modern Tooling**: Python-friendly schemas and migrations | ||
* **Performance**: Efficient queries for complex data | ||
* **Developer Experience**: An intuitive query language (EdgeQL) | ||
|
||
Need Help? | ||
---------- | ||
|
||
If you run into issues while following this tutorial: | ||
|
||
- Check the `|Gel| documentation <https://docs.geldata.com>`_ | ||
- Visit our `community Discord <https://discord.gg/gel>`_ | ||
- File an issue on `GitHub <https://github.com/geldata/gel>`_ |
Oops, something went wrong.