@nextplus/js-sdk
v0.0.12
Published
A TypeScript SDK for interacting with the NextPlus API, automatically generated from OpenAPI specifications.
Maintainers
Readme
@nextplus/js-sdk
A TypeScript SDK for interacting with the NextPlus API, automatically generated from OpenAPI specifications.
Installation
npm install @nextplus/js-sdkUsage
Authentication Methods
The SDK supports two authentication methods:
- Token-based authentication - Use an existing access token (recommended for React/browser apps)
- Credential-based authentication - Provide email/username + password (SDK handles login automatically)
Token-Based Authentication (Recommended for React/Browser Apps)
Perfect for apps where authentication is handled separately (e.g., OAuth, Firebase Auth, custom auth):
import { configure } from '@nextplus/js-sdk';
// With explicit baseURL
const sdk = configure({
baseURL: 'https://your-nextplus-instance.com',
accessToken: 'your-existing-token',
returnType: 'data', // 'data' | 'raw'
});
// Auto-detect baseURL in browser (uses window.location.origin + '/api')
const sdk = configure({
accessToken: userToken, // from your auth provider
returnType: 'data',
});
// Use the SDK
const tables = await sdk.TableService.find({});Credential-Based Authentication
The SDK automatically handles login and token refresh:
import { configure } from '@nextplus/js-sdk';
// With email
const sdk = configure({
baseURL: 'https://your-nextplus-instance.com',
email: '[email protected]',
password: 'your-password',
returnType: 'data',
});
// With username
const sdk = configure({
baseURL: 'https://your-nextplus-instance.com',
username: 'your-username',
password: 'your-password',
returnType: 'data',
});Return Types
'data': Returns only the response data (default behavior)'raw': Returns the full response object with{ data, request, response }
Important: The returnType setting only applies when using service methods through the SDK instance (e.g., sdk.TableService.find()). When using sdk.client directly with individual service imports, responses are always in raw format ({ data, request, response }).
Error Handling
By default, non-OK responses return { error: ... } instead of throwing:
const response = await TableService.find({ limit: 10 });
if ('error' in response) {
console.error('API Error:', response.error);
} else {
console.log('Success:', response.data);
}To make errors throw automatically, use throwOnError: true:
configure({
accessToken: token,
throwOnError: true, // Errors will throw
});
try {
const response = await TableService.find({ limit: 10 });
// Only success responses reach here
} catch (error) {
// All API errors throw here
console.error('API Error:', error);
}| Configuration | Error Behavior |
| ------------------------------- | --------------------------- |
| returnType: 'data' (Full SDK) | ✅ Throws errors |
| returnType: 'raw' (Full SDK) | ❌ Returns { error: ... } |
| throwOnError: true | ✅ Throws errors |
| Default (tree-shakeable) | ❌ Returns { error: ... } |
Usage Patterns
All patterns use the same configure() function!
Pattern 1: Full SDK (Convenient, with return type control)
import { configure } from '@nextplus/js-sdk';
const sdk = configure({
accessToken: userToken,
returnType: 'data', // Returns just the data
});
// Use service methods through SDK - returns transformed data
const tables = await sdk.TableService.find({ limit: 10 });
// tables = [{ ... }, { ... }] - just the arrayPattern 2a: Automatic Global Client (Cleanest - Recommended)
import { configure, TableService, UserModelService } from '@nextplus/js-sdk';
// Configure once - automatically becomes global default!
configure({
accessToken: userToken,
throwOnError: true, // Make errors throw (optional)
});
// Use anywhere in your app - no client parameter needed!
try {
const response = await TableService.find({ limit: 10 });
const user = await UserModelService.findById({ path: { id: '123' } });
// Returns raw response format
console.log(response.data); // The actual data
} catch (error) {
// With throwOnError: true, errors are thrown
console.error('API Error:', error);
}Benefits:
- ✅ Automatic -
configure()sets global client automatically - ✅ No client parameter needed anywhere
- ✅ Tree-shakeable - only import services you use
- ✅ Optional error throwing with
throwOnError: true - ✅ Clean code everywhere in your app
Pattern 2b: Bound Services (Per-component)
import { configure, TableService, UserModelService } from '@nextplus/js-sdk';
// Configure once
const sdk = configure({ accessToken: userToken });
// Bind services - no need to pass client every time!
const Tables = sdk.service(TableService);
const Users = sdk.service(UserModelService);
// Use bound services
const response = await Tables.find({ limit: 10 });
const user = await Users.findById({ path: { id: '123' } });Pattern 2c: Manual Client (Explicit control)
const sdk = configure({ accessToken: userToken });
// Pass client explicitly each time
const response = await TableService.find({
client: sdk.client,
limit: 10,
});Pattern 3: Manual Client Setup (Maximum control)
import { TableService } from '@nextplus/js-sdk/services';
import { createClient } from '@nextplus/js-sdk/sdk/client';
const client = createClient({
baseUrl: window.location.origin + '/api',
});
// Add auth interceptor manually
client.interceptors.request.use((req) => {
req.headers.set('Authorization', myToken);
return req;
});
const response = await TableService.find({ client });Comparison:
| Feature | Pattern 1: Full SDK | Pattern 2: Tree-shakeable | Pattern 3: Manual |
| ------------------- | ------------------- | ------------------------- | ----------------- |
| Function | configure() | configure() | createClient() |
| Service access | sdk.ServiceName.* | Direct ServiceName.* | Direct |
| Bundle size | Larger | Smaller | Smallest |
| Return type control | ✅ 'data'/'raw' | ❌ Always raw | ❌ Always raw |
| Auto-login | ✅ (credentials) | ✅ (credentials) | ❌ |
| Complexity | Simple | Simple | More control |
| Best for | Full apps | Modern bundlers | Advanced use |
Automatic Global Client
configure() automatically sets the global default client - no manual setup needed!
import { configure, TableService, UserModelService } from '@nextplus/js-sdk';
// Configure once - automatically becomes global default!
configure({ accessToken: token });
// Use anywhere in your app - no client needed!
const tables = await TableService.find({ limit: 10 });
const users = await UserModelService.find({ limit: 10 });
// No client parameter needed! ✨Use Cases:
// React app initialization
function App() {
useEffect(() => {
// configure() automatically sets global client
configure({ accessToken: getAuthToken() });
return () => clearDefaultClient(); // Cleanup on unmount
}, []);
return <Routes />;
}
// Any component can now use services
function TableList() {
const [tables, setTables] = useState([]);
useEffect(() => {
// No client parameter needed!
TableService.find({ limit: 100 }).then(res => setTables(res.data));
}, []);
return <div>{/* render tables */}</div>;
}Manual Control (Optional):
// Set global client manually
setDefaultClient(client);
// Get current global client
const client = getDefaultClient();
// Clear global client (useful for logout)
clearDefaultClient();Client Override:
You can still pass a client parameter to override the global default:
configure({ accessToken: token1 }); // Sets global client
// Uses global client
await TableService.find({ limit: 10 });
// Uses specific client (overrides global)
await TableService.find({ client: customClient, limit: 10 });Interceptors (Request/Response Middleware)
Add custom logic to all requests and responses:
import { configure } from '@nextplus/js-sdk';
const sdk = configure({ accessToken: token });
// Request interceptor - runs before every request
sdk.client.interceptors.request.use((request) => {
console.log('Request:', request.url);
// Add custom headers
request.headers.set('X-Custom-Header', 'value');
// Modify request
return request;
});
// Response interceptor - runs after every successful response
sdk.client.interceptors.response.use((response) => {
console.log('Response:', response.status);
// Log or transform responses
return response;
});
// Error interceptor - runs on any error
sdk.client.interceptors.error.use((error) => {
console.error('API Error:', error);
// Custom error handling, logging, etc.
throw error; // Re-throw or handle
});Common Use Cases:
// Add request ID to all requests
sdk.client.interceptors.request.use((request) => {
request.headers.set('X-Request-ID', crypto.randomUUID());
return request;
});
// Add retry logic
sdk.client.interceptors.error.use(async (error) => {
if (error.response?.status === 429) {
// Rate limited - wait and retry
await new Promise((resolve) => setTimeout(resolve, 1000));
// Retry logic here
}
throw error;
});
// Log all API calls
sdk.client.interceptors.request.use((request) => {
console.log(`[API] ${request.method} ${request.url}`);
return request;
});Works with all patterns:
// Pattern 1: Full SDK
const sdk = configure({ accessToken: token });
sdk.client.interceptors.request.use(/* ... */);
await sdk.TableService.find({});
// Pattern 2: Tree-shakeable
const Tables = sdk.service(TableService);
await Tables.find({}); // Interceptors still apply!
// Pattern 3: Manual
await TableService.find({ client: sdk.client }); // Interceptors apply!API Usage Examples
// Find with filters
const tables = await sdk.TableService.find({
where: { name: 'my-table' },
limit: 10,
order: 'createdAt DESC',
});
// Find with filter object
const tables = await sdk.TableService.find({
filter: {
where: { name: 'my-table' },
limit: 10,
},
});
// Create a record
const newTable = await sdk.TableService.create({
name: 'New Table',
description: 'Table description',
});
// Update a record
const updated = await sdk.TableService.prototypePatchAttributes({
path: { id: 'table-id' },
name: 'Updated Name',
});
// With raw response type
const sdk = configure({
accessToken: 'token',
returnType: 'raw',
});
const response = await sdk.TableService.find({});
console.log(response.data); // Response data
console.log(response.request); // Request object
console.log(response.response); // Full HTTP responseDevelopment
Prerequisites
- Node.js (v18 or higher)
- npm
Setup
# Install dependencies
npm install
# Generate SDK from OpenAPI spec and build
npm run generateAvailable Scripts
npm run generate: Clean, generate from OpenAPI spec, and buildnpm run build: Build TypeScript to JavaScript with type declarationsnpm run clean: Remove thedistfoldernpm run openapi-ts: Generate SDK from OpenAPI specificationnpm run lint: Run ESLint
Development Workflow
- Make changes to the source code in
src/ - Generate and build:
npm run generate - Test changes:
npm run test(run smoke tests) - Publish:
npm publish(see Publishing section)
Code Generation
The SDK is automatically generated from OpenAPI specifications using @hey-api/openapi-ts. The generated code includes:
- Type-safe API clients
- Request/response type definitions
- Authentication handling
- Error handling
Generated files are located in src/sdk/ and should not be manually edited.
Testing
Test Commands
# Run full test suite (builds first, then runs tests)
npm test
# Run tests directly from TypeScript (faster for development)
npm run test:dev
# Run tests in watch mode (re-runs on file changes)
npm run test:watchTest Suite
Make sure your NextPlus instance is running on localhost:3000 before running tests.
The test suite (src/smoke.ts) uses Node's built-in test runner and validates:
- Token-based authentication
- Email and username authentication
- Different return types (
datavsraw) - Tree-shakeable service imports
- API calls to the TableService
- Configuration validation and error handling
Manual Testing
// Test with token authentication
import { configure } from '@nextplus/js-sdk';
const sdk = configure({
baseURL: 'http://localhost:3000',
accessToken: 'your-test-token',
});
const result = await sdk.TableService.find({});
console.log(result);
// Test with credentials
const sdk2 = configure({
baseURL: 'http://localhost:3000',
email: '[email protected]',
password: 'password',
});
const result2 = await sdk2.TableService.find({});
console.log(result2);
// Test tree-shakeable imports
import { configure, TableService } from '@nextplus/js-sdk';
const sdk = configure({
accessToken: 'your-test-token',
});
const tables = await TableService.find({ client: sdk.client });
console.log(tables);Publishing
Version Management
Update the version in package.json and publish:
# Bump version (patch, minor, or major)
npm version patch # 0.0.3 -> 0.0.4
npm version minor # 0.0.3 -> 0.1.0
npm version major # 0.0.3 -> 1.0.0
# Publish to npm
npm publishThe prepublishOnly script automatically cleans and builds before publishing.
Publishing Steps
- Ensure you're logged in:
npm whoami - Build:
npm run build(optional, done automatically) - Publish:
npm publish
Project Structure
├── src/
│ ├── index.ts # Main SDK entry point
│ ├── smoke.ts # Smoke test file
│ └── sdk/ # Generated SDK code (do not edit)
│ ├── client/ # API client implementations
│ ├── core/ # Core utilities and types
│ └── *.gen.ts # Generated files
├── dist/ # Built JavaScript and declarations
├── package.json
├── tsconfig.json
└── README.mdConfiguration
OpenAPI Generation
Configure OpenAPI generation in openapi-ts.config.ts:
export default {
input: 'path/to/openapi.json',
output: './src/sdk',
// ... other options
};TypeScript
The project uses modern TypeScript with:
- ES2020 target
- ESNext modules
- Strict type checking
- Declaration file generation
License
MIT
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
npm run test - Build:
npm run build - Submit a pull request
Support
For issues and questions, please create an issue in the repository or contact the NextPlus team.
