Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ Remote functions, callable in the browser.

- Telefunc is an RPC framework you can attach to your server.
- It enables full-stack teams to adopt development patterns that
- Improve application security and performance,
- And allow for rapid and flexible iteration.
- Telefunc makes it easy to add RPC to your server, but it's up to you to take advantage of the benefits RPC has to offer.
- Improve performance.
- Improve application security.
- Telefunc is easy to add RPC to your server — you can use Telefunc in parallel to your server. It's up to you how much RPC you want to use (you can progressively migrate back and forth between your server API endpoints and Telefunc). It's up to you to take advantage of the benefits RPC has to offer.
Copy link
Member

@ambergristle ambergristle Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed that there needs to be more emphasis on how easy it is to migrate to/from telefunc, but

  • the final bullet was meant to emphasize that RPC is pattern moreso than a technology. i.e., you can use telefunc in a way that bypasses the potential performance benefits
    • i think this is an important point to make clearly early-on; let me know if you have thoughts on how to better express that
  • the migration DX is a point i would emphasize later in the docs. i agree that it makes sense to mention early-on, but i'd think of it more as a detail than a vertical of its own, e.g.,
    - Allow for rapid and flexible iteration.
+     - Easy to progressively add (or remove) from project
    - Improve application security.
  - It's up to you to take advantage of the benefits RPC has to offer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wanted to expand on this a bit

i'd think of it more as a detail than a vertical of its own

as i understand it, RPC's main value prop is the design pattern, which allows for

  • better dx (i.e., faster iteration)
    • related code is close together
    • development is full-stack
    • no schemas to build or maintain
    • easy to add, remove, migrate progressively
  • minimal/tailored request payloads
    • better security
    • better performance

in my mind, these are two main values, from which all the rest follow. in the introduction, i think it's helpful to

  • focus on the high-level rather than the detail
  • group related values/ideas to help readers build the right mental model

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 It all makes sense.

i think this is an important point to make clearly early-on; let me know if you have thoughts on how to better express that

Nice idea 😍 How about:

Telefunc is a tool, as well as a new mental model we call
*Event-based telefunctions* for maximum development speed.

Btw. I'd suggest we avoid the term "RPC" outside the /RPC and /RPC-vs-GraphQL-REST pages. Most users don't know what RPC means. If the user doesn't want to learn then it's better we don't confront him with the term "RPC".


> A [remote procedure call](https://en.wikipedia.org/wiki/Remote_procedure_call) (RPC) is when a \[...] computer programmer writes essentially the same code whether the subroutine is local to the executing program, or remote.

- RPC is a pattern for calling a remote function as if it were colocated with the client. No adapters or injection needed.
1. Create named Telefunctions to call the database or do other backend work.
- RPC is a pattern for calling a remote function as if it were define in the client. Zero boilerplate.
1. Create telefunctions (on the server) to call the database or do other backend work.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the parenthetical feels a little redundant. the context of this list is "a pattern for calling a remote fn", so to me it's clear that telefunctions are remote.

if i'm wrong about the clarity, i'd suggest that's a more general issue, and that the outline needs to do a better job of explaining the control flow early-on.

  • parentheticals can add helpful context, but can also be distracting/interrupt reading. a strong foundation of knowledge/context, is often enough.

but it could also be helpful to add a simple flow diagram.

sequenceDiagram
    Client->>Server: Request
    Server-->>Telefunction: Telefunction args
    Telefunction--)Server: Telefunction return
	Server->>Client: Response
Loading

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good point. I can see that I'm often too conservative in my writing (repeating myself too much).

So far I think we don't need a diagram (for this). Although it could be nice to use diagrams for things that are hard to communicate succinctly. E.g. for the RPC vs REST/GraphQL page, a diagram could be very helpful. Or maybe https://telefunc.com/event-based could also benefit a diagram — not sure how the diagram would look like, but that could be very powerful. The mental model is quite important (a lot more important than the RPC vs REST/GraphQL page).

2. Import and call the function from your frontend, passing any required arguments (e.g., `await onNewTodo(text)`).
3. Telefunc creates a lightweight HTTP client to call your Telefunction, which returns only what the caller needs.

::: info
- The term RPC is often used loosely to denote RPC-like approaches, like creating JSON endpoints.
- RPC-like describes an API that is schemaless — in contrast to RESTful and GraphQL APIs that always have a schema.
- RPC-like describes an API that is schemaless — in contrast to RESTful and GraphQL APIs which have a schema.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i'm actually not sure which is correct.

"that" vs "which" is a confusing english rule. as i understand it, it basically depends on whether the clause you're adding is required or supplemental

"The cat, which is hungry, ran away from home"

  • knowing that the cat is hungry is bonus info -> which

"The scratch that the cat gave me stings"

  • knowing which scratch you're talking about is required -> that

the guide i use is basically "which" if i use a comma, otherwise "that", but it's imperfect.

this case seems like a gray area to me, but i would lean towards "that", because "have a schema" is clarifying info, rather than definitional. either works though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Didn't know that.

My main motivation was to remove always. Because many users say they're building a REST API, but what they're actually doing is creating JSON enpoints without following any kinds of schemas. Many(/most?) users don't know that REST => RESTful level-3 => schema. Instead of teaching users about REST, we can accept the confusion and mostly talk about generic APIs (GraphQL/REST) in the comparison page. Event though users might not follow the level-3 schema thing, I think most are still building an API that is somewhat generic (few endpoints to cover a max number of use cases). So I'd frame the discussion around "event-based" VS "generic".


:::

Expand Down Expand Up @@ -75,8 +76,8 @@ async function TodoList() {
- You can think of RPC as a type-safe `fetch` call to a registered server-side function.

## Securing resources
- Always keep in mind that your **telefuncs are public**.
- A simple HTTP request could be used to extract, modify, or destroy user secrets or business-critical data from an unprotected function.
- Always keep in mind that your **telefunctions are public**.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be good to more prominently introduce/define the term "telefunction" earlier in the docs

- A simple HTTP request could be used to extract, modify, or destroy user secrets or business-critical data from an unprotected telefunction.

- Add guards to Telefunctions to prevent illegitimate access.
- Use `throw Abort()` to exit early if the client is unauthenticated or doesn't have permission to access the requested resource
Expand All @@ -85,7 +86,7 @@ async function TodoList() {
- Use `shield()` to validate incoming requests.
- Don't forget to sanitize SQL arguments
- Allows argument types to be inferred.
- Telefunc generates a `shield` automatically from TypeScript types, if available.
- Telefunc generates a `shield` automatically from TypeScript types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what was your motivation here? if i understand the docs correctly, a shield is only generated if you use typescript/add types to your telefunctions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, shield() is only auto generated for TypeScript telefunctions. I was just making it slightly more succinct (kinda what you said above: "with a strong foundation we can be more succinct"). We probably agree here.


```js
// When using TypeScript, Telefunc automatically generates
Expand Down Expand Up @@ -144,14 +145,14 @@ export async function MarkAllComplete() {
}
```

- To the consumer, these are all just functions, that do some async work.
- To the client, these are all just functions, that do some async work.
- RPC abstracts away the request management.
- This is nice, but the real value comes from the (framework-agnostic) colocation of browser and server code it enables.
- You can write a Telefunction for each view and interaction, just as if you were loading data from local storage, but with all the power of a remote server.

### Design patterns
- RPC is a full-stack pattern, and projects that use a monorepo will get the most out of the acceleration it has to offer.
- It allows you to colocate (and codevelop) highly-tailored server functions with client code.
- It allows you to colocate (and co-develop) highly-tailored server functions with client code.

```jsx
// /Todos.jsx
Expand All @@ -164,8 +165,9 @@ import {
} from './Todos.telefunc.js'

async function TodoList() {
// No need for an adapter, or "server only" flag
const todoItems = await onLoad()
// @ambergristle We'll implement useData() after we merged Dani's streaming PR
// No need for an adapter, or "server only" flag, works for both Client and Server components
const todoItems = await useData(() => onLoad())

async function onClick(form) {
const text = form.input.value
Expand Down Expand Up @@ -196,9 +198,12 @@ async function TodoList() {
}
```

- The naming convention enforces coupling, which is a cornerstone of keeping RPC calls lithe (i.e., avoiding function scope creep).
- The naming convention enforces coupling, which is a cornerstone of keeping RPC calls slim (i.e., avoiding function scope creep).
- Opting out of the naming convention is perfectly fine, though we recommend having a clear reason for doing so.
> @ambergristle I didn't know the word "lithe" — I guess many non-native English speakers won't know it either
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call-out!

i studied literature for years, and still talk like an english professor sometimes. 😆 usually i catch stuff like that in review, but i appreciate the reminder to keep a global audience in mind!


- Accordingly, RPC versioning is typically equivalent to project versioning. You will need a custom solution if you choose to use RPC with a monorepo.
> @ambergristle The best pattern here is to always deploy the frontend and JS server at the same time. Versioning telefunctions are a pain (even more than versionin RESTFul/GraphQL APIs because telefunctions change a *lot* more change generic RESTful/GraphQL APIs) — it's a core Telefunc philosophy: you develop frontend hand-in-hand with telefunctions. The backend can be decoupled using a three party setup: static files (frontend) + JS server for SSR + telefunctions (frontend) + backend (Java/Ruby/...) — this setup is commonly called "BFF" (backend-for-frontend).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the added detail! this was one of the weaker sections, and some of the detail about monorepos/project structure got lost in the shuffle.

- Telefunc is most effective when used with a monorepo architecture, allowing close coupling and dynamic type-sharing between frontend and backend.
  - Telefunctions can change as fast as your UI, so deploying frontend and backend at the same time radically simplifies versioning.

it's a core Telefunc philosophy: you develop frontend hand-in-hand with telefunctions

i tried to emphasize this in the outline, but i think there's opportunity for improvement. it's a really important point to make, but also quite difficult to communicate. i'm still trying to figure out what helped me shift my mental model/understand it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best is to have a single repository that holds both the frontend and telefunctions. No monorepo.


## Schema-less vs schema-full
- Comparing RPC to GraphQL or REST is a bit like comparing apples to oranges. They each make trade-offs to achieve different goals.
Expand All @@ -215,12 +220,12 @@ async function TodoList() {

### Should I use it?
> Premature optimization is the root of all evil.
> - *Turing Award winner, Donal Knuth*
> - *Donal Knuth, Turing Award winner*

- In most cases you can start with RPC, and switch to REST or GraphQL as needed.
- RPC enables you to stay lean, iterating faster and pivoting more flexibly.
- Many apps will never need a public (or schema-full) API.
- RPC functions are just functions; migrating to REST or GraphQL is fairly straightforward.
- RPC functions are just functions; migrating to REST or GraphQL is straightforward.
- There's a simple litmus test for whether RPC is the right solution:
- If your goal is to enable third party developers to access your data, then you need a generic API and you'll have to use REST or GraphQL
- If your goal is to seamlessly add data and interactivity to a front-end, then RPC can improve DX and enable security and performance optimizations.
- If your goal is to seamlessly add data and interactivity to a front-end, then RPC improves DX and enable security and performance optimizations.