-
Notifications
You must be signed in to change notification settings - Fork 443
fastapi quickstart #8347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
fastapi quickstart #8347
Changes from 1 commit
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
7aa6b1c
fastapi quickstart
a406dde
Update
6b06120
fix filename
ccb4096
fix codeblock
246bc07
Merge branch 'new-gel-docs' into fastapi-quickstart
beerose 05b2f67
use async client
c333220
Merge branch 'fastapi-quickstart' of github.com:edgedb/edgedb into fa…
f00a879
Merge branch 'new-gel-docs' into fastapi-quickstart
becba6c
update refs
1d1d7c4
Merge branch 'new-gel-docs' into fastapi-quickstart
scotttrinh 7c0535c
Move FastAPI into env-switcher
scotttrinh 4085d5c
Update for FastAPI <> Next.js alignment
scotttrinh 4a73306
Empty
scotttrinh 6ca8eeb
Merge branch 'new-gel-docs' into fastapi-quickstart
scotttrinh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ Get Started | |
| :hidden: | ||
|
|
||
| quickstart | ||
| quickstart/fastapi/index | ||
| cli | ||
| instances | ||
| projects | ||
|
|
||
This file contains hidden or 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,75 @@ | ||
| .. _ref_quickstart_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_client`` function isn't being passed any connection details. How does Gel know how to connect to the database you set up earlier? When we ran ``gel project init`` earlier, the CLI created credentials for the local database and stored them in a well-known location. When you initialize your client with ``create_client()``, Gel will check the places it knows about for 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 when you're ready to deploy, and the client knows what to do! | ||
|
|
||
| .. edb:split-point:: | ||
|
|
||
| .. code-block:: python | ||
| :caption: ./test.py | ||
|
|
||
| import gel | ||
|
|
||
| client = gel.create_client() | ||
|
|
||
| result = client.query_single("select 'Hello from Gel!';") | ||
| print(result) | ||
|
|
||
| .. 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 | ||
|
|
||
| client = gel.create_client() | ||
|
|
||
| - result = client.query_single("select 'Hello from Gel!';") | ||
| - print(result) | ||
| + client.query(""" | ||
| + insert Deck { name := "I am one" } | ||
| + """) | ||
| + | ||
| + client.query(""" | ||
| + insert Deck { name := "I am two" } | ||
| + """) | ||
| + | ||
| + decks = client.query(""" | ||
| + select Deck { | ||
| + id, | ||
| + name | ||
| + } | ||
| + """) | ||
| + | ||
| + for deck in decks: | ||
| + print(f"ID: {deck.id}, Name: {deck.name}") | ||
| + | ||
| + client.query("delete Deck") | ||
|
|
||
|
|
||
| .. 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 hidden or 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,71 @@ | ||
| .. _ref_quickstart_fastapi: | ||
|
|
||
| ############ | ||
| Quickstart | ||
| ############ | ||
|
|
||
| .. toctree:: | ||
| :maxdepth: 1 | ||
| :hidden: | ||
|
|
||
| setup | ||
| modeling | ||
| connecting | ||
| working | ||
| inheritance | ||
|
|
||
| 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>`_ |
This file contains hidden or 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_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 hidden or 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,88 @@ | ||
| .. _ref_quickstart_modeling: | ||
|
|
||
| ================= | ||
| Modeling the data | ||
| ================= | ||
|
|
||
| .. edb:split-section:: | ||
|
|
||
| The flashcards application has a simple data model, but it's interesting enough to get a taste of many of the features of the Gel schema language. You have a ``Card`` class that extends the ``CardBase`` class describing a single flashcard, which for now contains two required string properties: ``front`` and ``back``. Each ``Card`` belongs to a ``Deck``, and there is an explicit ordering to the cards in a given deck. | ||
|
|
||
| Looking at the mock data, you can see this structure in the JSON. | ||
|
|
||
| .. code-block:: typescript | ||
|
|
||
| from pydantic import BaseModel | ||
|
|
||
| class CardBase(BaseModel): | ||
| order: int | ||
| 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. | ||
|
|
||
| .. 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; | ||
| + }; | ||
| + }; | ||
| }; | ||
|
|
||
| .. 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: | ||
|
|
||
| * **Create a migration**: a file with a list of low-level instructions. | ||
|
|
||
| .. 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. | ||
|
|
||
| * **Apply the migration**: basically, tell Gel "I want you to use these instructions and get my types ready for action." | ||
|
|
||
| .. code-block:: sh | ||
|
|
||
| $ gel migration create | ||
| Created ./dbschema/migrations/00001-m125ajr.edgeql, id: m125ajrbqp7ov36s7aniefxc376ofxdlketzspy4yddd3hrh4lxmla | ||
| $ 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. | ||
|
|
||
| .. code-block:: sh | ||
|
|
||
| $ gel ui |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.