Generates a TypeScript client for Servant APIs.
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.
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
});
}
};