You can also use Kilpi on the client side with the @kilpi/client package which allows you to fetch authorization decisions from the server. 
Setup
 Install the @kilpi/client package 
npm install @kilpi/clientyarn add @kilpi/clientpnpm add @kilpi/clientbun add @kilpi/clientSetup environment variables
 Create public environment variables (e.g. with PUBLIC_, NEXT_PUBLIC_ or similar prefix to ensure they are included in the client bundle). 
PUBLIC_KILPI_URL=http://localhost:3000/api/kilpiPUBLIC_KILPI_SECRET=generate-secretInstall EndpointPlugin
 The Kilpi client requires an endpoint for fetching authorization decisions. Use the EndpointPlugin from @kilpi/core to create an authorization endpoint. It exposes the Kilpi.$createPostEndpoint() function, which constructs a web-standard request-response function to use as your endpoint. 
import { createKilpi, EndpointPlugin } from "@kilpi/core";
export const Kilpi = createKilpi({  ...,  plugins: [   EndpointPlugin({     secret: process.env.PUBLIC_KILPI_SECRET,     // Optional     getContext(request) { ... },     async onBeforeHandleRequest(request) { ... },     async onBeforeProcessItem(requestItem) { ... },   }) ]})Read more about the EndpointPlugin for more information on the setup options.
Setup the authorization endpoint
Expose the endpoint using your framework of choice.
export const POST = Kilpi.$createPostEndpoint();const endpoint = Kilpi.$createPostEndpoint();
app.post('/api/kilpi', async (c) => await endpoint(c.req.raw)); Create your KilpiClient instance 
 Create your KilpiClient instance with createKilpiClient. This object is used to fetch authorization decisions from the server. 
 Optionally, pass infer for improved type safety, and add any plugins via plugins. 
import type { Kilpi } from "./kilpi.ts";
export const KilpiClient = createKilpiClient({  // Infer subject and policies from server instance  infer: {} as typeof Kilpi,  // Connect to the endpoint  connect: {    endpointUrl: process.env.PUBLIC_KILPI_URL,    secret: process.env.PUBLIC_KILPI_SECRET,  },});Fetch authorization decisions on the client
 Now you can use KilpiClient to fetch authorization decisions from the server with a similar API as on the server. 
 KilpiClient automatically provides request caching, batching and deduplication for performance. 
// Similar decision object as on the serverconst decision = await KilpiClient.posts.edit(myPost).authorize();Use with your frontend framework
 You can use @kilpi/client with your frontend framework of choice or use one of the provided plugins that provide e.g. components and other useful bindings for KilpiClient. 
React (client)
 Get utility hooks and components for client-side React with the @kilpi/react-client
package. 
import { KilpiClient } from "./kilpi-client";const { AuthorizeClient } = KilpiClient.$createReactClientComponents();
export function Component({ post }: { post: Post }) {  // Use via hooks  const { granted } = KilpiClient.posts.create().useAuthorize();
  // Or components  return (    <AuthorizeClient policy={KilpiClient.posts.delete(post)}>      <DeletePostForm />    </AuthorizeClient>  );} The authorize() API 
 The authorize() API is similar to the server-side authorize() API and it returns the same decision object. You can provide it additional options as shown below. 
const decision = await KilpiClient.some.policy().authorize({  abortSignal, // Provide a custom abort signal for the fetch call});Caching
 On top of deduplicating and batching queries for performance, KilpiClient also caches all requests for you automatically. 
All authorization decisions are cached indefinitely, until the cache is invalidated.
Invalidating the cache
 Use the KilpiClient.$cache API to invalidate the cache. 
// Fully clears the cacheKilpiClient.$cache.invalidate();
// Fine-grained invalidation: Only invalidate this policyKilpiClient.my.policy().invalidate();Hooks
To react to cache invalidations, you can use the hooks API as follows.
const unregister = KilpiClient.$hooks.onCacheInvalidate(({ key }) => {  // When any cache invalidation happens  console.log("Some cache invalidation!");
  // When the full cache is invalidated  if (key === null) console.log("Full cache invalidation!");  // When a specific cache key is invalidated  else console.log(`Cache invalidation for key:`, key);});Cache keys
 Kilpi uses Tanstack Query inspired keys of type unknown[], which must be serializable. Internally, their stable string representation is used. This means each key must be stringifiable. To compare whether two cache keys are equal, use: 
const keyA = ["some-key", { value: 1 }];const keyB = ["some-key", { value: 1 }];
// False due to referential equivalencekeyA === keyB;
// True, due to stable stringificationKilpiClientCache.areCacheKeysEqual(keyA, keyB);Getting a policy’s cache key
 Each policy exposes its cache key as KilpiClient.my.policy().$cacheKey. While not recommended, you may also use the cache key directly as 
// Not recommended but possibleKilpiClient.$cache.invalidateKey(KilpiClient.my.policy().$cacheKey); Or you may use it in onCacheInvalidate hooks. 
const policy = KilpiClient.my.policy();const unregister = KilpiClient.$hooks.onCacheInvalidate(({ key }) => {  if (    key === null ||    KilpiClientCache.areCacheKeysEqual(key, policy.$cacheKey)  ) {    console.log("My policy was invalidated");  }});