npm package: @jwork-space/strapi-sdk
Description: SDK para Strapi con soporte para React Query y TypeScript
Author: Roman Ernesto Gonzalez Alas, Jonathan Fernando González Ramos
Latest version: 1.0.7

# @jwork-space/strapi-sdk

SDK para Strapi con soporte completo para **TypeScript** y **React Query**.  
Permite realizar operaciones CRUD, manejar paginación, relaciones y personalizar requests fácilmente.

---

## Instalación

```bash
# Usando pnpm
pnpm add @jwork-space/strapi-sdk

# Usando npm
npm install @jwork-space/strapi-sdk
```

## Importación

```bash
import {
  StrapiSDK,
  type PaginationResponse,
  type RequestOptions,
  type SerializeOptions,
  type StrapiConfig,
} from "@jwork-space/strapi-sdk";

```

## Definición de las interfaces
Para definir las interfaces de tus modelos, puedes usar los autocompletados de TypeScript de los populate y filters:

```bash
interface Section {
  id: number;
  name: string;
}

interface Category {
  id: number;
  name: string;
}

interface Stock {
  id: number;
  name: string;
  section?: Section | null;
}

interface Product {
  id: number;
  name: string;
  description?: string;
  price: number;
  category?: Category[] | null;
  stocks?: Stock | null;
}

```

## Configuración básica

Antes de usar el SDK, puedes configurar los parámetros de conexión y comportamiento:
```bash
const productStrapiApiConfig: StrapiConfig = {
  /* set only in case of no user proxy */
  baseURL: "http://localhost:1337",
  /* on case use proxy prefix */
  prefix:
    "strapi-api-v2" /* ouput: http://localhost:1337/strapi-api-v2/api/products */,
  /* in case of use uid on create model product */
  onCreate: {
    generateUID: false,
    keyUID: "strapiUID",
  },
  /* default jwr */
  tokenKey: "jwt-strapi",
  /* default on save/update/list-all { body: { data } } */
  wrapBodyInData: false,
  /* remember that is use on response list */
  transformResponse: false,
  /* page size por default when user method listAll, default 15 */
  pageSize: 40,
  defaultQueriesInvalidations: {
    /* default this model (products), buy you can add others: example ['users', 'stocks'] */
    destroy: ["stocks"],
    update: ["stocks"],
    save: ["stocks"],
  },
};

```

## Ejemplo básico de uso
```bash
export const productAPI = new StrapiSDK<Product>(
  "products",
  productStrapiApiConfig
);

/* example usave method */

const requestOptions: RequestOptions<Product> = {
  /* default sort by id:desc */
  sort: "createdAt:desc",
  fields: ["id", "name", "price"],
  populate: {
    category: true,
    stocks: {
      populate: {
        section: true,
      },
      filters: {
        section: {
          id: {
            $eq: 1,
          },
        },
      },
    },
  },
};

export const exampleUsage = async () => {
  const id = 1;

  /* retornar lista de productos paginados */
  const { data: _data, pagination: _pagination } = await productAPI.listAll(
    requestOptions
  );
  
  /* retornar todos de productos de la base de datos y sin paginación */
  const { data: _data, pagination: _pagination } = await productAPI.getAllData(
    requestOptions
  );

  const requestOptionsOnActions: RequestOptions<Product> = {
    populate: {
      category: true,
      stocks: {
        populate: {
          section: true,
        },
      },
    },
  };

  const serializeOptionsOnSaveAndUpdate: SerializeOptions = {
    onlyIdForRelations: true,
    preserveRelations: ["stocks"],
  };

  const _productSaved = await productAPI.save(
    {
      id: 0 /* use cero for create new */,
      name: "product-name",
      description: "product-description",
      price: 100,
      category: [{ id: 1, name: "category-name" }],
      stocks: { id: 1, name: "stock-name" },
    },
    requestOptionsOnActions,
    serializeOptionsOnSaveAndUpdate
  );

  const _productUpdated = await productAPI.update(
    id,
    {
      id,
      name: "product-name",
      description: "product-description",
      price: 100,
      category: [{ id: 1, name: "category-name" }],
      stocks: { id: 1, name: "stock-name" },
    },
    requestOptionsOnActions,
    serializeOptionsOnSaveAndUpdate
  );

  /* return boolean */
  const hasDeleted = await productAPI.destroy(id);
};

```

## Ejemplo extendido: Métodos personalizados

Puedes extender el SDK para agregar métodos custom o sobreescribir los existentes:

```bash
/* example extends SDK Methods */
class ProductAPI extends StrapiSDK<Product> {
  constructor() {
    super("products", productStrapiApiConfig);
  }

  /* override default method */
  listAll = async (options?: RequestOptions<Product>) => {
    /* raw response type return:
    data: Product[]
      pagination: {
        page: number
        pageSize: number
        total: number
        totalPages: number
        nextPage: number
        prevPage: number
      }
    */
    return await this.getCall<PaginationResponse<Product>>("products", options);
  };

  /* exmplay override method save */
  save = async (
    entity: Product,
    requestOptions?: RequestOptions<Product>,
    serializeOptions?: SerializeOptions
  ): Promise<Product> => {
    /* or set your own settings to create */
    if (!entity.id) {
      entity.name = `NEW-${entity.name}`;
    }
    // Llamamos al save original
    return await this.save(entity, requestOptions, serializeOptions);
  };

  /* exmplay create method save custom */
  saveCustom = async (
    entity: Product,
    requestOptions?: RequestOptions<Product>,
    serializeOptions?: SerializeOptions
  ): Promise<Product> => {
    /* or set your own settings to create */
    if (!entity.id) {
      entity.category = [{ id: 1, name: "category-name" }];
      entity.name = `NEW-${entity.name}`;
    }
    /* you can use methods hhtp */
    /* example */
    return await this.postCall<Product>("products", {
      /* auth default true, (if is false, no send token to backend) */
      auth: false,
      body: entity,
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
        /* on case no use default token */
        "Bearer token": "your-token",
      },
      responseType: "json", // default json || 'blob'
      query: {
        extraQuery: {
          "your-query-key": "your-query-value",
        },
      },
    });
    // Llamamos al save original
    return await this.save(entity, requestOptions, serializeOptions);
  };

  /* new method  */
  listAllCustom = async (options?: RequestOptions<Product>) => {
    /* raw response type return:
    data: Product[]
    meta: {
      pagination: {
        page: number
        pageSize: number
        total: number
        totalPages: number
        nextPage: number
        prevPage: number
      }
    }
    */

    /* the options can be the that  */
    return await this.getCall<PaginationResponse<Product>>("products", options);
  };
}

export const productAPIExtended = new ProductAPI();

/* example usage methods SDK Methods Custom */
await productAPIExtended.listAll();
await productAPIExtended.saveCustom({
  id: 0,
  name: "product-name",
  price: 100,
});
```