@hackage servant-aeson-generics-typescript0.0.0.2

Generates a TypeScript client for Servant APIs

Servant Aeson Generics TypeScript

This project leveratges aeson-generics-typescript to generate type safe API bindings in TypeScript for a given Servant API. Included here are tests that round trip by compling the TypeScript with tsc, running the client in nodejs, and checking that request response round trips with the Servant server.

data Foo = ...
  deriving stock (Generic)
  deriving anyclass (ToJSON, FromJSON, TypeScriptDefinition)

type API = "foo" :> Get '[JSON] Foo

Is all it takes to have a TypeScript Definition, and TypeScript client. Now you can obtain the TypeScript client as a string like so.

client :: String
client = gen @API

Support

TypeScript code generated for HTTP uses the fetch api, which works in Browsers and Nodejs. TypeScript code generated for WebSocket's only works in Browsers for the moment, as Nodejs doesn't have WebSocket support. Round trips are tested with Selenium using Chromium.

Example

You can see many examples in the tests. One provided here for documentation purposes:

data Foo = Foo { thang :: String, otherThang :: Int }
  deriving stock (Generic)
  deriving anyclass (ToJSON, FromJSON, TypeScriptDefinition)

type API
     = "foo" :> Capture "bar" Int :> Post '[JSON] Foo
  :<|> "qux" :> WebSocketConduit String Foo

client = tsClient @'[Foo] @API

will generate

// Defined in Servant.Client.TypeScriptSpec of main
interface Foo {
  // readonly tag: "Foo";
  readonly thang: string;
  readonly otherThang: number;
}
const API = {
  base: "",
  "/foo/:bar": (bar: number): Promise<Foo> => {
    const uri = `${API.base}/foo/${bar}`;
    return fetch(uri, {
      method: "POST"
    }).then(res => res.json());
  },
  "/qux": ():
    Promise<{ send : (input: string) => void
            , receive : (cb: (output: Foo) => void) => void
            , raw : WebSocket
    }> => {
      const pr = window.location.protocol === "http:" ? "ws:" : "wss:";
      const ws = new WebSocket(`${pr}//${window.location.host}${API.base}/foo/bar`);
      return Promise.resolve({
        send: (input: User) => ws.send(JSON.stringify(input)),
        receive: (cb: ((output: User) => void)) =>
          ws.onmessage = (message: MessageEvent<string>) => cb(JSON.parse(message.data)),
        raw: ws
      });
  }
};