RPC

Turn any function into an API! This lets you call a function in Val Town without manually handling a Request or Response object.

This val can be used as a replacement for the Run API, which implicitly did something similar.

Create an API from your function:

This is how you can expose a function as an API:

  1. Create an HTTP handler val
  2. Import this val
  3. Call rpc with your function as an argument

Here's an example from @std/rpc_example:

import { rpc } from "https://esm.town/v/std/rpc?v=5";

export const rpc_example = rpc(async (a: number, b: number) => {
  return a + b;
});

Send a request to your val

You can invoke that function from your client or your local machine with an HTTP request. You can either use a GET or a POST request, passing arguments in the query parameters or the body respectively. Make sure your val's privacy is set to Unlisted or Public.

GET request

const res = await fetch("https://std-rpc_example.web.val.run/?args=[2,3]");
console.log(await res.json()); // 5

POST request

const res = await fetch("https://std-rpc_example.web.val.run/", {
  method: "POST",
  body: JSON.stringify({ args: [2, 3] })
});
console.log(await res.json()); // 5
Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { parseRPCArgs } from "https://esm.town/v/std/parseRPCArgs";
import { z } from "npm:zod@3.22.4";
export function rpc(fn: Function) {
return async (req: Request): Promise<Response> => {
try {
const args = await parseRPCArgs(req);
const result = await fn.apply(null, args);
return Response.json(result);
} catch (e) {
if (e instanceof Response) {
return e;
}
console.error(e);
return Response.json("Error", { status: 500 });
}
};
}
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.
Comments
zarutian avatar

Here is a version that supports private vals. (To stave of confusion, the http handler can not be private but it can import a private val)

stevekrouse avatar

Very cool! FYI – you can submit a pull request by clicking the three dots menu of your forked val – we'd love to get those improvement into this val!

Sorry about all the changes to our API. Hopefully we've finally got things into a secure and stable state.

stevekrouse avatar

Thanks for submitting the PR!

On second thought, I think this private thing makes more sense as a layer on top, ie it could be a middleware in the same way that @pomdtr/basicAuth is!

Basically you could extract this part into a function and then wrap it around anything you want to have bearer auth!

     if (req.headers["Authorization"] != `Bearer ${VALTOWN_TOKEN}`) {
        return Response.json("Unauthorized", {
          status: 401,
          statusText: "unauthorized, val is private"
        })
      }

I guess we could even call that function bearerAuth

zarutian avatar

the idea was to make that rpc() adapter in this way to make the run api deprication less of an breaking change.

stevekrouse avatar

Wait a sec – if you make the HTTP val private it will do this authorization part for you automatically! So you should be able to just wrap your function with std/rpc as it's currently written and then make the HTTP val private and it would work. We should probably just update the README on this val to explain that