Amplication plugins offer powerful lifecycle functions that allow you to customize and extend the code generation process precisely where you need it. Every event within Amplication’s plugin system exposes these lifecycle functions, giving you consistent control throughout the resource generation process.

Event Structure

Each event in Amplication’s plugin system follows a predictable structure, offering both before and after hooks:

export interface PluginEventType<T extends EventParams> {
  before?: PluginBeforeEvent<T>;
  after?: PluginAfterEvent<T>;
}
  • before: This function executes before Amplication emits the main event logic. Use it to modify event parameters and tailor the default behavior before it happens.
  • after: This function executes after Amplication’s core event logic. It provides access to the generated modules or files, allowing you to apply transformations, add custom logic, or restructure outputs.

Each event defines its specific EventParams interface (T extends EventParams), providing you with type-safe access to the relevant data for that particular stage of the generation process.

export interface EventParams {}

Function Signatures

Let’s examine the signatures of the before and after functions to understand how you can interact with Amplication’s code generation:

export type PluginBeforeEvent<T extends EventParams> = (
  dsgContext: DsgContext,
  eventParams: T
) => Promisable<T>;

export type PluginAfterEvent<T extends EventParams> = (
  dsgContext: DsgContext,
  eventParams: T,
  modules: ModuleMap
) => Promisable<ModuleMap>;

Parameters:

dsgContext
DsgContext

The Design Context provides access to shared information and utilities across all events within a plugin execution. Use it to retrieve configuration, access the service schema, and more.

eventParams
T extends EventParams

These are the parameters specific to the event being handled. The type T is defined by the specific event interface, giving you access to relevant data points to influence the generation.

modules
ModuleMap

A ModuleMap is a structure containing the generated code modules as key-value pairs, where keys are module paths and values are the code content. This is your entry point to modify the generated code in Node.js plugins.

Node.js plugins, the after function works with a ModuleMap, representing code as modules. .NET plugins, the after function uses a FileMap, which is designed for file-based manipulations.

Return Values:

before function
Promisable<T>

The before function should return a Promise that resolves to the (potentially modified) eventParams. Returning the modified eventParams allows you to influence the subsequent default behavior of the event.

after function
Promisable<ModuleMap>

The after function should return a Promise resolving to the (potentially modified) ModuleMap. Returning the modified ModuleMap ensures your changes are incorporated into the final generated output.

The Context Object

Both before and after lifecycle functions have access to a global context object that provides essential data and utilities. This context is shared between events and contains crucial information about your application.

Here are the key properties available in the context object:

NameTypeDescription
resourceTypekeyOf typeOf EnumResourceTypeThe type of resource: MessageBroker, ProjectConfiguration, Service
resourceInfoAppInfoGeneral application data including name, description, version, ID, URL, and settings
entitiesEntity[]List of entities in the service
rolesRolesList of roles in the service
pluginInstallationsPluginInstallationsList of installed plugins
topicsTopic[]List of topics connected to the service
modulesModuleMapMap of generated modules (files)
DTOsDTOsList of generated DTOs
pluginsPluginMapMap of event names with their before/after functions and event parameters
loggerBuildLoggerLogger for creating user-facing build logs
utilsContextUtilUtility functions including skipDefaultBehavior and importStaticModules
clientDirectoriesClientDirectoriesPaths for admin UI generation (base, src, auth, public, api directories)
serverDirectoriesServerDirectoriesPaths for server generation (base, src, auth, scripts, messageBroker directories)

For detailed information about these types and their definitions, check out the code-gen-types.ts file in the Amplication GitHub repository.

Skipping Default Behavior

The before function provides a powerful capability to skip Amplication’s default behavior through the context object. This allows you to completely override the default functionality with your own implementation:

// In your before function
context.utils.skipDefaultBehavior = true;

By default, skipDefaultBehavior is set to false. When set to true, Amplication will skip its default implementation, and you must provide alternative logic either in the before function or in the corresponding after function.

Use skipDefaultBehavior with caution. Skipping default behavior without providing proper alternative functionality can lead to unexpected results, especially if other generated files depend on the skipped output.

Practical Use: Modifying Generated Files in after Functions

A common use case for after functions is to restructure the generated files. Imagine you need to enforce a specific folder structure for all your organization’s services. The after function is the perfect place to achieve this.

For example, you might want to:

  • Move all generated GraphQL resolvers into a dedicated resolvers directory.
  • Organize entity-related files into entity-specific subfolders.
  • Add custom documentation or README files to specific directories.

The after function gives you the flexibility to manipulate the generated ModuleMap or FileMap and reorganize files as needed to meet your project’s architectural requirements.

Explore Real-World Examples

The best way to understand the power of before and after functions is to see them in action.

We encourage you to dive into the code of Amplication’s official plugins. These plugins are built using the same plugin framework and provide numerous practical examples of how to implement before and after lifecycle functions for various customization tasks.

By exploring these examples, you can gain insights into:

  • Modifying code templates to inject custom logic.
  • Adding new files and modules to the generated output.
  • Integrating with external services and APIs during generation.
  • Enforcing coding standards and best practices through automated modifications.

Browse our complete collection of official plugins in the Amplication Plugins Repository for more examples and inspiration.

Best Practices and Important Considerations

To ensure your plugins are robust and maintainable, keep these best practices in mind when working with before and after functions: