The Amplication Plugin System
Tags: news, opensource, node, programming
Published: 2023-05-02 14:00:58

---
title: The Amplication Plugin System
description: Discover how Amplication's new plugin system adds flexibility to generated applications by allowing custom features and behaviors in just a few simple steps.
published: true
tags: news,opensource,node,programming
canonical_url: https://amplication.com/blog/the-amplication-plugin-system
cover_image: https://static-assets.amplication.com/blog/the-amplication-plugin-system/hero.png
---

The [plugin system](https://docs.amplication.com/getting-started/plugins/) is one of the many exciting features released in version 1.0.0 of [Amplication](https://amplication.com/). As an open-source company, we're driven by the developer community's needs. The idea to develop a plugin system came from a specific call from developers: to have more flexibility in the generated application.

Some of the most debated topics among software developers today are best practices, principles (like SOLID, DRY, and YAGNI), and whether to use opinionated frameworks and libraries. Those familiar with our generated application software have probably noticed it is very opinionated and follows the NestJS best practices. However, we want our platform to be the answer for everyone—and we want to stay responsive to the developer community. So we sat down to consider best practices for enabling the extension and manipulation of Amplication-generated applications to make them less opinionated and more flexible. The result? Amplication's new, community-driven solution for plugin system architectures.

Let's dive into what plugins are, explore how Amplication handles them, showcase how we build them, and explore some of the considerations required to create an effective, error-free plugin.

## Plugins in Amplication

Plugins are software additions that allow the customization and enhancement of computer programs, applications, and web browsers. [Amplication's plugin system](https://docs.amplication.com/plugins/overview/) allows us to add new features or manipulate our generated application's behaviors. Our Data Service Generator (DSG) gathers all the data that the user configures in the UI or CLI, including entities and their fields, roles, permissions, and services settings. The DSG then uses these values as inputs for its functions; each function is responsible for code generation in a specific area, such as `CreateEntityController`, `CreateEnitityResolver`, `CreateServerDockerCompose`, and more.

To build this framework for plugins, we've exposed each function to plugin developers, providing all the data and context known at that point and allowing the system to manipulate the data and impact the event's results.

### Before and After Functions

All events share the same interface, which contains two functions: before and after. These functions are responsible for different steps in an event's lifecycle. Here's how they look:

```TypeScript
export interface PluginEventType<T extends EventParams> {
  before?: PluginBeforeEvent<T>;
  after?: PluginAfterEvent<T>;
}
```

For each event, the type of `EventParams` is different, providing access to relevant data for the specific event.

```TypeScript
export interface EventParams {}

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

export type PluginAfterEvent<T extends EventParams> = (
  dsgContext: DsgContext,
  eventParams: T,
  modules: Module[]
) => Promisable<Module[]>;
```

In the before and after functions, developers can access the context and the event parameters. We use the context to gather data shared between events. The event parameters are used to manipulate the default behavior by passing values that differ from the default.

The after function also gives us access to the generated modules. This parameter can be helpful when a developer wants to restructure the generated modules into a different folder structure.

Amplication will continue generating code as normal when no lifecycles event is provided and no parameters are changed.

### Utility Functions: `skipDefaultBehavior`

In addition to the specific parameters required for each event, we also provide a global context object that can help the developer access other data they need, such as the list of entities, roles, other services in the project, and all files generated thus far. It also has a `util` property consisting of utility functions. One of these utility functions is `skipDefaultBehavior`.

```TypeScript
context.utils.skipDefaultBehavior = true;
```

The default behavior won't execute when the `skipDefaultBehavior` is set to `true` in the before function, so developers must provide their logic. Developers can do so after the skip in the before or after functions.

It's important to remember that using skipDefaultBehavior without providing an alternative functionality can cause unexpected behavior—for example, the skipped event could generate files imported by other files, so it must be used wisely and carefully. As Uncle Ben said, "With great power comes great responsibility."

### Events Hierarchy

It's essential to consider the execution order of the events in the DSG (Figure 1.) There are two main events in the DSG service: one for the server generation and the other for the admin-ui creation.

These main events include other sub-events to create the server and admin-ui's files. (In Amplication's source code, these files are sometimes called modules.) The internal events within the server and admin-ui events are synchronous, so the execution order of plugin code must be accounted for when developing them.

<figure>
  <img src="https://static-assets.amplication.com/blog/the-amplication-plugin-system/1.png"
    alt="Execution order of the events in the DSG"
    width="2000" height="1675"
    loading="lazy"
    style="margin-left: auto; margin-right: auto;">
  <figcaption style="margin-left: auto; margin-right: auto; width: fit-content;">Figure 1: <a href="https://docs.amplication.com/plugins/event-hierarchy/" target="_blank">Execution order of the events in the DSG</a></figcaption>
</figure>
<br />

Let's break down this image into three component types:

- A single DSG event with a plugin wrapper is indicated by a rectangle with no forward arrows. The title represents the corresponding event name.
- "Create message broker" is a function with a plugin wrapper that executes other events of the message broker creation. As in the previous category, the title corresponds to the event name.
- Resources creation (in a gray rectangle "for each entity") is a function that executes the events of the resources' creation, such as services, controllers, and resolvers.

## A Quick Breather

Let's take a breather before we go to how to develop a plugin. Deep breath in, 1... 2... 3... 4... 5..., deep breath out, 1... 2... 3... 4... 5...

<img src="https://static-assets.amplication.com/blog/the-amplication-plugin-system/cta.gif"
  alt="A breathing exercise"
  width="287" height="311"
  loading="lazy"
  style="margin-left: auto; margin-right: auto;">

If you're interested in the Amplication Plugin System and would like to see more of what the platform offers. In that case, we invite you to check out the [Amplication GitHub repository](https://github.com/amplication/amplication).

We've built our platform with open-source technologies, and your support is crucial to our success. If you find our platform helpful, we would greatly appreciate it if you could take a moment to 🌟 us on GitHub. Your support helps us to continue developing and improving the platform for everyone. Additionally, if you want to contribute to the project, we welcome pull requests and issues on our GitHub repository. We appreciate your support!

## Developing a Plugin

As we've seen, plugins are complex beasts with interdependent parts that must be kept in mind during their development process, including wrappers, before and after lifecycle events, event parameters, context, `skipDefaultBehavior`, and event hierarchy. Understanding the importance of each part can help us develop an effective and error-free plugin.

### Amplication's Development Process

In this last section, we want to share the plugin development process used by our team at Amplication. Before development begins, we take the following steps:

1. Generate a service with Amplication.
2. Apply the changes required manually on the resulting source code that Amplication generated:
   - Add any missing functionality.
   - Manipulate the existing functionality.
3. Create a PR to see the changes, and use the result to design the plugin.
   - Determine which events are needed. For example, if we use a new npm package, we must update the package.json file.
   - Decide how to use events with the before and after lifecycle functions. We know that `CreateServerPackageJson` is one of the first events that run, so we must capture that event in the before function and add the new npm package.
   - Take into consideration exception errors caused by the plugin's functionality or limitations.

### Plugin Development Workflow in Action

To illustrate this development workflow, let's look at the [MySQL](https://github.com/amplication/plugins/tree/master/plugins/db-mysql) plugin. But first, let's review the functionality of a database connection:

1. We already have the functionality of a database connection using Prisma as an ORM.
2. Prisma supports MySQL as a provider; change the provider on the Prisma schema accordingly.
3. We use environment variables for the Prisma schema database's URL and the DockerCompose values; we change the environment variables and their values where needed.

Now, let's translate the above steps to events:

<figure>
  <img src="https://static-assets.amplication.com/blog/the-amplication-plugin-system/2.png"
    alt="The register method for Events"
    width="441" height="399"
    loading="lazy"
    style="margin-left: auto; margin-right: auto;">
  <figcaption style="margin-left: auto; margin-right: auto; width: fit-content;">Figure 2: The <code>register</code> method for <code>Events</code></figcaption>
</figure>
<br />

During Step 3 of the development workflow, we encountered an unexpected behavior: Prisma's MySQL provider does not support lists of primitive types. In the following function (`beforeCreateServer`), we will account for this limitation in Prisma by throwing an error when one of the entities' fields type is `MultiSelectOptionSet`. Here is an example of a use case where the plugin should throw an error:

<figure>
  <img src="https://static-assets.amplication.com/blog/the-amplication-plugin-system/3.png"
    alt="The beforeCreateServer event"
    width="1077" height="387"
    loading="lazy"
    style="margin-left: auto; margin-right: auto;">
  <figcaption style="margin-left: auto; margin-right: auto; width: fit-content;">Figure 3: The <code>beforeCreateServer</code> event</figcaption>
</figure>
<br />

#### `CreateServerDotEnv`: `before`

On this event, we send our [event parameters](https://docs.amplication.com/plugins/plugin-events/create-server-dot-env/#event-params), specifically the environment variables for the MySQL database. As a result, the .env file will be generated with the default variables it already holds and our environment variables.

#### `CreateServerDockerCompose`: `before`

We also send our [event params](https://docs.amplication.com/plugins/plugin-events/create-server-docker-compose/#event-params) on this event, this time the YAML properties and values for the MySQL database. As a result, the docker-compse.yml file is generated so that the MySQL properties will replace the PostgreSQL properties.

#### `CreateServerDockerComposeDB`: `before`

This event is responsible for generating the docker-compose.db.yml file, which contains the docker image of the PostgreSQL database. Here we have an excellent example of `skipDefaultBehavior` usage. We can skip this file generation and provide different functionality—in this case, a separate file—later, in the after function.

<figure>
  <img src="https://static-assets.amplication.com/blog/the-amplication-plugin-system/4.png"
    alt="The beforeCreateServerDockerComposeDB event"
    width="375" height="146"
    loading="lazy"
    style="margin-left: auto; margin-right: auto;">
  <figcaption style="margin-left: auto; margin-right: auto; width: fit-content;">Figure 4: The <code>beforeCreateServerDockerComposeDB</code> event</figcaption>
</figure>
<br />

#### `CreateServerDockerComposeDB`: `after`

After skipping the default behavior in the before function, we provide our docker-compose.db.yml file on this event.

<figure>
  <img src="https://static-assets.amplication.com/blog/the-amplication-plugin-system/5.png"
    alt="The afterCreateServerDockerComposeDB event"
    width="505" height="182"
    loading="lazy"
    style="margin-left: auto; margin-right: auto;">
  <figcaption style="margin-left: auto; margin-right: auto; width: fit-content;">Figure 5: The <code>afterCreateServerDockerComposeDB</code> event</figcaption>
</figure>
<br />

#### `CreatePrismaSchema`: `before`

This event is responsible for manipulating the following part of the Prisma schema:

<figure>
  <img src="https://static-assets.amplication.com/blog/the-amplication-plugin-system/6.png"
    alt="The dataSource object"
    width="310" height="137"
    loading="lazy"
    style="margin-left: auto; margin-right: auto;">
  <figcaption style="margin-left: auto; margin-right: auto; width: fit-content;">Figure 6: The <code>dataSource</code> object</figcaption>
</figure>
<br />

##### EventParams

We use the [event params](https://docs.amplication.com/plugins/plugin-events/create-prisma-schema/#event-params) to:

- Change the data source name from Postgres to MySQL
- Change the provider from Postgresql to MySQL

(`CreateServerDotEnv` handles the `DB_URL`)

## Summary & Conclusion

Amplication's Plugin System allows developers to extend or alter the code generation process to include support for additional features or to make any changes to the default "flavor" of the generated application. It is an excellent way for our developer community to extend our platform safely and for our users to adjust the generated code to their specific needs. When writing these lines, over a dozen plugins are already available for Amplication, and our community and users are currently developing others.

In this blog, we reviewed what Plugins are and their architecture and gave a real-world example of creating a new Plugin.

If you liked the idea behind our Plugins and want to try writing one, please join our [Discord community](https://discord.gg/amplication-757179260417867879), where you can get help from fellow community members and our core team. We are friendly, and we like to help the community generate more useful plugins. If you develop a Plugin and we publish it, we will make sure to send you cool Swag 🙂.