API Requests

SmartCommon provides the useApi hook which simplifies API calls with automatic JWT authentication management, token refresh, and error handling.

ky Documentation (HTTP client used)

Configuration

1. Configure the Provider

The ApiProvider must be configured in your LibConfigProvider:

// src/appConfig.js

export const appConfig = {
  debug: true,
  api: {
    prefixUrl: import.meta.env.VITE_API_URL,
    timeout: 30000,
    debug: true
  }
};
// src/App.jsx

import { Provider } from '@cap-rel/smartcommon';
import { appConfig } from './appConfig';

export const App = () => {
  return (
    <Provider config={appConfig}>
      <Router />
    </Provider>
  );
};

Using useApi

Import the Hook

import { useApi } from '@cap-rel/smartcommon';

Returned Structure

The useApi hook returns an object with the following methods:

Method Description
user Connected user object (from gst)
login(body, options) User login, automatically stores tokens
logout(options) Logout, removes tokens
entities(options) Get available entities (before login)
device(body, options) Register/select a device
public ky instance for public requests
private ky instance for authenticated requests
get(url, options) Shortcut for authenticated GET request
post(url, options) Shortcut for authenticated POST request
put(url, options) Shortcut for authenticated PUT request
patch(url, options) Shortcut for authenticated PATCH request
del(url, options) Shortcut for authenticated DELETE request

Login Request

import { useApi } from '@cap-rel/smartcommon';
import { Input, Button } from '@cap-rel/smartcommon';

export const Login = () => {
  const api = useApi();

  const handleSubmit = async (e) => {
    e.preventDefault();

    const formData = new FormData(e.target);
    const credentials = Object.fromEntries(formData.entries());

    try {
      const user = await api.login({
        ...credentials,
        rememberMe: true
      });

      console.log('Logged in:', user);
    } catch (error) {
      console.error('Login error:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <Input name="login" label="Email" type="email" />
      <Input name="password" label="Password" type="password" />
      <Button type="submit">Login</Button>
    </form>
  );
};

Authenticated Requests

For requests requiring authentication, use api.private:

import { useApi } from '@cap-rel/smartcommon';
import { useState, useEffect } from 'react';
import { List, ListItem, Spinner } from '@cap-rel/smartcommon';

export const ItemsList = () => {
  const api = useApi();
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchItems = async () => {
      try {
        const data = await api.private.get('items').json();
        setItems(data);
      } catch (error) {
        console.error('Error:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchItems();
  }, []);

  if (loading) return <Spinner />;

  return (
    <List>
      {items.map(item => (
        <ListItem key={item.id}>{item.name}</ListItem>
      ))}
    </List>
  );
};

POST/PUT/DELETE Requests

const api = useApi();

// POST - Create
const createItem = async (data) => {
  return api.private.post('items', { json: data }).json();
};

// PUT - Update
const updateItem = async (id, data) => {
  return api.private.put(`items/${id}`, { json: data }).json();
};

// DELETE - Delete
const deleteItem = async (id) => {
  return api.del(`items/${id}`);
};

Shortcut Methods

In addition to api.private.get(...).json(), useApi exposes shortcut methods that automatically handle JSON deserialization and errors:

const api = useApi();

// GET
const items = await api.get('items');

// POST with JSON body
const created = await api.post('items', { json: { label: 'New' } });

// PUT
const updated = await api.put(`items/${id}`, { json: data });

// PATCH
const patched = await api.patch(`items/${id}`, { json: { status: 1 } });

// DELETE
await api.del(`items/${id}`);

Raw Option (Binary Data)

To retrieve binary data (images, files), pass raw: true:

// Download a file in binary
const response = await api.get(`file/${hash}/binary`, { raw: true });
const blob = await response.blob();

Silent Option (Suppress Errors)

By default, API errors trigger the onApiError callback (toast display). To suppress them:

// Do not display toast on error
const data = await api.get('optional-endpoint', { silent: true });

Global Error Handling

Configure onApiError in appConfig to centralize API error handling:

// src/appConfig.js
import toast from 'react-hot-toast';

export const appConfig = {
  api: {
    prefixUrl: import.meta.env.VITE_API_URL,
    onApiError: (message) => {
      toast.error(message);
    },
  },
};

When a request fails, useApi:

  • Extracts the error message from the JSON response body (error or message field)
  • Calls onApiError with this message (unless silent: true)
  • Also detects application errors (HTTP 200 with error field in body)

Access Connected User

The useApi hook directly exposes the user object:

const api = useApi();

// Direct access to user
const user = api.user;

if (user) {
  console.log(user.login, user.accessToken);
}

Public Requests

For requests that do not require authentication:

const api = useApi();

const fetchPublicData = async () => {
  return api.public.get('public/info').json();
};

Automatic Features

Token Management

  • Access token is automatically added to private request headers
  • Token is automatically refreshed before expiration
  • On 401 error, a refresh is automatically attempted

Circuit Breaker

  • Protection against cascading requests in case of server error
  • Temporary blocking of requests after multiple failures
  • Automatic detection of internet connection

Automatic Headers

Each request automatically includes:

  • Authorization: Bearer <token> (private requests)
  • X-DEVICEID: <uuid> (device identification)

Classic Method (Native fetch)

If you prefer to use fetch directly (not recommended):

Fetch API Documentation

const API_URL = import.meta.env.VITE_API_URL;

const request = {
  method: "POST",
  body: JSON.stringify(data),
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json",
    Authorization: `Bearer ${token}`
  }
};

fetch(`${API_URL}/items`, request)
  .then(response => response.json())
  .then(json => console.log(json))
  .catch(error => console.error(error));

Tip

It is recommended to use useApi rather than native fetch to benefit from automatic token management and circuit breaker.

See Also