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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@apito-io/js-apito-plugin-sdk

v0.2.4

Published

JavaScript SDK for building Apito HashiCorp plugins

Readme

Apito JavaScript Plugin SDK

A simplified JavaScript/Node.js SDK for building HashiCorp plugins for the Apito Engine. This SDK abstracts away all the boilerplate code and provides a clean, easy-to-use interface for plugin developers.

Installation

npm install @apito-io/js-apito-plugin-sdk
# or
yarn add @apito-io/js-apito-plugin-sdk

Quick Start

Basic Plugin Structure

const { init } = require("@apito/js-plugin-sdk");
const {
  StringField,
  FieldWithArgs,
  StringArg,
  GETEndpoint,
  ObjectSchema,
  StringSchema,
} = require("@apito/js-plugin-sdk/helpers");

async function main() {
  // Initialize the plugin
  const plugin = init("my-awesome-plugin", "1.0.0", "your-api-key");

  // Register GraphQL queries
  plugin.registerQuery(
    "hello",
    FieldWithArgs("String", "Returns a greeting", {
      name: StringArg("Name to greet"),
    }),
    helloResolver
  );

  // Register GraphQL mutations
  plugin.registerMutation(
    "createUser",
    FieldWithArgs("String", "Creates a new user", {
      name: StringArg("User name"),
      email: StringArg("User email"),
    }),
    createUserResolver
  );

  // Register REST API endpoints
  plugin.registerRESTAPI(
    GETEndpoint("/hello", "Simple hello endpoint")
      .withResponseSchema(
        ObjectSchema({
          message: StringSchema("Hello message"),
          timestamp: StringSchema("Current timestamp"),
        })
      )
      .build(),
    helloRESTHandler
  );

  // Register custom functions
  plugin.registerFunction("processData", processDataFunction);

  // Start the plugin server
  await plugin.serve();
}

// GraphQL Resolvers
async function helloResolver(context, args) {
  const name = args.name || "World";
  return `Hello, ${name}!`;
}

async function createUserResolver(context, args) {
  const { name, email } = args;
  return `Created user: ${name} <${email}>`;
}

// REST Handlers
async function helloRESTHandler(context, args) {
  return {
    message: "Hello from REST API!",
    timestamp: new Date().toISOString(),
  };
}

// Custom Functions
async function processDataFunction(context, args) {
  return "Data processed successfully";
}

// Start the plugin
main().catch(console.error);

API Reference

Plugin Initialization

init(name, version, apiKey)

Initializes a new plugin instance.

  • name: Plugin name (string)
  • version: Plugin version (string)
  • apiKey: API key for authentication (string)

Returns a Plugin instance.

GraphQL Schema Registration

Individual Registration

// Register a single query
plugin.registerQuery(name, field, resolver);

// Register a single mutation
plugin.registerMutation(name, field, resolver);

Batch Registration

// Register multiple queries at once
const queries = {
  getUser: FieldWithArgs("String", "Get user by ID", {
    id: StringArg("User ID"),
  }),
  getUsers: StringField("Get all users"),
};

const resolvers = {
  getUser: getUserResolver,
  getUsers: getUsersResolver,
};

plugin.registerQueries(queries, resolvers);

GraphQL Field Helpers

Basic Fields

const {
  StringField,
  IntField,
  BooleanField,
  FloatField,
  ListField,
  NonNullField,
} = require("@apito/js-plugin-sdk/helpers");

StringField("Description"); // String
IntField("Description"); // Int
BooleanField("Description"); // Boolean
FloatField("Description"); // Float
ListField("String", "Description"); // [String]
NonNullField("String", "Description"); // String!
NonNullListField("String", "Description"); // [String!]!

Fields with Arguments

const {
  FieldWithArgs,
  StringArg,
  IntArg,
  BooleanArg,
} = require("@apito/js-plugin-sdk/helpers");

FieldWithArgs("String", "Get user greeting", {
  name: StringArg("User name"),
  age: IntArg("User age"),
  active: BooleanArg("Is user active"),
});

Object Fields

const { ObjectField, ObjectArg } = require("@apito/js-plugin-sdk/helpers");

ObjectField("User object", {
  id: StringArg("User ID"),
  name: StringArg("User name"),
  email: StringArg("User email"),
});

Complex Object Types

const { NewObjectType } = require("@apito/js-plugin-sdk/helpers");

// Define a complex object type
const userType = NewObjectType("User", "A user in the system")
  .addStringField("id", "User ID", false) // Required field
  .addStringField("name", "User name", false) // Required field
  .addStringField("email", "User email", true) // Optional field
  .addBooleanField("active", "Is user active", false)
  .build();

// Use in GraphQL queries
plugin.registerQuery(
  "getUserProfile",
  FieldWithArgs("User", "Get user profile", {
    userId: StringArg("User ID to fetch"),
  }),
  getUserProfileResolver
);

REST API Registration

Individual Registration

const {
  GETEndpoint,
  POSTEndpoint,
  ObjectSchema,
  StringSchema,
} = require("@apito/js-plugin-sdk/helpers");

const endpoint = GETEndpoint("/users", "Get all users")
  .withResponseSchema(
    ObjectSchema({
      users: ArraySchema(
        ObjectSchema({
          id: StringSchema("User ID"),
          name: StringSchema("User name"),
        })
      ),
    })
  )
  .build();

plugin.registerRESTAPI(endpoint, getUsersHandler);

Batch Registration

const endpoints = [
  GETEndpoint("/health", "Health check").build(),
  POSTEndpoint("/users", "Create user").build(),
];

const handlers = {
  "GET_/health": healthHandler,
  "POST_/users": createUserHandler,
};

plugin.registerRESTAPIs(endpoints, handlers);

REST Endpoint Builders

const {
  GETEndpoint,
  POSTEndpoint,
  PUTEndpoint,
  DELETEEndpoint,
  PATCHEndpoint,
} = require("@apito/js-plugin-sdk/helpers");

GETEndpoint(path, description);
POSTEndpoint(path, description);
PUTEndpoint(path, description);
DELETEEndpoint(path, description);
PATCHEndpoint(path, description);

REST Schema Helpers

const {
  ObjectSchema,
  ArraySchema,
  StringSchema,
  IntegerSchema,
  BooleanSchema,
} = require("@apito/js-plugin-sdk/helpers");

ObjectSchema(properties); // Object schema
ArraySchema(itemSchema); // Array schema
StringSchema(description); // String schema
IntegerSchema(description); // Integer schema
BooleanSchema(description); // Boolean schema

Function Registration

Individual Registration

plugin.registerFunction("processData", async (context, args) => {
  // Function logic here
  return "result";
});

Batch Registration

const functions = {
  processData: processDataFunction,
  validateData: validateDataFunction,
  transformData: transformDataFunction,
};

plugin.registerFunctions(functions);

Utility Functions

Argument Extraction

const {
  getStringArg,
  getIntArg,
  getBoolArg,
  getObjectArg,
  getArrayArg,
} = require("@apito/js-plugin-sdk/helpers");

async function myResolver(context, args) {
  const name = getStringArg(args, "name", "Default Name");
  const age = getIntArg(args, "age", 0);
  const active = getBoolArg(args, "active", true);
  const user = getObjectArg(args, "user", {});
  const tags = getArrayArg(args, "tags", []);

  return { name, age, active, user, tags };
}

REST Parameter Extraction

const {
  getPathParam,
  getQueryParam,
  getBodyParam,
  logRESTArgs,
} = require("@apito/js-plugin-sdk/helpers");

async function myRESTHandler(context, args) {
  // Debug log all arguments
  logRESTArgs("myHandler", args);

  // Extract different parameter types
  const userId = getPathParam(args, ":id"); // Path parameter
  const search = getQueryParam(args, "search"); // Query parameter
  const userData = getBodyParam(args, "user"); // Body parameter

  return { userId, search, userData };
}

Function Signatures

/**
 * @typedef {Function} ResolverFunc
 * @param {Object} context - Request context
 * @param {Object} args - Function arguments
 * @returns {Promise<any>} Resolver result
 */

/**
 * @typedef {Function} RESTHandlerFunc
 * @param {Object} context - Request context
 * @param {Object} args - Function arguments
 * @returns {Promise<any>} Handler result
 */

/**
 * @typedef {Function} FunctionHandlerFunc
 * @param {Object} context - Request context
 * @param {Object} args - Function arguments
 * @returns {Promise<any>} Function result
 */

Advanced Examples

Complex GraphQL Query with Nested Objects

const {
  FieldWithArgs,
  ObjectArg,
  ListArg,
} = require("@apito/js-plugin-sdk/helpers");

plugin.registerQuery(
  "processComplexData",
  FieldWithArgs("String", "Process complex input data", {
    user: ObjectArg("Single user", {
      id: IntArg("User ID"),
      name: StringArg("User name"),
      email: StringArg("User email"),
      active: BooleanArg("Is user active"),
    }),
    tags: ListArg("String", "Array of tags"),
    users: ListArg("Object", "Array of user objects"),
  }),
  processComplexDataResolver
);

async function processComplexDataResolver(context, args) {
  const { user, tags, users } = args;

  // Process the complex data
  const result = {
    processedUser: user,
    tagCount: tags.length,
    userCount: users.length,
    timestamp: new Date().toISOString(),
  };

  return JSON.stringify(result);
}

REST API with Complex Schema

const {
  POSTEndpoint,
  ObjectSchema,
  ArraySchema,
} = require("@apito/js-plugin-sdk/helpers");

const endpoint = POSTEndpoint("/api/users", "Create new user")
  .withRequestSchema(
    ObjectSchema({
      user: ObjectSchema({
        name: StringSchema("User name"),
        email: StringSchema("User email"),
        age: IntegerSchema("User age"),
        metadata: ObjectSchema({
          department: StringSchema("User department"),
          role: StringSchema("User role"),
        }),
      }),
      tags: ArraySchema(StringSchema("Tag name")),
    })
  )
  .withResponseSchema(
    ObjectSchema({
      success: BooleanSchema("Operation success"),
      user_id: StringSchema("Created user ID"),
      message: StringSchema("Response message"),
    })
  )
  .build();

plugin.registerRESTAPI(endpoint, createUserWithMetadataHandler);

async function createUserWithMetadataHandler(context, args) {
  const { logRESTArgs, getBodyParam } = require("@apito/js-plugin-sdk/helpers");

  logRESTArgs("createUserWithMetadata", args);

  const user = getBodyParam(args, "user");
  const tags = getBodyParam(args, "tags");

  // Create user logic here
  const userId = `user_${Date.now()}`;

  return {
    success: true,
    user_id: userId,
    message: `User ${user.name} created successfully with ${tags.length} tags`,
  };
}

Health Checks

// Register custom health checks
plugin.registerHealthCheck(async (context) => {
  // Check database connection
  const dbStatus = await checkDatabase();

  return {
    status: dbStatus.connected ? "healthy" : "unhealthy",
    database: {
      connected: dbStatus.connected,
      latency: dbStatus.latency,
    },
  };
});

plugin.registerHealthCheck(async (context) => {
  // Check external API
  const apiStatus = await checkExternalAPI();

  return {
    status: apiStatus.available ? "healthy" : "degraded",
    external_api: {
      available: apiStatus.available,
      response_time: apiStatus.responseTime,
    },
  };
});

Error Handling

All resolver functions, REST handlers, and custom functions should handle errors gracefully:

async function myResolver(context, args) {
  try {
    // Validate input
    const name = getStringArg(args, "name");
    if (!name) {
      throw new Error("Name is required");
    }

    // Process data
    const result = await processData(name);
    return result;
  } catch (error) {
    console.error("Resolver error:", error);
    throw error; // Re-throw to be handled by the SDK
  }
}

Context Usage

The context parameter provides access to the request context:

async function myResolver(context, args) {
  // Access context information
  const pluginId = context.plugin_id;
  const projectId = context.project_id;

  console.log(
    `Processing request for plugin ${pluginId} in project ${projectId}`
  );

  // Use context for request-scoped operations
  return processWithContext(context, args);
}

Building and Running

  1. Create your plugin using the SDK
  2. Install dependencies:
    npm install
  3. Run your plugin:
    node main.js
  4. The Apito Engine will execute your plugin as a HashiCorp plugin

Environment Variables

The SDK automatically handles environment variables passed by the engine:

  • PLUGIN_GRPC_PORT: gRPC server port (automatically assigned)
  • Custom environment variables from the engine configuration

Debugging

Enable debug logging by setting environment variables:

NODE_ENV=development node main.js

The SDK provides structured logging for:

  • Plugin initialization
  • Schema registration
  • Function execution
  • Error handling

Best Practices

  1. Use descriptive names for GraphQL fields and REST endpoints
  2. Validate input data in your resolvers and handlers
  3. Handle errors gracefully and return meaningful error messages
  4. Use the utility functions for consistent argument extraction
  5. Add proper JSDoc comments for better IDE support
  6. Test your plugins thoroughly before deployment
  7. Use async/await for better error handling and readability

TypeScript Support

While this SDK is written in JavaScript, you can use it with TypeScript:

import { init } from "@apito/js-plugin-sdk";
import {
  StringField,
  FieldWithArgs,
  StringArg,
} from "@apito/js-plugin-sdk/helpers";

interface ResolverContext {
  plugin_id: string;
  project_id: string;
}

interface HelloArgs {
  name?: string;
}

async function helloResolver(
  context: ResolverContext,
  args: HelloArgs
): Promise<string> {
  const name = args.name || "World";
  return `Hello, ${name}!`;
}

// Rest of your plugin code...

License

This SDK is part of the Apito Engine project.

Support

For questions and support, please visit the Apito Documentation or contact our support team.