1+ """Low-level async client for direct Bubble Data API access.
2+
3+ Use RawClient when you need direct control over API calls without ORM overhead.
4+ For most use cases, prefer BubbleModel which provides a higher-level interface.
5+ """
6+
17import asyncio
28import http
39import json
1622# in addition to 'sort_field' and 'descending', it is possible to have
1723# multiple additional sort fields
1824class AdditionalSortField (typing .TypedDict ):
25+ """Secondary sort field for multi-field sorting in find queries."""
26+
1927 sort_field : str
2028 descending : bool
2129
2230
2331class RawClient :
24- """
25- Raw Client layer focuses on bubble.io API endpoints.
32+ """Raw Client layer focuses on bubble.io API endpoints.
2633
2734 https://manual.bubble.io/core-resources/api/the-bubble-api/the-data-api/data-api-requests
2835 https://www.postman.com/bubbleapi/bubble/request/jigyk5v/
@@ -31,9 +38,10 @@ class RawClient:
3138 _transport : Transport
3239
3340 def __init__ (self ) -> None :
34- pass
41+ """Initialize the client (must be used as async context manager)."""
3542
3643 async def __aenter__ (self ) -> typing .Self :
44+ """Enter async context and initialize the HTTP transport."""
3745 self ._transport = Transport ()
3846 await self ._transport .__aenter__ ()
3947 return self
@@ -44,27 +52,34 @@ async def __aexit__(
4452 exc_val : BaseException | None ,
4553 exc_tb : types .TracebackType | None ,
4654 ) -> None :
55+ """Exit async context and close the HTTP transport."""
4756 await self ._transport .__aexit__ (exc_type , exc_val , exc_tb )
4857
4958 async def retrieve (self , typename : str , uid : str ) -> httpx .Response :
59+ """Fetch a single thing by its unique ID."""
5060 return await self ._transport .get (f"/{ typename } /{ uid } " )
5161
5262 async def create (self , typename : str , data : typing .Any ) -> httpx .Response :
63+ """Create a new thing with the given data."""
5364 return await self ._transport .post (url = f"/{ typename } " , json = data )
5465
5566 async def bulk_create (self , typename : str , data : list [typing .Any ]) -> httpx .Response :
67+ """Create multiple things in a single request using newline-delimited JSON."""
5668 return await self ._transport .post_text (
5769 url = f"/{ typename } /bulk" ,
5870 content = "\n " .join (json .dumps (item ) for item in data ),
5971 )
6072
6173 async def delete (self , typename : str , uid : str ) -> httpx .Response :
74+ """Delete a thing by its unique ID."""
6275 return await self ._transport .delete (f"/{ typename } /{ uid } " )
6376
6477 async def update (self , typename : str , uid : str , data : typing .Any ) -> httpx .Response :
78+ """Partially update a thing with PATCH, only modifying specified fields."""
6579 return await self ._transport .patch (f"/{ typename } /{ uid } " , json = data )
6680
6781 async def replace (self , typename : str , uid : str , data : typing .Any ) -> httpx .Response :
82+ """Fully replace a thing's data with PUT, clearing unspecified fields."""
6883 return await self ._transport .put (f"/{ typename } /{ uid } " , json = data )
6984
7085 # https://manual.bubble.io/core-resources/api/the-bubble-api/the-data-api/data-api-requests#get-a-list-of-things
@@ -80,6 +95,7 @@ async def find(
8095 exclude_remaining : bool | None = None ,
8196 additional_sort_fields : list [AdditionalSortField ] | None = None ,
8297 ) -> httpx .Response :
98+ """Search for things matching constraints with pagination and sorting."""
8399 params : dict [str , str ] = {}
84100
85101 if constraints is not None :
0 commit comments