npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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

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-sdk

Quick 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 authentication
  • baseUrl (optional): Base URL of the API (default: http://localhost:3000)
  • projectId (optional): Set a default project ID for this SDK instance
  • requestOptions (optional): Additional options to pass to the got HTTP 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 set

getInstanceByUrl(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 instance

Convenience 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