Skip to content

arataappdev/pocketbase_plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

PocketBase Plugin Introduction Goal

Enable seamless CRUD operations and authentication with PocketBase collections through an elegant, minimal Python interface that requires zero configuration per collection. Context

This plugin provides a unified interface to interact with any PocketBase collection without pre-defining schemas or writing collection-specific code. It handles authentication automatically, supports batch operations, and adapts to any collection structure dynamically. Overview

The plugin exposes a single PocketBase class that can interact with any collection through method calls or dynamic attribute access. All operations are async and require proper environment variables to be set. Steps Step 1: Environment Setup

Why: The plugin reads PocketBase credentials from environment variables for security and portability.

Code Snippet:

Python

In .env file at project root

POCKETBASE_URL=https://your-instance.pocketbase.io POCKETBASE_EMAIL=admin@example.com POCKETBASE_PASSWORD=your-admin-password

Dos and Don'ts:

✅ DO use POCKETBASE_URL without trailing slash
✅ DO use admin credentials for POCKETBASE_EMAIL/PASSWORD
❌ DON'T commit .env to version control
❌ DON'T use user credentials for admin operations

Step 2: Basic Import and Initialization

Why: Two import patterns provide flexibility - singleton for quick use, class for explicit collection binding.

Code Snippet:

Python

Method 1: Using singleton (dynamic collections)

from pocketbase_plugin import pb

Method 2: Creating specific collection instance

from pocketbase_plugin import PocketBase users = PocketBase("users")

Dos and Don'ts:

✅ DO use singleton pb for multiple collections
✅ DO create instances for single collection operations
❌ DON'T mix patterns in the same function
❌ DON'T forget collections are accessed as attributes on singleton

Step 3: Listing/Searching Records

Why: Retrieve multiple records with optional filtering, sorting, and pagination.

Code Snippet:

Python

Basic list

records = await pb.users.list()

With filters (two methods)

records = await pb.users.list(filter="age>21 && verified=true") records = await pb.users.list(age=21, verified=True) # kwargs become filters

With sorting and pagination

records = await pb.posts.list( sort="-created,title", # DESC by created, then ASC by title page=2, perPage=50 )

Dos and Don'ts:

✅ DO use filter strings for complex conditions
✅ DO use kwargs for simple equality filters
✅ DO prefix sort fields with - for DESC
❌ DON'T mix filter string with filter kwargs
❌ DON'T forget pagination returns metadata in response

Step 4: Retrieving Single Records

Why: Fetch a specific record by ID with optional relation expansion.

Code Snippet:

Python

Basic get

user = await pb.users.get("record_id_xyz")

With expanded relations

post = await pb.posts.get( "post_id_123", expand="author,comments.user" )

With field selection

user = await pb.users.get( "user_id", fields="id,name,email" )

Dos and Don'ts:

✅ DO use expand for related data
✅ DO use fields to minimize payload
❌ DON'T assume record exists (handle exceptions)
❌ DON'T expand more than 6 levels deep

Step 5: Creating Records

Why: Insert new records into a collection with automatic schema adaptation.

Code Snippet:

Python

Simple creation

user = await pb.users.create( name="John Doe", email="john@example.com", age=30 )

With query parameters for field selection

post = await pb.posts.create( title="Hello World", content="Post content", author="user_id", _params={"fields": "id,title,created"} # _params for query parameters )

Returns created record with ID

print(user["id"]) # "newly_created_id"

Dos and Don'ts:

✅ DO pass fields as kwargs matching collection schema
✅ DO use _params for query parameters
✅ DO handle the returned record data
❌ DON'T include id unless you want specific ID
❌ DON'T assume field types (let PocketBase validate)

Step 6: Updating Records

Why: Modify existing records with partial updates (only specified fields change).

Code Snippet:

Python

Partial update

updated = await pb.users.update( "user_id_xyz", name="Jane Doe", age=31 )

Update with expansion

updated = await pb.posts.update( "post_id", title="Updated Title", _params={"expand": "author"} )

Dos and Don'ts:

✅ DO pass only fields you want to update
✅ DO use the returned updated record
❌ DON'T pass unchanged fields
❌ DON'T forget the record ID as first argument

Step 7: Deleting Records

Why: Remove records from a collection permanently.

Code Snippet:

Python

Single deletion

await pb.users.delete("user_id_xyz")

Check before delete pattern

try: user = await pb.users.get("user_id") if user["status"] == "inactive": await pb.users.delete(user["id"]) except Exception: pass # Record doesn't exist

Dos and Don'ts:

✅ DO verify record exists if needed
✅ DO handle deletion in try/except
❌ DON'T delete without considering relations
❌ DON'T expect return value (returns empty dict)

Step 8: Batch Operations

Why: Execute multiple operations in a single request for performance.

Code Snippet:

Python

Batch create

new_users = await pb.users.create_many([ {"name": "User 1", "email": "user1@example.com"}, {"name": "User 2", "email": "user2@example.com"}, {"name": "User 3", "email": "user3@example.com"} ])

Batch update

updates = { "user_id_1": {"status": "active"}, "user_id_2": {"status": "inactive"}, "user_id_3": {"name": "Updated Name"} } results = await pb.users.update_many(updates)

Batch delete

await pb.users.delete_many(["id1", "id2", "id3"])

Custom batch operations

results = await pb.batch([ {"method": "POST", "url": "/api/collections/users/records", "body": {...}}, {"method": "PATCH", "url": "/api/collections/posts/records/id", "body": {...}}, {"method": "DELETE", "url": "/api/collections/comments/records/id"} ])

Dos and Don'ts:

✅ DO use batch for multiple operations
✅ DO check individual operation results
✅ DO use create_many for bulk inserts
❌ DON'T exceed reasonable batch sizes (100-500 ops)
❌ DON'T mix unrelated operations in batch

Step 9: User Authentication

Why: Authenticate as a collection user (not admin) for user-specific operations.

Code Snippet:

Python

Authenticate as user

auth_data = await pb.users.auth( "user@example.com", # identity (email/username) "user_password" )

Access auth token and user data

token = auth_data["token"] user_record = auth_data["record"] user_id = user_record["id"]

Note: This doesn't affect admin operations

Admin auth is handled automatically via env vars

Dos and Don'ts:

✅ DO use for user-specific operations
✅ DO store token for subsequent user requests
❌ DON'T confuse with admin authentication
❌ DON'T use user auth for admin operations

Step 10: Working with Different Collections

Why: The same interface works for any collection regardless of schema.

Code Snippet:

Python

Different collections, same interface

await pb.products.create(name="Mouse", price=29.99, stock=100) await pb.orders.create(user="user_id", items=["item1", "item2"], total=59.98) await pb.categories.create(name="Electronics", slug="electronics")

Dynamic access to any collection

collection_name = "dynamic_collection" records = await getattr(pb, collection_name).list()

Or use explicit initialization

dynamic = PocketBase(collection_name) await dynamic.create(field1="value1", field2="value2")

Dos and Don'ts:

✅ DO use the same methods for all collections
✅ DO let PocketBase handle schema validation
✅ DO use getattr for dynamic collection names
❌ DON'T hardcode collection schemas in your code
❌ DON'T assume fields exist across collections

Step 11: Error Handling

Why: Properly handle network, authentication, and validation errors.

Code Snippet:

Python

import httpx

try: user = await pb.users.create(email="invalid-email") except httpx.HTTPStatusError as e: if e.response.status_code == 400: print("Validation error:", e.response.json()) elif e.response.status_code == 401: print("Authentication failed") elif e.response.status_code == 404: print("Collection or record not found") else: print(f"HTTP error {e.response.status_code}") except httpx.RequestError as e: print("Network error:", str(e))

Dos and Don'ts:

✅ DO wrap operations in try/except
✅ DO check status codes for specific errors
✅ DO log errors appropriately
❌ DON'T suppress all exceptions silently
❌ DON'T assume operations always succeed

Step 12: Advanced Filtering

Why: Build complex queries for precise data retrieval.

Code Snippet:

Python

Complex filter strings

results = await pb.products.list( filter="(price >= 10 && price <= 100) && (category = 'electronics' || featured = true)", sort="-featured,-created", expand="category,reviews" )

Date filtering

posts = await pb.posts.list( filter="created >= '2024-01-01 00:00:00' && status = 'published'" )

Pattern matching

users = await pb.users.list( filter="email ~ 'gmail.com' && name !~ 'test'" # contains gmail, not test )

Null checks

records = await pb.items.list( filter="deletedAt = null && parentId != null" )

Dos and Don'ts:

✅ DO use parentheses for complex logic
✅ DO use ~ for pattern matching
✅ DO use ISO format for dates
❌ DON'T forget to escape quotes in strings
❌ DON'T use invalid operators for field types

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages