App Setup

First, let's make a new file, app.js, and move our App component over, making sure to export it.

import {html} from ''
export function App() {
return html`<h1>Hello Aft!</h1>`

And then import it in our index.html file:

<link rel=stylesheet href="./styles.css" />
<script type=module>
import {html, render} from ''
import {App} from './app.js'
render(html`<${App} />`, document.body);

Hit refresh on the client—you should still see your app rendering its greeting.

API client#

Now we'll add a small API client—some objects that will make it easy for us to talk to Aft.

Make a new file, aft.js, and add the following.

function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) {
return parts.pop().split(";").shift();
return ""
async function call(path, body) {
const result = await fetch(path, {
method: 'POST',
headers: {'X-CSRF': getCookie('csrf')},
body: JSON.stringify(body || {}),
const response = await result.json();
if (response.code) {
throw new Error(response.message);
const curryProxy = (inner) => {
return new Proxy({}, {
get(_, prop) {
return inner(prop)
export default {
api: curryProxy((interfaceName) => curryProxy((method) => (params) => {
return call("api/" + interfaceName + "." + method, params)
rpc: curryProxy((rpcName) => (args) => {
return call("rpc/" + rpcName, args)

The use of Proxy isn't really necessary, but it gives us a nice looking syntax for making API calls or RPCs to Aft. This short snippet is all you'll need in your app to use every bit of functionality Aft has to offer.

Okay, nice work! Up next, we'll make our login UI and sign in to our app.