What is ephaptic?
ephaptic (adj.)
electrical conduction of a nerve impulse across an ephapse without the mediation of a neurotransmitter.
Nah, just kidding. It's an RPC framework.
ephaptic — Call your backend straight from your frontend. No JSON. Low latency. Invisible middleware.
Getting Started
-
Ephaptic is designed to be invisible. Write a function on the server, call it on the client. No extra boilerplate.
-
Plus, it's horizontally scalable with Redis (optional), and features extremely low latency thanks to msgpack.
-
Oh, and the client can also listen to events broadcasted by the server. No, like literally. You just need to add an
eventListener. Did I mention? Events can be sent to specific targets, specific users - not just anyone online. -
Saved the best for last: it's type-safe. Don't believe me? Try it out for yourself. Simply type hint return values and parameters on the backend, and watch those very Python types transform into interfaces and types on the TypeScript frontend. Plus, you can use Pydantic - which means, for those of you who are FastAPI users, this is going to be great.
What are you waiting for? Let's go.
To see why you might want to use Ephaptic instead of traditional REST, see the article on it.
Python
from fastapi import FastAPI # or `from quart import Quart`
from ephaptic import Ephaptic
app = FastAPI() # or `app = Quart(__name__)`
ephaptic = Ephaptic.from_app(app) # Finds which framework you're using, and creates an ephaptic server.
If you're trying to expose functions statelessly, e.g. in a different file, feel free to instead import and use the expose function from the library instead of the instance. Please note that if you do this, you must define all exposed functions before creating the ephaptic instance - easily done by simply placing your import line above the ephaptic constructor. The same thing can be done with the global identity_loader decorator.
Yep, it's really that simple.
But what if your code throws an error? No sweat — it surfaces on the frontend as a typed error you can `catch`. Raise a `ServiceError` for structured, typed errors (carrying a `code`, `message`, and `data`), or let ephaptic turn unexpected exceptions into a safe, generic error (details stay on the server unless you opt into debug mode). See the [error handling docs](https://ephaptic.github.io/ephaptic/tutorial/errors).
And, want to say something to the frontend?
class Notification(BaseModel):
message: str
priority: Literal["high", "low", "default"]
await ephaptic.to(user1, user2).emit(Notification(message="Hello, world!", priority="high"))
from ephaptic import identity_loader
@identity_loader
async def load_identity(auth): # You can use synchronous functions here too.
jwt = auth.get("token")
if not jwt: return None # unauthorized
... # app logic to retrieve user ID
return user_id
ephaptic.active_user() within any exposed function, and it will give you the current active user ID / whatever else your identity loading function returns. (This is also how ephaptic.to works.)
JavaScript/TypeScript — Browser (Svelte, React, Angular, Vite, etc.)
To use with a framework / Vite:
Then: Or, you can use it with a custom URL: You can even send auth objects to the server for identity loading. And you can load types, too (pass `--watch` to auto-reload on changes):import { connect } from "@ephaptic/client";
import { type EphapticService } from './schema';
const client = connect(...) as EphapticService;
Or, to use in your browser:
See more in the docs.
Conformance
Ephaptic's wire protocol is specified in specs/SERVER.md and specs/CLIENT.md. Each package ships a CONFORMANCE.md recording exactly which requirements it satisfies, which it does not, and why.
License
© ephaptic 2025 · I read this and now I don't know if Ephaptic is worth it anymore. Who cares.