---
source_hash: "075f7f3d"
title: "Chapter 3: Data Flow"
weight: 510
---

# Chapter 3: Data Flow

## Overview

Understanding how data flows in SmartMaker is essential. This chapter traces the complete path of data, from user click to final rendering.

## The 3 Types of Data

| Type | Storage | Persistence | Hook |
| --- | --- | --- | --- |
| Local State | Component memory | No | useState, useStates |
| Global State | Redux store | Optional (localStorage) | useGlobalStates |
| Server Data | API -> Dolibarr | Database | useApi |

## API Request Flow

```
┌──────────────────────────────────────────────────────────────┐
│                        FRONTEND                               │
├──────────────────────────────────────────────────────────────┤
│  1. User clicks "Load"                                     │
│           ↓                                                   │
│  2. Component calls useApi()                               │
│           ↓                                                   │
│  3. api.private.get('items')                                 │
│           ↓                                                   │
│  4. ky automatically adds:                                  │
│     - Authorization: Bearer <accessToken>                    │
│     - X-DEVICEID: <deviceId>                                 │
└──────────────────────────────────────────────────────────────┘
                            ↓ HTTPS
┌──────────────────────────────────────────────────────────────┐
│                        BACKEND                                │
├──────────────────────────────────────────────────────────────┤
│  5. pwa/api.php receives the request                            │
│           ↓                                                   │
│  6. SmartAuth validates JWT                                  │
│           ↓                                                   │
│  7. Router calls ItemController::index()                   │
│           ↓                                                   │
│  8. Controller loads Dolibarr objects                        │
│           ↓                                                   │
│  9. Mapper converts to DTO                                  │
│           ↓                                                   │
│ 10. Returns JSON                                            │
└──────────────────────────────────────────────────────────────┘
                            ↓ HTTPS
┌──────────────────────────────────────────────────────────────┐
│                        FRONTEND                               │
├──────────────────────────────────────────────────────────────┤
│ 11. ky parses JSON response                                 │
│           ↓                                                   │
│ 12. Component updates state                              │
│           ↓                                                   │
│ 13. React re-renders with new data                         │
└──────────────────────────────────────────────────────────────┘
```

## Concrete Example: Product List

### 1. The React Component

```javascript
// components/pages/private/ProductsPage/index.jsx
import { useEffect } from 'react';
import { Page, Block, List, ListItem, Spinner } from '@cap-rel/smartcommon';
import { useApi, useStates } from '@cap-rel/smartcommon';

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

    const st = useStates({
        initialStates: {
            products: [],
            loading: true,
            error: null
        }
    });

    // Load on mount
    useEffect(() => {
        loadProducts();
    }, []);

    const loadProducts = async () => {
        st.set('loading', true);
        st.set('error', null);

        try {
            // API request with automatic JWT
            const data = await api.private.get('products').json();
            st.set('products', data.products);
        } catch (err) {
            st.set('error', err.message);
        } finally {
            st.set('loading', false);
        }
    };

    // Conditional display
    if (st.get('loading')) {
        return <Page><Spinner /></Page>;
    }

    if (st.get('error')) {
        return (
            <Page>
                <Block>Error: {st.get('error')}</Block>
            </Page>
        );
    }

    return (
        <Page title="Products">
            <List>
                {st.get('products').map(product => (
                    <ListItem
                        key={product.id}
                        title={product.label}
                        subtitle={`${product.price} €`}
                    />
                ))}
            </List>
        </Page>
    );
};
```

### 2. The API Router (PHP)

```php
<?php
// pwa/api.php
require_once '../smartmaker-api-prepend.php';

use SmartAuth\Api\AuthController;
use SmartAuth\Api\RouteController as Route;
use MonModule\Api\ProductController;

// Authentication routes
Route::get('login', AuthController::class, 'index');
Route::post('login', AuthController::class, 'login');
Route::get('refresh', AuthController::class, 'refresh');
Route::post('logout', AuthController::class, 'logout', true);

// Product routes (protected)
Route::get('products', ProductController::class, 'index', true);
Route::get('products/{id}', ProductController::class, 'show', true);
Route::post('products', ProductController::class, 'create', true);
Route::put('products/{id}', ProductController::class, 'update', true);
Route::delete('products/{id}', ProductController::class, 'delete', true);

// Fallback
json_reply('Access denied', 403);
```

### 3. The Controller (PHP)

```php
<?php
// smartmaker-api/Controllers/ProductController.php
namespace MonModule\Api;

use MonModule\Api\Mappers\dmProduct;

class ProductController
{
    public function index($payload = null)
    {
        global $db, $user;

        // Load Dolibarr products
        require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';

        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "product";
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
        $sql .= " ORDER BY label ASC";

        $result = $db->query($sql);
        $products = [];

        if ($result) {
            $mapper = new dmProduct();

            while ($obj = $db->fetch_object($result)) {
                $product = new \Product($db);
                $product->fetch($obj->rowid);

                // Map to React DTO
                $products[] = $mapper->exportMappedData($product);
            }
        }

        return [['products' => $products], 200];
    }

    public function show($payload = null)
    {
        global $db;

        $id = $payload['id'];

        require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
        $product = new \Product($db);

        if ($product->fetch($id) <= 0) {
            return [['error' => 'Product not found'], 404];
        }

        $mapper = new dmProduct();
        return [$mapper->exportMappedData($product), 200];
    }
}
```

### 4. The Mapper (PHP)

```php
<?php
// smartmaker-api/Mappers/dmProduct.php
namespace MonModule\Api\Mappers;

class dmProduct
{
    public function exportMappedData($product)
    {
        return [
            'id' => (int) $product->id,
            'ref' => $product->ref,
            'label' => $product->label,
            'description' => $product->description,
            'price' => (float) $product->price,
            'price_ttc' => (float) $product->price_ttc,
            'stock' => (float) $product->stock_reel,
            'status' => (int) $product->status,
            'created_at' => $product->datec,
            'updated_at' => $product->tms
        ];
    }

    public function importMappedData($data)
    {
        // DTO -> Dolibarr conversion for creation/modification
        return [
            'ref' => $data['ref'] ?? '',
            'label' => $data['label'] ?? '',
            'description' => $data['description'] ?? '',
            'price' => $data['price'] ?? 0
        ];
    }
}
```

## JWT Authentication

### Login Flow

```
1. User enters email/password
           ↓
2. api.login({ login, password })
           ↓
3. SmartAuth checks credentials
           ↓
4. Returns { accessToken, refreshToken, user }
           ↓
5. SmartCommon stores in session (localStorage)
           ↓
6. All future requests have the token
```

### Login Example

```javascript
// components/pages/public/LoginPage/index.jsx
import { Page, Block, Form, Input, Button } from '@cap-rel/smartcommon';
import { useApi, useNavigation } from '@cap-rel/smartcommon';

export const LoginPage = () => {
    const api = useApi();
    const nav = useNavigation();

    const handleSubmit = async (values) => {
        try {
            // api.login handles everything automatically
            await api.login({
                login: values.email,
                password: values.password
            });

            // Redirect to home
            nav.navigate('/');
        } catch (err) {
            alert('Incorrect credentials');
        }
    };

    return (
        <Page title="Login">
            <Block>
                <Form onSubmit={handleSubmit}>
                    <Input name="email" label="Email" type="email" required />
                    <Input name="password" label="Password" type="password" required />
                    <Button type="submit">Login</Button>
                </Form>
            </Block>
        </Page>
    );
};
```

### Automatic Refresh

The access token expires after 15 minutes. SmartCommon automatically handles:

1. Detects 401 error (expired token)
2. Calls `/refresh` with the refreshToken
3. Gets a new accessToken
4. Retries the original request

Everything is transparent to the developer.

## Global State vs Local State

### When to Use useStates (local)

- Data specific to a component
- Form state
- Loading state
- Temporary data

```javascript
const st = useStates({
    initialStates: {
        loading: false,
        formData: { name: '', email: '' }
    }
});
```

### When to Use useGlobalStates

- Data shared between components
- User session
- Preferences
- Main business data

```javascript
const gst = useGlobalStates();

const session = gst.get('session');
const cart = gst.get('cart');

// Write
gst.local.set('session', userData);
gst.set('cart', { items: [], total: 0 });
```

## Data Creation

### Creation Flow

```
1. Form filled
           ↓
2. api.private.post('products', { json: data })
           ↓
3. Controller::create() validates and creates
           ↓
4. Returns the created product
           ↓
5. Update local/global state
```

### Example

```javascript
const handleCreate = async (formData) => {
    try {
        const newProduct = await api.private
            .post('products', { json: formData })
            .json();

        // Add to local list
        st.set('products', [...st.get('products'), newProduct]);

        // Or update global state
        // const gst = useGlobalStates();
        // gst.set('products', [...gst.get('products'), newProduct]);

        nav.navigate('/products');
    } catch (err) {
        st.set('error', err.message);
    }
};
```

## Full Flow Summary

| Step | Layer | Action |
| --- | --- | --- |
| 1 | React | User interacts |
| 2 | Hook | useApi() prepares the request |
| 3 | ky | Adds JWT + sends |
| 4 | PHP | api.php routes the request |
| 5 | SmartAuth | Validates token |
| 6 | Controller | Executes business logic |
| 7 | Dolibarr | Database access |
| 8 | Mapper | Converts to DTO |
| 9 | PHP | Returns JSON |
| 10 | ky | Parses the response |
| 11 | Hook | Updates state |
| 12 | React | Re-renders the interface |

## Key Points to Remember

1. **useApi** automatically handles JWT authentication
2. **Controller** loads Dolibarr objects
3. **Mapper** converts Dolibarr -> React DTO
4. **useStates** for local state
5. **useGlobalStates** for shared state
6. Refresh token is handled automatically

[Previous Chapter](/training/module5-architecture-smartmaker/configuration) | [Back to Module](/training/module5-architecture-smartmaker) | [Next Module: SmartCommon Components ->](/training/module6-smartcommon-composants)
