Skip to content

Public API Overview

James Maes edited this page Aug 27, 2025 · 2 revisions

Public API Overview

QQQ provides a comprehensive set of public APIs for building applications and extending the framework. This document outlines the key public interfaces that users and contributors can depend on.

Quick Links

API Stability Guarantees

QQQ follows semantic versioning for API stability:

  • Public APIs: Guaranteed stable within major versions
  • Internal APIs: May change without notice
  • Breaking Changes: Only in major version releases
  • Deprecation Policy: Two-version timeline for removal

Core Framework APIs

Action Framework

The foundation for business logic in QQQ:

// Core action interface
public abstract class AbstractQActionFunction<Input extends AbstractActionInput, Output extends AbstractActionOutput>
{
   public abstract Output execute(Input input) throws QException;
   
   public CompletableFuture<Output> executeAsync(Input input) throws QException;
}

// Input/Output base classes
public abstract class AbstractActionInput implements Serializable
{
   // Common input functionality
}

public abstract class AbstractActionOutput implements Serializable
{
   // Common output functionality
}

Usage Example:

public class CreateUserAction extends AbstractQActionFunction<CreateUserInput, CreateUserOutput>
{
   @Override
   public CreateUserOutput execute(CreateUserInput input) throws QException
   {
      // Business logic implementation
      User user = userService.createUser(input.getUserName(), input.getEmail());
      return new CreateUserOutput(user.getId(), user.getStatus());
   }
}

Context Management

Thread-local context for QQQ operations:

public class QContext
{
   // Get current context
   public static QContext getCurrent();
   
   // Initialize context
   public static void init(QInstance instance, QSession session);
   
   // Clear context
   public static void clear();
   
   // Access context data
   public QInstance getQInstance();
   public QSession getQSession();
   public QBackendTransaction getTransaction();
}

Usage Example:

// Initialize context for action execution
QContext.init(instance, session);
try
{
   // Execute actions within context
   CreateUserOutput output = action.execute(input);
}
finally
{
   // Always clean up context
   QContext.clear();
}

Metadata System

Configuration-driven application definition:

public class QInstance
{
   // Add metadata objects
   public void addMetaData(AbstractMetaData metadata);
   public void addBackend(QBackendMetaData backend);
   public void addTable(QTableMetaData table);
   public void addAction(QActionMetaData action);
   
   // Retrieve metadata
   public <T extends AbstractMetaData> Map<String, T> getMetaData(Class<T> type);
   public QBackendMetaData getBackend(String name);
   public QTableMetaData getTable(String name);
}

Usage Example:

QInstance instance = new QInstance();

// Configure backends
instance.addBackend(new RDBMSBackendMetaData()
   .withName("mainDb")
   .withConnectionUrl("jdbc:postgresql://localhost:5432/mydb"));

// Configure tables
instance.addTable(QTableMetaData.newForEntity(UserRecord.class)
   .withName("users")
   .withBackendName("mainDb"));

// Configure actions
instance.addAction(QActionMetaData.newForAction(CreateUserAction.class)
   .withName("createUser")
   .withTableName("users"));

Backend Module APIs

Backend Module Interface

Extend QQQ with new storage backends:

public interface QBackendModuleInterface
{
   // Module identification
   String getBackendType();
   
   // Metadata classes
   Class<? extends QBackendMetaData> getBackendMetaDataClass();
   Class<? extends QTableBackendDetails> getTableBackendDetailsClass();
   
   // Action interfaces
   QueryInterface getQueryInterface();
   InsertInterface getInsertInterface();
   UpdateInterface getUpdateInterface();
   DeleteInterface getDeleteInterface();
   CountInterface getCountInterface();
}

Implementation Example:

public class RedisBackendModule implements QBackendModuleInterface
{
   @Override
   public String getBackendType()
   {
      return "redis";
   }
   
   @Override
   public Class<? extends QBackendMetaData> getBackendMetaDataClass()
   {
      return RedisBackendMetaData.class;
   }
   
   // Implement required action interfaces...
}

Data Operation Interfaces

Standard interfaces for data operations:

public interface QueryInterface
{
   QueryOutput execute(QueryInput input) throws QException;
}

public interface InsertInterface
{
   InsertOutput execute(InsertInput input) throws QException;
}

public interface UpdateInterface
{
   UpdateOutput execute(UpdateInput input) throws QException;
}

public interface DeleteInterface
{
   DeleteOutput execute(DeleteInput input) throws QException;
}

Middleware APIs

HTTP Server (Javalin)

REST API endpoints and web interface:

public class QApplicationJavalinServer
{
   // Start server
   public void start(QInstance instance);
   
   // Stop server
   public void stop();
   
   // Add custom routes
   public void addRouteProvider(RouteProvider provider);
}

// Custom route provider
public interface RouteProvider
{
   void registerRoutes(Javalin app, QInstance instance);
}

Usage Example:

QApplicationJavalinServer server = new QApplicationJavalinServer();

// Add custom API endpoints
server.addRouteProvider(new CustomApiProvider());

// Start server with QQQ instance
server.start(qInstance);

CLI Commands (PicoCLI)

Command-line interface for QQQ applications:

public class QApplicationCli
{
   // Execute CLI command
   public void execute(String[] args);
   
   // Add custom commands
   public void addCommand(Command command);
}

// Custom CLI command
@Command(name = "custom", description = "Custom operation")
public class CustomCommand implements Runnable
{
   @Option(names = {"-p", "--param"}, description = "Parameter")
   private String parameter;
   
   @Override
   public void run()
   {
      // Command implementation
   }
}

Frontend Framework APIs

React Dashboard

Extend the Material-UI dashboard:

// Widget interface
interface QWidget {
   id: string;
   type: string;
   props: Record<string, any>;
   render: (context: QWidgetContext) => React.ReactElement;
}

// Widget context
interface QWidgetContext {
   instance: QInstance;
   session: QSession;
   data: any;
}

// Custom widget
class CustomWidget implements QWidget {
   id = 'custom-widget';
   type = 'custom';
   props = {};
   
   render(context: QWidgetContext) {
      return <div>Custom Widget Content</div>;
   }
}

Configuration APIs

Environment Configuration

Configure QQQ applications:

public class QConfiguration
{
   // Load from environment variables
   public static QConfiguration fromEnvironment();
   
   // Load from properties file
   public static QConfiguration fromProperties(Properties props);
   
   // Load from YAML file
   public static QConfiguration fromYaml(InputStream yaml);
   
   // Access configuration values
   public String getString(String key);
   public Integer getInteger(String key);
   public Boolean getBoolean(String key);
}

Configuration Example:

# application.yml
qqq:
  application:
    name: "My QQQ App"
    version: "1.0.0"
  
  database:
    backend: "rdbms"
    connection:
      url: "jdbc:postgresql://localhost:5432/mydb"
      username: "${DB_USERNAME}"
      password: "${DB_PASSWORD}"
  
  server:
    port: 8080
    context-path: "/api"

Extension APIs

Metadata Producer Interface

Create custom metadata:

public interface MetaDataProducerInterface
{
   // Produce metadata
   void produce(QInstance instance) throws QException;
   
   // Execution order
   int getSortOrder();
   
   // Enable/disable
   boolean isEnabled();
}

// Custom metadata producer
public class CustomMetaDataProducer implements MetaDataProducerInterface
{
   @Override
   public void produce(QInstance instance) throws QException
   {
      // Create and add custom metadata
      CustomMetaData metadata = new CustomMetaData();
      instance.addMetaData(metadata);
   }
   
   @Override
   public int getSortOrder()
   {
      return 100;
   }
}

Validation Plugin Interface

Custom validation rules:

public interface QInstanceValidatorPluginInterface<T extends AbstractMetaData>
{
   // Validate metadata
   void validate(T metadata) throws QException;
   
   // Get metadata type
   Class<T> getMetaDataClass();
}

// Custom validator
public class CustomValidator implements QInstanceValidatorPluginInterface<CustomMetaData>
{
   @Override
   public void validate(CustomMetaData metadata) throws QException
   {
      // Validation logic
      if (metadata.getRequiredField() == null)
      {
         throw new QException("Required field is missing");
      }
   }
}

Error Handling

Exception Hierarchy

QQQ-specific exceptions:

public class QException extends Exception
{
   public QException(String message);
   public QException(String message, Throwable cause);
   public QException(String message, Object... args);
}

public class QValidationException extends QException
{
   public QValidationException(String message);
   public QValidationException(String message, String field);
}

public class QBackendException extends QException
{
   public QBackendException(String message, String backendType);
   public QBackendException(String message, String backendType, Throwable cause);
}

Usage Example:

try
{
   // QQQ operation
   QueryOutput output = queryAction.execute(input);
}
catch (QValidationException e)
{
   // Handle validation errors
   LOG.error("Validation failed", "field", e.getField(), "error", e.getMessage());
}
catch (QBackendException e)
{
   // Handle backend errors
   LOG.error("Backend operation failed", "backend", e.getBackendType(), "error", e.getMessage());
}
catch (QException e)
{
   // Handle general QQQ errors
   LOG.error("QQQ operation failed", "error", e.getMessage());
}

Logging APIs

Structured Logging

QQQ's logging system:

public class QLogger
{
   // Log levels with structured data
   public void debug(String message, Object... keyValuePairs);
   public void info(String message, Object... keyValuePairs);
   public void warn(String message, Object... keyValuePairs);
   public void error(String message, Object... keyValuePairs);
   
   // Exception logging
   public void error(String message, Throwable throwable, Object... keyValuePairs);
}

// Usage example
private static final QLogger LOG = QLogger.getLogger(MyClass.class);

LOG.info("Processing order", "orderId", order.getId(), "customerId", order.getCustomerId());
LOG.error("Failed to process order", exception, "orderId", order.getId());

API Versioning

Version Compatibility

QQQ maintains API compatibility:

  • Major versions: May include breaking changes
  • Minor versions: New features, backward compatible
  • Patch versions: Bug fixes, backward compatible

Migration Support

  • Deprecation warnings: Clear migration paths
  • Migration guides: Step-by-step instructions
  • Examples: Updated code samples
  • Support: Help with migration questions

Best Practices

API Usage

  • Follow patterns: Use established QQQ patterns
  • Handle errors: Always catch and handle QQQ exceptions
  • Manage context: Properly initialize and clean up QContext
  • Use logging: Leverage QQQ's structured logging

API Extension

  • Implement interfaces: Follow QQQ extension contracts
  • Register components: Use QQQ's registration mechanisms
  • Test thoroughly: Ensure extensions work with QQQ core
  • Document changes: Update documentation for new APIs

QQQ's public APIs provide a solid foundation for building applications and extending the framework. Follow established patterns and maintain backward compatibility for the best user experience.

For architecture details, see High-Level Architecture. For versioning policy, see Semantic Versioning Policy.

Clone this wiki locally