# FluidKit > Web development for the Pythonist FluidKit bridges Python and SvelteKit into a unified fullstack framework. Decorate async Python functions — FluidKit registers them as FastAPI endpoints and generates colocated `.remote.ts` files that SvelteKit imports as native remote functions with full type safety. - Website: https://fluidkit.github.io - GitHub: https://github.com/AswanthManoj/Fluidkit - PyPI: https://pypi.org/project/fluidkit/ - SvelteKit remote functions: https://svelte.dev/docs/kit/remote-functions - Full LLM reference: https://fluidkit.github.io/llm-full.txt ## Install pip install fluidkit No system Node.js required — FluidKit bundles it via nodejs-wheel. ## How it works 1. You decorate an async Python function with @query, @command, @form, or @prerender 2. FluidKit registers it as a FastAPI endpoint (parameter types, validation, return types extracted automatically) 3. FluidKit generates a colocated `.remote.ts` file — a SvelteKit remote function wrapper with full TypeScript types 4. You import from `$lib/yourfile.remote` in Svelte and use it as a native SvelteKit remote function Generated `.remote.ts` files update automatically on save in dev mode via HMR. They are real TypeScript you can inspect. ## Decorators ### @query — Read data Cached on client, refreshable on demand. Errors trigger nearest when using await. ```python from fluidkit import query @query async def get_posts() -> list[Post]: return await db.get_all_posts() ``` Svelte usage: ```svelte {#each await get_posts() as post}

{post.title}

{/each} ``` Query also exposes .loading, .error, .current properties as an alternative to await. Refreshing: get_posts().refresh() — refetches from server. Caching: get_posts() === get_posts() — cached while on page, no reference needed. #### @query.batch — Solve N+1 Batches concurrent calls into a single request. Function receives list of all arguments, must return a callable (arg, index) -> result. ```python @query.batch async def get_post_likes(post_ids: list[int]): likes = await db.get_likes_bulk(post_ids) lookup = {row.post_id: row.likes for row in likes} return lambda post_id, idx: lookup.get(post_id, 0) ``` Svelte side usage is identical to regular @query — each call uses a single argument. ### @form — Write data via forms Works without JavaScript (progressive enhancement). Supports file uploads, nested Pydantic models, and redirects. Errors render nearest +error.svelte. ```python from fluidkit import form, Redirect, FileUpload @form async def create_post(title: str, content: str) -> None: slug = title.lower().replace(" ", "-") await db.insert(slug, title, content) raise Redirect(303, f"/blog/{slug}") @form async def upload_file(label: str, attachment: FileUpload) -> dict: contents = await attachment.read() return {"success": True, "filename": attachment.filename} ``` Svelte usage: ```svelte
``` For file uploads, add enctype="multipart/form-data" to the form. FileUpload extends FastAPI's UploadFile — read(), filename, content_type are available. Supported parameter types: str, int, float, bool, FileUpload, list[FileUpload], list[str], list[int], Optional[...], Pydantic BaseModel (nested objects via dot notation). Nested types example: ```python from typing import Optional from pydantic import BaseModel from fluidkit import form, FileUpload class Info(BaseModel): height: int likesDogs: Optional[bool] = None @form async def create_profile(name: str, age: int, tags: list[str], info: Info, photo: FileUpload) -> None: await db.insert_profile(name, age, tags, info) ``` ```svelte
``` Nested fields use dot notation for objects (info.height) and bracket notation for arrays (tags[0]). SvelteKit coerces values based on input name prefix: n: for numbers, b: for booleans. Files work alongside nested types — FluidKit sends structured data as JSON and files as separate multipart fields. Prefix sensitive params with underscore (e.g. _password) to prevent round-tripping on validation failure. Validation: add_post.fields.title.issues() returns validation errors. add_post.validate() triggers validation. Returns: add_post.result?.success — ephemeral, vanishes on resubmit/navigation/reload. Enhance (custom submit behavior): ```svelte
{ await submit(); form.reset(); // not automatic with enhance })}> ``` Redirect status codes: 303 (See Other, most common), 307 (Temporary), 308 (Permanent). ### @command — Write data imperatively Called from event handlers, not tied to a form. Requires JavaScript. Errors are caught by your own try/catch. ```python from fluidkit import command @command async def like_post(post_id: int) -> bool: return await db.increment_likes(post_id) ``` Svelte usage: ```svelte