README
ΒΆ
Hello World Plugin - Complete Apito SDK Showcase
This plugin demonstrates the complete capabilities of the Apito Plugin SDK, serving as both a comprehensive example and a migration guide for developers building plugins for the Apito Engine.
π What This Plugin Demonstrates
Complete SDK Feature Coverage
This plugin showcases every major SDK feature in production-ready examples:
- β GraphQL Queries & Mutations with complex type definitions
- β REST API Endpoints with full CRUD operations
- β Custom Functions for business logic
- β Complex Object Types with nested structures
- β Automatic Argument Parsing and type conversion
- β Context & Authentication handling
- β Error Handling & Validation patterns
- β Debug Support with VSCode integration
π Migration Impact
Code Reduction Statistics
- Original Version: 675 lines of boilerplate code
- SDK Version: 310 lines of business logic
- Reduction: 54% fewer lines (365 lines eliminated)
- Boilerplate Eliminated: ~400 lines of HashiCorp plugin, gRPC, and protobuf setup
Developer Experience Improvements
- β‘ 5x Faster Development: Focus on business logic, not infrastructure
- π§ Zero Boilerplate: SDK handles all plugin communication
- π‘οΈ Type Safety: Built-in helpers prevent runtime errors
- π Easy Debugging: Native debugging support with delve
- π Self-Documenting: Schema definitions serve as documentation
π― Core Features Demonstrated
1. GraphQL Schema with Complex Types
Simple Queries
// Basic string query with arguments
plugin.RegisterQuery("helloWorldQueryFahim",
sdk.FieldWithArgs("String", "Hello World Plugin Query", map[string]interface{}{
"name": sdk.StringArg("Name to greet (optional)"),
"object": sdk.ObjectArg("Object argument", map[string]interface{}{
"name": sdk.StringProperty("Object name"),
"age": sdk.IntProperty("Object age"),
}),
"arrayofObjects": sdk.ListArg("Object", "Array of objects"),
}),
helloWorldResolver)
Complex Object Types
// Define reusable object types
userType := sdk.NewObjectType("User", "A user in the system").
AddStringField("id", "User ID", false).
AddStringField("name", "User's full name", false).
AddStringField("email", "User's email address", true).
AddObjectField("address", "User's address", addressType, true).
AddObjectListField("tags", "User tags", tagType, true, false).
AddBooleanField("active", "Whether the user is active", false).
Build()
// Use in queries
plugin.RegisterQuery("getUserProfile",
sdk.ComplexObjectFieldWithArgs("Get user profile", userType, map[string]interface{}{
"userId": sdk.StringArg("User ID to fetch"),
}),
getUserProfileResolver)
Paginated Responses
// Built-in pagination support
paginatedProductType := sdk.PaginatedResponseType("Product")
plugin.RegisterQuery("getProductsPaginated",
sdk.ComplexObjectFieldWithArgs("Get paginated products", paginatedProductType, map[string]interface{}{
"page": sdk.IntArg("Page number (1-based)"),
"pageSize": sdk.IntArg("Number of items per page"),
"category": sdk.StringArg("Filter by category"),
}),
getProductsPaginatedResolver)
2. REST API Endpoints - Complete CRUD
Simple GET Endpoint
plugin.RegisterRESTAPI(sdk.RESTEndpoint{
Method: "GET",
Path: "/hello",
Description: "Simple hello endpoint",
Schema: map[string]interface{}{},
}, helloRESTHandler)
Resource Management (Full CRUD)
// CREATE - POST /users
plugin.RegisterRESTAPI(sdk.RESTEndpoint{
Method: "POST",
Path: "/users",
Description: "Create a new user",
Schema: map[string]interface{}{
"name": "string",
"email": "string",
"username": "string",
"active": "boolean",
},
}, createUserRESTHandler)
// READ - GET /users/:id
plugin.RegisterRESTAPI(sdk.RESTEndpoint{
Method: "GET",
Path: "/users/:id",
Description: "Get user by ID with query parameters",
Schema: map[string]interface{}{
"include_profile": "boolean",
"format": "string",
},
}, getUserRESTHandler)
// UPDATE - PUT /users/:id
plugin.RegisterRESTAPI(sdk.RESTEndpoint{
Method: "PUT",
Path: "/users/:id",
Description: "Update user by ID",
Schema: map[string]interface{}{
"name": "string",
"email": "string",
"active": "boolean",
},
}, updateUserRESTHandler)
// DELETE - DELETE /users/:id
plugin.RegisterRESTAPI(sdk.RESTEndpoint{
Method: "DELETE",
Path: "/users/:id",
Description: "Delete user by ID",
Schema: map[string]interface{}{},
}, deleteUserRESTHandler)
Advanced REST Features
// Pagination and Search
plugin.RegisterRESTAPI(sdk.RESTEndpoint{
Method: "GET",
Path: "/users",
Description: "List users with pagination",
Schema: map[string]interface{}{
"page": "integer",
"pageSize": "integer",
"search": "string",
"active": "boolean",
},
}, listUsersRESTHandler)
// File Upload Simulation
plugin.RegisterRESTAPI(sdk.RESTEndpoint{
Method: "POST",
Path: "/upload",
Description: "Handle file upload with metadata",
Schema: map[string]interface{}{
"filename": "string",
"content": "string",
"tags": "array",
"description": "string",
},
}, uploadFileRESTHandler)
// Complex Nested Data
plugin.RegisterRESTAPI(sdk.RESTEndpoint{
Method: "POST",
Path: "/complex-data",
Description: "Handle complex nested JSON data",
Schema: map[string]interface{}{
"user": map[string]interface{}{
"name": "string",
"email": "string",
"address": map[string]interface{}{
"street": "string",
"city": "string",
"zip": "string",
},
},
"preferences": "array",
"metadata": "object",
},
}, processComplexDataRESTHandler)
3. Resolver Implementation Patterns
Type-Safe Argument Extraction
func getUserRESTHandler(ctx context.Context, args map[string]interface{}) (interface{}, error) {
// Extract path parameters
userID := sdk.GetStringArg(args, ":id", "unknown")
// Extract query parameters with defaults
includeProfile := sdk.GetBoolArg(args, "include_profile", false)
format := sdk.GetStringArg(args, "format", "json")
// Business logic here...
return response, nil
}
GraphQL Resolver Pattern
func getUserProfileResolver(ctx context.Context, rawArgs map[string]interface{}) (interface{}, error) {
// Automatic argument parsing based on schema
args := sdk.ParseArgsForResolver("getUserProfile", rawArgs)
userID := sdk.GetStringArg(args, "userId", "default-user")
// Access context data
pluginID := sdk.GetPluginID(rawArgs)
projectID := sdk.GetProjectID(rawArgs)
// Return complex object matching schema
return map[string]interface{}{
"id": userID,
"name": "John Doe",
"email": "john@example.com",
"address": map[string]interface{}{
"street": "123 Main St",
"city": "New York",
"state": "NY",
"zip": "10001",
},
"active": true,
}, nil
}
4. Data Types & Validation
Supported Types
- πΈ Primitives: String, Int, Boolean, Float
- πΈ Objects: Complex nested structures
- πΈ Arrays: Lists of primitives or objects
- πΈ Object Arrays: Complex array processing with
ArrayObjectArg
- πΈ Enums: Predefined value sets
- πΈ Custom Types: User-defined complex types
Automatic Type Conversion
// The SDK automatically handles type conversion
name := sdk.GetStringArg(args, "name", "default") // string
age := sdk.GetIntArg(args, "age", 0) // int
active := sdk.GetBoolArg(args, "active", true) // bool
price := sdk.GetFloatArg(args, "price", 0.0) // float64
tags := sdk.GetArrayArg(args, "tags") // []interface{}
user := sdk.GetObjectArg(args, "user") // map[string]interface{}
users := sdk.GetArrayObjectArg(args, "users") // []map[string]interface{}
π οΈ Development Guide
Quick Start
- Initialize Plugin
plugin := sdk.Init("your-plugin-name", "1.0.0", "your-api-key")
- Register GraphQL Schemas
plugin.RegisterQuery("queryName", schema, resolverFunction)
plugin.RegisterMutation("mutationName", schema, resolverFunction)
- Register REST APIs
plugin.RegisterRESTAPI(sdk.RESTEndpoint{...}, handlerFunction)
- Start Server
plugin.Serve()
Build & Deploy
# Development build
go build -o your-plugin .
# Production build with optimizations
go build -ldflags="-s -w" -o your-plugin .
# Test the plugin directly
./your-plugin
# The Apito Engine will load it automatically via plugins.yml
Testing Your Plugin
GraphQL Queries
# Test GraphQL via engine
curl -X POST http://localhost:5050/v3/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "{ helloWorldQueryFahim(name: \"Developer\") }"
}'
REST API Endpoints
# Test simple GET
curl http://localhost:5050/plugins/hc-hello-world-plugin/hello
# Test POST with data
curl -X POST http://localhost:5050/plugins/hc-hello-world-plugin/users \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"username": "johndoe"
}'
# Test with path parameters
curl http://localhost:5050/plugins/hc-hello-world-plugin/users/123?include_profile=true
π Debug Support
This plugin includes full debug support with VSCode integration:
Debug Setup
- Enable Debug Mode: Set
debug: true
inplugins.yml
- Start Engine: Plugin logs PID for attachment
- Attach Debugger: Use provided debug scripts
- Set Breakpoints: Debug resolvers in real-time
Debug Scripts Included
runDebug.sh
- Attach delve to running pluginkillDebug.sh
- Clean up debug processesDEBUG_GUIDE.md
- Complete debugging instructions
π API Documentation
Available Endpoints
GraphQL Queries
helloWorldQueryFahim
- Simple greeting with argumentsgetUserProfile
- Complex user objectgetUsers
- Array of users with filteringgetProduct
- Product with arrays and metadatagetProductsPaginated
- Paginated product listing
GraphQL Mutations
createUser
- User creation with validationprocessBulkTags
- Array object processing example
REST Endpoints
GET /hello
- Simple greetingPOST /custom-hello
- Custom greeting with parametersGET /users
- List users with pagination and searchGET /users/:id
- Get user with profile optionPOST /users
- Create new userPUT /users/:id
- Update userDELETE /users/:id
- Delete userPOST /upload
- File upload simulationPOST /complex-data
- Complex nested data processingGET /status
- Plugin health and metrics
Response Formats
Success Response
{
"success": true,
"data": { ... },
"message": "Operation completed successfully"
}
Error Response
{
"success": false,
"error": "Error description",
"code": 400
}
Paginated Response
{
"success": true,
"data": [...],
"pagination": {
"page": 1,
"pageSize": 10,
"total": 50,
"totalPages": 5
}
}
π§ Configuration
Plugin Configuration (plugins.yml
)
hc-hello-world-plugin:
id: "hc-hello-world-plugin"
language: "go"
title: "Hello World Plugin (SDK Showcase)"
description: "Complete SDK feature demonstration"
type: "internal"
enable: true
debug: true
version: "2.0.0"
env_vars:
- key: "DEBUG_MODE"
value: "true"
π Migration Guide
From Original Plugin System
- Replace Boilerplate: Remove all HashiCorp plugin setup code
- Use SDK Init: Replace handshake config with
sdk.Init()
- Simplify Schemas: Use SDK's declarative schema builders
- Update Resolvers: Use SDK's automatic argument parsing
- Add Type Safety: Leverage SDK's type helpers
Benefits After Migration
- β 54% Less Code: Focus on business logic
- β Type Safety: Prevent runtime errors
- β Better Testing: Isolated resolver functions
- β Easier Debugging: Native debug support
- β Self-Documenting: Schema serves as API docs
π Best Practices
Schema Design
- Use descriptive names and descriptions
- Define reusable object types
- Implement proper validation
- Support pagination for lists
Error Handling
- Return structured error responses
- Include helpful error messages
- Use appropriate HTTP status codes
- Log errors for debugging
Performance
- Use type helpers for efficient parsing
- Implement caching where appropriate
- Avoid heavy operations in resolvers
- Use context for request cancellation
Security
- Validate all inputs
- Sanitize user data
- Use context for authentication
- Implement rate limiting
π Learning Resources
SDK Documentation
- Main Repository: https://github.com/apito-io/go-apito-plugin-sdk
- Type System Guide:
OBJECT_TYPES_GUIDE.md
- Debug Documentation:
DEBUG_GUIDE.md
- SDK Examples:
/examples
directory
Related Files
main.go
- Complete SDK implementation (1200+ lines)DEBUG_README.md
- Debug setup and troubleshootingOBJECT_TYPES_GUIDE.md
- Advanced type system usage
π Get Started
- Clone this plugin as a starting point
- Modify the schemas to match your business logic
- Implement your resolvers using the provided patterns
- Test with both GraphQL and REST APIs
- Deploy using the Apito Engine
This plugin demonstrates the complete power of the Apito Plugin SDK - same functionality, 54% less code, infinite possibilities!
Documentation
ΒΆ
There is no documentation for this package.
Click to show internal directories.
Click to hide internal directories.