Generated client

Generated API client

As well as using the APIs directly, you can generated a typescript client for your frontend projects to easily work with your APIs with full end to end type safety

💡

Requires version 0.351.0 or higher of the CLI

Generating the client

To generate the client, use the client command in the CLI from within a Keel project

 keel client

This will generate a single file with zero dependancies in your current directory.

To output the client directly into your frontend project you can either pass an output path

keel client --output ../myFrontend/src

or by running the CLI from your frontend project and referencing the Keel directory

keel client -d ../myKeelApp

This will generate a client for the first API in your project. If you have multiple APIs you can specify which API to generate for with the -a apiName flag

This client uses fetch so will only work in environments where fetch is available

Using the client

First, import APIClient from the generated file and create a new instance with the deployed url of your project (or the localhost (opens in a new tab) address if running locally)

N.B. This baseUrl url should include the API name path but not the protocol (e.g. json/rpc/gql)

import { APIClient } from "../keelClient";
 
const client = new APIClient({
  baseUrl: "https://myproject.keelapps.xyz/web/",
});

Navigating the Generated client

The generated client is segmented into three parts, api, client, and ctx.

api

This includes all the actions that you have specified in your Keel schema. For example, if you have an action that lists all users, here is how you can use the generated client to execute this action on the client-side:

const client = new APIClient({
  baseUrl: "https://myproject.keelapps.xyz/web/",
});
 
// use generated client to execute the action on the client-side
const allUsers = await client.api.queries.listUsers()
 
// log response from API to console
console.log(response.data?.results)

You can also use .api.mutations if you have actions that mutate data. For example, if you have an action that deletes a user, you can use it like so:

const client = new APIClient({
  baseUrl: "https://myproject.keelapps.xyz/web/",
});
 
// use generated client to execute the action on the client-side
const response = await keel.api.mutations.createUser({ name: name, email: email});
 
// log response from API to console
console.log(response.data)

client

These are the core client actions that serve as configuration options for the client. They remain consistent across every Keel schema. They include:

  • setHeaders()
  • setHeader()
  • setBaseUrl()
  • setToken()
  • clearToken()

ctx

This provides access to user authentication state. It includes the following options:

  • isAuthenticated: boolean
  • token: string

Usage:

const client = new APIClient({
  baseUrl: "https://myproject.keelapps.xyz/web/",
});
 
// returns a boolean that represents if a user is authenticated or not
client.ctx.isAuthenticated
 
// returns the bearer token if available
client.ctx.token

Authenticating

If your API requires authentication, use the authenticate action as normal and then store the returned token to be used for subsequent requests

const auth = await client.api.mutations.authenticate({
  emailPassword: {
    email: "me@keel.xyz",
    password: "topsecret",
  },
  createIfNotExists: true,
});
 
if (auth.error) {
  console.log("Auth failed", auth.error.type);
  return;
}
 
console.log("Authenticated");

On logout remember to clear the token

client.client.clearToken();

Error handling

The response of an action is a promise that resolves to an object that either contains the data or an error object

type Data<T> = {
  data: T;
  error?: never;
};
 
type Error = {
  data?: never;
  error: ErrorObj ;
};
 
type ErrorObj = {
  type: "forbidden" | "not_found" | "internal_server_error" | "unknown" | "unauthorized" | "bad_request";
  message: string;
  requestId?: string; // can be used to access traces in the console
};

You can then check the presence of the error object to handle any errors

const myAction = await client.myAction({
  input: "foo"
});
 
if (myAction.error) {
  console.log("Failed", myAction.error.type);
  return;
}
 
const { data } = myAction
 
// Do things with the data