Docs / Core / API Client
ven.js

API Client

Use venjs.api.connect() to call external REST APIs. In a real project the API call lives in logic/ and the page component in components/ imports it.

venjs.api.connect(url, options)

Performs a fetch and returns the parsed JSON body. Throws an Error when the response is not OK.

const data = await venjs.api.connect(url, options);
OptionDefaultDescription
method"GET"HTTP method.
headers{ "Content-Type": "application/json" }Merged with your headers.
bodyIf an object, it is JSON.stringify-ed automatically.
any fetch optionSpread into the fetch config.
const res = await venjs.api.connect("https://api.example.com/users/1", {
  method: "GET",
  mode: "cors"
});
Errors: On a non-2xx status the promise rejects with Error("HTTP 404: ..."). Always try/catch.

venjs.api.query(key, fetcher, options)

Caches a fetcher's result keyed by key. Within the TTL the cached value is returned without re-running fetcher.

const data = await venjs.api.query(key, fetcher, { ttl, force });
OptionDefaultDescription
ttl30000Time-to-live in milliseconds.
forcefalseWhen true, bypass the cache and re-fetch.
const users = await venjs.api.query("all-users", () =>
  venjs.api.connect("/api/users"), { ttl: 60000 });

venjs.api.invalidate(key)

Deletes a single cache entry so the next query re-fetches.

venjs.api.invalidate("all-users");

venjs.api.clearCache()

Empties the entire cache.

Real-life example: fetch users from an external API

A Home page with a button that fetches users from an external REST API. The API logic is isolated in logic/users.js and the UI lives in components/home.js.

File structure

venjs/
  components/
    home.js        # HomePage — button + user list UI
  logic/
    users.js       # fetchUsers() — venjs.api.connect() call

logic/users.js

This file contains only the API logic. It uses venjs.api.query to cache the result for 60 seconds so repeated button clicks don't spam the server.

export const fetchUsers = async () => {
  const result = await venjs.api.query("external-users", () =>
    venjs.api.connect("https://jsonplaceholder.typicode.com/users")
  );
  return result;
};

components/home.js

The component imports fetchUsers, shows a button, and renders the list with loading/error states.

import { fetchUsers } from "../logic/users.js";

const HomePage = () => {
  const users = venjs.signal([]);
  const loading = venjs.signal(false);
  const error = venjs.signal("");

  const load = async () => {
    loading.value = true;
    error.value = "";
    try {
      const data = await fetchUsers();
      users.value = Array.isArray(data) ? data : [];
    } catch (err) {
      error.value = "Failed to load users: " + err.message;
    } finally {
      loading.value = false;
    }
  };

  return venjs.div({ class: "page" }, [
    venjs.h1({ class: "page-title" }, "Users"),
    venjs.button({
      onclick: load,
      disabled: loading.value
    }, loading.value ? "Loading..." : "Fetch users"),
    error.value ? venjs.p({ class: "status error" }, error.value) : null,
    users.value.length ? venjs.ul({ class: "user-list" }, users.value.map(u =>
      venjs.li({ class: "user-item" }, [
        venjs.strong({}, u.name),
        venjs.span({}, " — " + u.email)
      ])
    )) : venjs.p({ class: "status" }, "No users loaded yet.")
  ]);
};

window.HomePage = HomePage;

Real-life example: POST data to an external API

Sending form data to an external endpoint. The component collects inputs, the logic file posts them.

logic/contact.js

export const submitContact = async (name, email, message) => {
  const result = await venjs.api.connect("https://api.example.com/contact", {
    method: "POST",
    body: { name, email, message }
  });
  return result;
};

components/contact.js

import { submitContact } from "../logic/contact.js";

const ContactPage = () => {
  const name = venjs.signal("");
  const email = venjs.signal("");
  const message = venjs.signal("");
  const status = venjs.signal("");
  const loading = venjs.signal(false);

  const submit = async () => {
    loading.value = true;
    status.value = "";
    try {
      await submitContact(name.value, email.value, message.value);
      status.value = "Message sent successfully!";
      name.value = "";
      email.value = "";
      message.value = "";
    } catch (err) {
      status.value = "Error: " + err.message;
    } finally {
      loading.value = false;
    }
  };

  return venjs.div({ class: "page" }, [
    venjs.h1({ class: "page-title" }, "Contact Us"),
    venjs.input({
      label: "Name",
      value: name.value,
      oninput: (e) => (name.value = e.target.value)
    }),
    venjs.input({
      label: "Email",
      type: "email",
      value: email.value,
      oninput: (e) => (email.value = e.target.value)
    }),
    venjs.input({
      label: "Message",
      value: message.value,
      oninput: (e) => (message.value = e.target.value)
    }),
    venjs.button({
      onclick: submit,
      disabled: loading.value
    }, loading.value ? "Sending..." : "Send message"),
    status.value ? venjs.p({ class: "status" }, status.value) : null
  ]);
};

window.ContactPage = ContactPage;