@voidagency/api-gateway-sdk
v1.0.3
Published
Node.js SDK for the DDEV Update Manager API - A powerful toolkit for managing DDEV instances, executing commands, running SQL queries, and PHP scripts with convenient template literal syntax
Downloads
24
Maintainers
Readme
DDEV Update Manager API SDK
A Node.js SDK for interacting with the DDEV Update Manager API, built with the got HTTP client library.
Installation
npm install @voidagency/api-gateway-sdkQuick Start
const DdevUpdateManagerSDK = require('@voidagency/api-gateway-sdk');
// Initialize the SDK with your API key
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key-here',
baseUrl: 'http://localhost:3000' // optional, defaults to localhost:3000
});
// List all DDEV instances
const instances = await sdk.listInstances();
console.log(instances);Configuration
The SDK constructor accepts the following options:
apiKey(required): Your API key for authenticationbaseUrl(optional): Base URL of the API (default:http://localhost:3000)projectId(optional): Set a default project ID for this SDK instancerequestOptions(optional): Additional options to pass to thegotHTTP client
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key',
baseUrl: 'https://api.example.com',
projectId: 'my-drupal-project', // Set default project ID
requestOptions: {
timeout: { request: 60000 }, // 60 second timeout
retry: { limit: 5 } // 5 retry attempts
}
});Setting Project ID
You can set the project ID in three different ways:
Method 1: Constructor Option
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key',
projectId: 'my-drupal-project'
});Method 2: Manual Setting
const sdk = new DdevUpdateManagerSDK({ apiKey: 'your-api-key' });
sdk.setProjectId('my-drupal-project');Method 3: By URL
const sdk = new DdevUpdateManagerSDK({ apiKey: 'your-api-key' });
const projectId = await sdk.getInstanceByUrl('https://my-drupal-project.ddev.site');
// projectId is now set to 'my-drupal-project'Once a project ID is set, you can use methods without passing the projectId parameter:
// With projectId set on instance
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key',
projectId: 'my-drupal-project'
});
// No need to pass projectId to methods
await sdk.getDrupalStatus();
await sdk.clearDrupalCache();
await sdk.execCommand('drush status');API Methods
Core Methods
getHello()
Get a simple hello message from the API.
const message = await sdk.getHello();
console.log(message); // "Hello World!"listInstances()
List all DDEV instances.
const instances = await sdk.listInstances();
console.log(instances);
// [
// {
// name: "my-drupal-project",
// status: "running",
// type: "drupal9",
// httpurl: "https://my-drupal-project.ddev.site"
// }
// ]getProject(projectId?)
Get detailed information about a specific DDEV project.
// With explicit projectId
const project = await sdk.getProject('my-drupal-project');
// With instance projectId set
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key',
projectId: 'my-drupal-project'
});
const project = await sdk.getProject(); // Uses instance projectId
console.log(project);
// {
// name: "my-drupal-project",
// status: "running",
// type: "drupal9",
// php_version: "8.1",
// database_type: "mysql",
// database_version: "8.0"
// }startProject(projectId?)
Start a DDEV project.
// With explicit projectId
const result = await sdk.startProject('my-drupal-project');
// With instance projectId set
const result = await sdk.startProject(); // Uses instance projectId
console.log(result);
// {
// name: "my-drupal-project",
// status: "running"
// }execCommand(command, projectId?)
Execute a command in a DDEV project.
// With explicit projectId
const result = await sdk.execCommand('drush status', 'my-drupal-project');
// With instance projectId set
const result = await sdk.execCommand('drush status'); // Uses instance projectId
console.log(result);
// {
// stdout: "Drupal version : 9.5.0\nPHP version : 8.1.0\nDatabase system : MySQL 8.0",
// stderr: "",
// exitCode: 0
// }execSqlQuery(query, projectId?)
Execute a SQL query in a DDEV project's database.
// With explicit projectId
const result = await sdk.execSqlQuery('SELECT uid, name, mail FROM users_field_data WHERE uid = 1', 'my-drupal-project');
// With instance projectId set
const result = await sdk.execSqlQuery('SELECT uid, name, mail FROM users_field_data WHERE uid = 1'); // Uses instance projectId
console.log(result);
// {
// stdout: {
// rows: [
// {
// uid: 1,
// name: "admin",
// mail: "[email protected]"
// }
// ]
// },
// stderr: "",
// exitCode: 0
// }runPhpScript(script, projectId?)
Run a PHP script in a DDEV project (Drupal will be automatically bootstrapped).
// With explicit projectId
const script = `
$user = \Drupal\user\Entity\User::load(1);
echo $user->getEmail();
`;
const result = await sdk.runPhpScript(script, 'my-drupal-project');
// With instance projectId set
const result = await sdk.runPhpScript(script); // Uses instance projectId
console.log(result);
// {
// stdout: "[email protected]",
// stderr: "",
// exitCode: 0
// }Template Literal Aliases
For even more convenient syntax, you can use template literal aliases:
$exec(command, projectId?)
Execute command using template literal syntax.
// Instead of: sdk.execCommand('ls -la', projectId)
await sdk.$exec`ls -la`
await sdk.$exec('ls -la') // Also works with regular strings$sql(query, projectId?)
Execute SQL query using template literal syntax.
// Instead of: sdk.execSqlQuery('SELECT * FROM users', projectId)
await sdk.$sql`SELECT id FROM users`
await sdk.$sql('SELECT * FROM users') // Also works with regular strings$php(script, projectId?)
Run PHP script using template literal syntax.
// Instead of: sdk.runPhpScript('echo "Hello";', projectId)
await sdk.$php`echo "Hello from PHP!";`
// Multi-line scripts work great with template literals
await sdk.$php`
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$terms = $term_storage->loadByProperties(['vid' => 'tags']);
$count = count($terms);
echo "Found {$count} taxonomy terms";
`Project ID Management Methods
setProjectId(projectId)
Set the project ID for this SDK instance.
sdk.setProjectId('my-drupal-project');getProjectId()
Get the current project ID.
const projectId = sdk.getProjectId();
console.log(projectId); // "my-drupal-project" or null if not setgetInstanceByUrl(url)
Get instance name (projectId) by URL and set it as the current project.
const projectId = await sdk.getInstanceByUrl('https://my-drupal-project.ddev.site');
console.log(projectId); // "my-drupal-project"
// The projectId is now set on the SDK instanceConvenience Methods
The SDK also provides convenience methods for common operations:
getDrupalStatus(projectId?)
Get Drupal status using drush.
const status = await sdk.getDrupalStatus('my-drupal-project');
// or with instance projectId: await sdk.getDrupalStatus();clearDrupalCache(projectId?)
Clear Drupal cache using drush.
const result = await sdk.clearDrupalCache('my-drupal-project');
// or with instance projectId: await sdk.clearDrupalCache();listFiles(path, projectId?)
List files in the project directory.
const files = await sdk.listFiles('/var/www/html', 'my-drupal-project');
// or with instance projectId: await sdk.listFiles('/var/www/html');getUserInfo(uid, projectId?)
Get user information from database.
const user = await sdk.getUserInfo(1, 'my-drupal-project');
// or with instance projectId: await sdk.getUserInfo(1);getInstalledModules(projectId?)
Get list of installed modules.
const modules = await sdk.getInstalledModules('my-drupal-project');
// or with instance projectId: await sdk.getInstalledModules();createNode(title, type, projectId?)
Create a new node.
const result = await sdk.createNode('My New Article', 'article', 'my-drupal-project');
// or with instance projectId: await sdk.createNode('My New Article', 'article');getPhpInfo(projectId?)
Get PHP information.
const phpInfo = await sdk.getPhpInfo('my-drupal-project');
// or with instance projectId: await sdk.getPhpInfo();Error Handling
The SDK provides comprehensive error handling with detailed error information from the API gateway. All errors are enhanced with additional context to help you debug issues effectively.
Basic Error Handling
try {
const result = await sdk.runPhpScript('echo "Hello";');
console.log('Success:', result);
} catch (error) {
console.error('Error:', error.message);
}Enhanced Error Information
The SDK automatically extracts detailed error information from API responses and provides it in a structured format:
try {
const result = await sdk.runPhpScript(`
<?php
// This will cause a syntax error
if (true {
echo "Missing closing brace";
}
`);
} catch (error) {
console.log('Error Message:', error.message);
console.log('Status Code:', error.statusCode);
// Check if it's an API error with details
if (DdevUpdateManagerSDK.isApiError(error)) {
const details = DdevUpdateManagerSDK.getErrorDetails(error);
console.log('Error Details:', details);
// Access specific error information
console.log('Short Message:', details.shortMessage);
console.log('STDERR:', details.stderr);
console.log('STDOUT:', details.stdout);
console.log('Command:', details.command);
console.log('Exit Code:', details.exitCode);
}
}Error Types and Handling
API Errors (HTTP 4xx/5xx)
These errors contain detailed information from the API gateway:
try {
await sdk.execCommand('invalid-command');
} catch (error) {
if (DdevUpdateManagerSDK.isApiError(error)) {
const details = DdevUpdateManagerSDK.getErrorDetails(error);
// Handle specific error types
switch (error.statusCode) {
case 401:
console.log('Authentication failed - check your API key');
break;
case 404:
console.log('Project not found - check the project ID');
break;
case 500:
console.log('Server error - check the API gateway logs');
if (details.stderr) {
console.log('Error output:', details.stderr);
}
break;
}
// Handle command-specific errors
if (details.exitCode === 1) {
console.log('Command failed with exit code 1 - likely a syntax or runtime error');
} else if (details.exitCode === 127) {
console.log('Command not found - check if the command exists in the DDEV environment');
}
}
}Network/Connection Errors
These occur when the SDK can't reach the API gateway:
try {
await sdk.getHello();
} catch (error) {
if (!DdevUpdateManagerSDK.isApiError(error)) {
console.log('Network/connection error:', error.message);
// This could be a timeout, DNS resolution failure, etc.
}
}Error Handling Utilities
The SDK provides static utility methods for working with errors:
DdevUpdateManagerSDK.isApiError(error)
Check if an error is an API error with details:
if (DdevUpdateManagerSDK.isApiError(error)) {
// This error has API details (statusCode, errorDetails, etc.)
const details = DdevUpdateManagerSDK.getErrorDetails(error);
}DdevUpdateManagerSDK.getErrorDetails(error)
Extract error details from an API error:
const details = DdevUpdateManagerSDK.getErrorDetails(error);
if (details) {
console.log('Command:', details.command);
console.log('Exit Code:', details.exitCode);
console.log('STDERR:', details.stderr);
}DdevUpdateManagerSDK.getOriginalError(error)
Get the original error from an enhanced API error:
const originalError = DdevUpdateManagerSDK.getOriginalError(error);
if (originalError) {
console.log('Original error:', originalError.message);
}Common Error Scenarios
PHP Syntax Errors
try {
await sdk.runPhpScript(`
<?php
if (true { // Missing closing brace
echo "Hello";
}
`);
} catch (error) {
if (DdevUpdateManagerSDK.isApiError(error)) {
const details = DdevUpdateManagerSDK.getErrorDetails(error);
console.log('PHP Syntax Error:', details.stderr);
// Output: Parse error: syntax error, unexpected token "{" in Standard input code on line 2
}
}Command Not Found
try {
await sdk.execCommand('non-existent-command');
} catch (error) {
if (DdevUpdateManagerSDK.isApiError(error)) {
const details = DdevUpdateManagerSDK.getErrorDetails(error);
if (details.exitCode === 127) {
console.log('Command not found:', details.command);
}
}
}Invalid Project ID
try {
await sdk.getProject('non-existent-project');
} catch (error) {
if (error.statusCode === 404) {
console.log('Project not found - check the project ID');
}
}Authentication Errors
try {
await sdk.listInstances();
} catch (error) {
if (error.statusCode === 401) {
console.log('Authentication failed - check your API key');
}
}Best Practices
1. Always Use Try-Catch
async function safeOperation() {
try {
return await sdk.execCommand('drush status');
} catch (error) {
console.error('Operation failed:', error.message);
throw error; // Re-throw if you want to handle it at a higher level
}
}2. Check Error Types
async function handleErrors(error) {
if (DdevUpdateManagerSDK.isApiError(error)) {
// Handle API errors with details
const details = DdevUpdateManagerSDK.getErrorDetails(error);
console.log('API Error:', details.shortMessage);
} else {
// Handle network/connection errors
console.log('Network Error:', error.message);
}
}3. Log Error Details for Debugging
try {
await sdk.runPhpScript(complexPhpCode);
} catch (error) {
console.error('Error:', error.message);
if (DdevUpdateManagerSDK.isApiError(error)) {
const details = DdevUpdateManagerSDK.getErrorDetails(error);
console.error('Full error details:', JSON.stringify(details, null, 2));
}
}4. Create Error Handling Wrappers
async function safeExecute(operation, projectId) {
try {
return await operation(projectId);
} catch (error) {
if (DdevUpdateManagerSDK.isApiError(error)) {
const details = DdevUpdateManagerSDK.getErrorDetails(error);
// Handle specific error types
if (details.exitCode === 1) {
console.log('Command failed with exit code 1 - likely a syntax or runtime error');
if (details.stderr) {
console.log('Error output:', details.stderr);
}
}
}
throw error;
}
}
// Usage
try {
await safeExecute(sdk.getDrupalStatus.bind(sdk), 'my-project');
} catch (error) {
console.log('Error handled by wrapper');
}Error Handling with Template Literals
The template literal methods also benefit from the same error handling:
try {
await sdk.$php`
<?php
echo "Hello from PHP!";
// This will cause a syntax error
if (true {
echo "Missing closing brace";
}
`;
} catch (error) {
if (DdevUpdateManagerSDK.isApiError(error)) {
const details = DdevUpdateManagerSDK.getErrorDetails(error);
console.log('PHP syntax error in template literal:', details.stderr);
}
}For more examples, see the error-handling-example.js file in this package.
Examples
Complete Workflow Example with Project ID
const DdevUpdateManagerSDK = require('api-gateway-sdk');
async function manageDdevProject() {
// Set project ID in constructor
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key',
baseUrl: 'http://localhost:3000',
projectId: 'my-drupal-project'
});
try {
// List all instances
const instances = await sdk.listInstances();
console.log('Available instances:', instances);
// Get project details (no projectId needed)
const project = await sdk.getProject();
console.log('Project details:', project);
// Start project if not running (no projectId needed)
if (project.status !== 'running') {
await sdk.startProject();
console.log('Project started');
}
// Get Drupal status (no projectId needed)
const status = await sdk.getDrupalStatus();
console.log('Drupal status:', status);
// Clear cache (no projectId needed)
await sdk.clearDrupalCache();
console.log('Cache cleared');
// Get installed modules (no projectId needed)
const modules = await sdk.getInstalledModules();
console.log('Installed modules:', modules);
} catch (error) {
console.error('Error:', error.message);
}
}
manageDdevProject();Template Literal Aliases Example
async function templateLiteralExample() {
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key',
projectId: 'my-drupal-project'
});
// Execute commands with template literals
const files = await sdk.$exec`ls -la`;
console.log('Files:', files.stdout);
// SQL queries with template literals
const users = await sdk.$sql`SELECT uid, name FROM users_field_data WHERE uid = 1`;
console.log('Users:', users.stdout);
// PHP scripts with template literals
const result = await sdk.$php`
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$terms = $term_storage->loadByProperties(['vid' => 'tags']);
$count = count($terms);
echo "Found {$count} taxonomy terms";
`;
console.log('PHP result:', result.stdout);
}Project ID by URL Example
async function projectByUrlExample() {
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key'
});
// Set project ID by URL
const projectId = await sdk.getInstanceByUrl('https://my-drupal-project.ddev.site');
console.log('Project ID:', projectId); // "my-drupal-project"
// Now use methods without projectId
const status = await sdk.getDrupalStatus();
const cache = await sdk.clearDrupalCache();
// Use template literals
const files = await sdk.$exec`pwd`;
const users = await sdk.$sql`SELECT COUNT(*) as count FROM users_field_data`;
}Database Operations Example
async function databaseOperations() {
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key',
projectId: 'my-drupal-project'
});
// Get user information (no projectId needed)
const user = await sdk.getUserInfo(1);
console.log('Admin user:', user);
// Custom SQL query with template literal
const result = await sdk.$sql`
SELECT nid, title, created
FROM node_field_data
WHERE type = 'article'
ORDER BY created DESC
LIMIT 5
`;
console.log('Recent articles:', result);
}PHP Script Execution Example
async function phpOperations() {
const sdk = new DdevUpdateManagerSDK({
apiKey: 'your-api-key',
projectId: 'my-drupal-project'
});
// Create a new vocabulary with template literal
const vocabResult = await sdk.$php`
$vocabulary = \Drupal\taxonomy\Entity\Vocabulary::create([
'name' => 'Test Vocabulary',
'vid' => 'test_vocabulary'
]);
$vocabulary->save();
echo "Vocabulary created: " . $vocabulary->id();
`;
console.log('Vocabulary creation result:', vocabResult);
// Get node count with template literal
const countResult = await sdk.$php`
$query = \Drupal::entityQuery('node')->accessCheck(FALSE);
$count = $query->count()->execute();
echo "Total nodes: " . $count;
`;
console.log('Node count result:', countResult);
}TypeScript Support
The SDK is written in JavaScript but can be used with TypeScript. You can create type definitions for better IntelliSense:
interface DdevInstance {
name: string;
status: string;
type: string;
httpurl: string;
}
interface ProjectDetails extends DdevInstance {
php_version: string;
database_type: string;
database_version: string;
}
interface CommandResult {
stdout: string;
stderr: string;
exitCode: number;
}
interface SqlResult {
stdout: {
rows: any[];
hasData: boolean;
executionTime: string;
affectedItemsCount: number;
warningsCount: number;
warnings: string[];
info: string;
autoIncrementValue: number;
};
stderr: string;
exitCode: number;
}License
ISC
