0.2.1 • Published 8 months ago

@bx93tn/core-sdk v0.2.1

Weekly downloads
-
License
MIT
Repository
github
Last release
8 months ago

@bx93tn/core-sdk

SDK for the Entity Core API, providing tools for interacting with neuroscience data and models.

npm

Installation

pnpm add @bx93tn/core-sdk zod
# or
bun add @bx93tn/core-sdk zod
# or
npm install @bx93tn/core-sdk zod
# or
yarn add @bx93tn/core-sdk zod

Note:

  • zod are listed as peer dependencies.
  • lru-cache is meant to be used in node environment (future releases will allow different external cache services).
  • in the browser, the client use the cache api.

Design Philosophy

This SDK is built with the following principles in mind:

  1. Modular Architecture: The codebase is organized into distinct modules:
    • client: Handles the core API communication (ApiClient, RequestBuilder).
    • functions: Contains individual, standalone functions for each API endpoint.
    • sdk: Provides a structured, class-based SDK interface that groups related functions.
    • models: Defines TypeScript types and Zod schemas for API request/response validation.
    • filters: Defines TypeScript types for API query parameters.
  2. Type Safety: Leverages TypeScript and Zod for strong typing of function arguments, return values, and API schemas, ensuring data integrity and improving developer experience.
  3. Flexible Access Patterns: Offers two primary ways to interact with the API:
    • A full SDK object (EntityCoreSDK) with grouped endpoints.
    • Standalone functions that can be imported individually.
  4. Robust Error Handling: API call results are wrapped in a Result<T, E> type, making error handling explicit and preventing unexpected runtime errors. An unwrap() method is provided for convenience when direct access to the successful value is desired.

Core Components

ApiClient

The ApiClient (imported from @bx93tn/core-sdk/client) is the foundation for all API interactions. It manages:

  • The base URL of the API.
  • Default request configurations (e.g., headers for authentication).
  • Optional features like caching, request retries, and lifecycle hooks.

You need to instantiate ApiClient before using either the SDK or standalone functions.

import { ApiClient } from "@bx93tn/core-sdk/client";

const apiClient = new ApiClient("https://your-api-base-url.com", {
  fetchOptions: {
    headers: {
      Authorization: "Bearer YOUR_API_TOKEN",
      "Content-Type": "application/json",
    },
  },
  // Optional configuration:
  // cache: { enabled: true, ttl: 60000, maxSize: 100 },
  // retry: { attempts: 3, delay: 1000 },
  // debug: true,
});

EntityCoreSDK

The SDK (imported from @bx93tn/core-sdk/sdk) provides a convenient, object-oriented wrapper around the ApiClient and standalone functions.

import { EntityCoreSDK } from "@bx93tn/core-sdk/sdk";
import { ApiClient } from "@bx93tn/core-sdk/client";

const apiClient = new ApiClient(/* ... */);
const sdk = new EntityCoreSDK(apiClient);

const morphology = await sdk.reconstructionMorphology.get("some-uuid").unwrap();

Standalone Functions

For more granular control or potentially better tree-shaking, you can import and use functions directly (imported from @bx93tn/core-sdk/functions). Each function requires an ApiClient instance as its first argument.

import { listReconstructionMorphologies } from "@bx93tn/core-sdk/functions";
import { ApiClient } from "@bx93tn/core-sdk/client";

const apiClient = new ApiClient(/* ... */);

const result = await listReconstructionMorphologies(apiClient, {
  /* query parameters */
});

if (result.ok) {
  console.log("Morphologies:", result.value);
} else {
  console.error("API Error:", result.error);
}

Why Offer Both SDK and Functions?

Providing both interfaces offers flexibility:

  • SDK (EntityCoreSDK):
    • Pros: Good discoverability of endpoints, convenient for applications using many related API calls, provides a structured, object-oriented feel.
    • Cons: Might import more code than strictly necessary if only a few endpoints are used.
  • Standalone Functions:
    • Pros: Allows importing only the specific functions needed, potentially leading to smaller bundle sizes (better tree-shaking), aligns well with functional programming patterns.
    • Cons: Requires manually passing the ApiClient to each call, less discoverability compared to the SDK structure.

Choose the approach that best suits your project's needs and coding style.

Usage Examples

Setup

import { ApiClient } from "@bx93tn/core-sdk/client";
import { EntityCoreSDK } from "@bx93tn/core-sdk/sdk";
import {
  listReconstructionMorphologies,
  getReconstructionMorphology,
  createReconstructionMorphology,
} from "@bx93tn/core-sdk/functions";

// 1. Use client for other purposes
const apiClient = new ApiClient("http://localhost:8000", {
  fetchOptions: {
    headers: { Authorization: "Bearer TOKEN" },
  },
});

// 2. Instantiate the SDK with same parameters as the ApiClient
const sdk = new EntityCoreSDK("http://localhost:8000", {
  debug: true,
  cache: { server: true },
  retry: { attempts: 5, backoff: "exponential", shouldRetry: () => true },
  fetchOptions: {
    headers: { Authorization: "Bearer TOKEN" },
  },
});

Using the SDK

async function useSDK() {
  // List items (using flat access and unwrap)
  console.log("Fetching list via SDK...");
  const listData = await sdk.reconstructionMorphology
    .list({ page: 1, page_size: 20 })
    .unwrap();
  // Access unwrapped data directly
  // Get a specific item (using grouped access)
  const morphologyId = listData.items[0]?.id;
  if (!morphologyId) {
    console.log("No morphology ID found to fetch.");
    return;
  }
  const getItemData = await sdk.reconstructionMorphology
    .get(morphologyId)
    .unwrap();
  console.log("Single Item (SDK):", getItemData);

  // Create an item
  const newItemData = {
    name: "New Morphology SDK" /* other required fields */,
  };
  const createdItem = await sdk.reconstructionMorphology
    .create({ ...newItemData })
    .unwrap();
}

useSDK();

Using Standalone Functions

async function useFunctions() {
    const apiClient = new ApiClient("http://localhost:8000/api/", {/* client config */})
    // or you can use the EntityCoreSDK.apiClient
  // List items
  const listResult = await listReconstructionMorphologies(apiClient, { page: 1, page_size: 20 } });
    if (listResult.ok) {
        const morphologyId = listResult.value.items[0]?.id;
        if (!morphologyId) {
            console.log("No morphology ID found to fetch.");
            return;
        }
    }
    const getResult = await getReconstructionMorphology(apiClient, "uuid");
    if (getResult.ok) {
      console.log('Single Item (Functions):', getResult.value);
    } else {
      console.error('Function Error (Get):', getResult.error);
    }
   // Create an item
    const newItemData = { name: "New Morphology Function", /* other required fields */ };
    const createResult = await createReconstructionMorphology(apiClient, { ...newItemData });

    if (createResult.ok) {
        console.log('Created Item (Functions):', createResult.value);
    } else {
        console.error('Function Error (Create):', createResult.error);
    }
}

useFunctions();

Result Handling

All API operations (both SDK methods and standalone functions) return a Promise<Result<T, E>>.

  • Result.ok: A boolean indicating success (true) or failure (false).
  • Result.value: The successful data payload (if ok is true).
  • Result.error: The error object (if ok is false).

You can handle results manually:

const result = await sdk.reconstructionMorphology.list();
if (result.ok) {
  console.log(result.value);
} else {
  console.error("Error:", result.error);
}

Or use the convenient .unwrap() method provided on the Result object, which returns the value on success or throws the error on failure:

try {
  const data = await sdk.reconstructionMorphology.list().unwrap();
  console.log(data);
} catch (error) {
  console.error("Caught error:", error);
}

Development

  • Build: pnpm run build (uses tshy for dual CJS/ESM output)
  • Lint: pnpm run lint
  • Format: pnpm run format
  • Check All: pnpm run check
  • Check Package Exports: pnpm run check-exports
0.2.1

8 months ago

0.2.0

8 months ago

0.1.0

8 months ago