webiny-dependency-injection

The universal createImplementation DI pattern and all injectable services. Use this skill when the developer is writing any Webiny extension and needs to understand dependency injection, constructor injection, how to access Logger/BuildParams/IdentityContext, how to inject CMS use-cases (list/get/create/update/delete entries), or how the dependencies array works. This is the connective tissue across all extension types -- API, Admin, CLI, and Infrastructure.

7,955 stars

Best use case

webiny-dependency-injection is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

The universal createImplementation DI pattern and all injectable services. Use this skill when the developer is writing any Webiny extension and needs to understand dependency injection, constructor injection, how to access Logger/BuildParams/IdentityContext, how to inject CMS use-cases (list/get/create/update/delete entries), or how the dependencies array works. This is the connective tissue across all extension types -- API, Admin, CLI, and Infrastructure.

Teams using webiny-dependency-injection should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/dependency-injection/SKILL.md --create-dirs "https://raw.githubusercontent.com/webiny/webiny-js/main/skills/user-skills/dependency-injection/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/dependency-injection/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How webiny-dependency-injection Compares

Feature / Agentwebiny-dependency-injectionStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

The universal createImplementation DI pattern and all injectable services. Use this skill when the developer is writing any Webiny extension and needs to understand dependency injection, constructor injection, how to access Logger/BuildParams/IdentityContext, how to inject CMS use-cases (list/get/create/update/delete entries), or how the dependencies array works. This is the connective tissue across all extension types -- API, Admin, CLI, and Infrastructure.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Dependency Injection Patterns

## TL;DR

Every Webiny extension type uses the same DI pattern: define a class implementing `*.Interface`, declare dependencies in the constructor, and export via `*.createImplementation({ implementation, dependencies })`. The DI container automatically provides the required services, ensures type safety, and validates at compile time. This pattern is the connective tissue across all extension types -- API, Admin, CLI, and Infrastructure.

## The Universal Pattern

```typescript
import { SomeFactory } from "webiny/some/path";
import { Logger } from "webiny/api/logger";
import { BuildParams } from "webiny/api/build-params";

class MyImplementation implements SomeFactory.Interface {
  constructor(
    private logger: Logger.Interface,
    private buildParams: BuildParams.Interface
  ) {}

  execute(/* factory-specific params */) {
    this.logger.info("Doing something...");
    // buildParams.get() returns T | null — always account for null.
    const value = this.buildParams.get<string>("MY_PARAM");
  }
}

export default SomeFactory.createImplementation({
  implementation: MyImplementation,
  dependencies: [Logger, BuildParams]
});
```

Key rules:

1. **One class per file** -- each extension file exports a single implementation.
2. **Constructor injection** -- dependencies are received as constructor parameters, in the same order as the `dependencies` array.
3. **Dependencies array** -- must exactly match the constructor parameter order and types.
4. **Interface types** -- always type constructor params as `Feature.Interface`.

## Where This Pattern Appears

| Extension Type  | Factory                | Import Path              |
| --------------- | ---------------------- | ------------------------ |
| Content Models  | `ModelFactory`         | `"webiny/api/cms/model"` |
| GraphQL Schemas | `GraphQLSchemaFactory` | `"webiny/api/graphql"`   |
| API Keys        | `ApiKeyFactory`        | `"webiny/api/security"`  |
| CLI Commands    | `CliCommandFactory`    | `"webiny/cli/command"`   |
| Pulumi Handlers | `CorePulumi`           | `"webiny/infra/core"`    |

> **Event handlers** use the same `createImplementation` pattern but are not injectable dependencies.

## Examples Across Extension Types

### API Extension (GraphQL Schema with DI)

GraphQL schemas use the **builder pattern**. The `execute` method receives a `builder` and uses `addTypeDefs` and `addResolver` to define the schema. Resolver-level DI is declared per-resolver via `dependencies` in `addResolver`, resolved at request time from the request-scoped container.

```typescript
import { GraphQLSchemaFactory } from "webiny/api/graphql";
import { IdentityContext } from "webiny/api/security";

class WhoAmISchema implements GraphQLSchemaFactory.Interface {
  async execute(
    builder: GraphQLSchemaFactory.SchemaBuilder
  ): Promise<GraphQLSchemaFactory.SchemaBuilder> {
    builder.addTypeDefs(/* GraphQL */ `
      extend type Query {
        whoAmI: String
      }
    `);

    builder.addResolver({
      path: "Query.whoAmI",
      dependencies: [IdentityContext],
      resolver: (identityContext: IdentityContext.Interface) => {
        return () => {
          const identity = identityContext.getIdentity();
          return `Hello, ${identity.displayName}!`;
        };
      }
    });

    return builder;
  }
}

export default GraphQLSchemaFactory.createImplementation({
  implementation: WhoAmISchema,
  dependencies: []
});
```

Note: `GraphQLSchemaFactory` implementations typically have `dependencies: []` because DI happens at the resolver level via `addResolver({ dependencies })`, not at the class constructor level.

### CLI Command with DI

```typescript
import { Ui } from "webiny/cli";
import { CliCommandFactory } from "webiny/cli/command";

class MyCommandImpl implements CliCommandFactory.Interface<{ name: string }> {
  constructor(private ui: Ui.Interface) {}

  execute(): CliCommandFactory.CommandDefinition<{ name: string }> {
    return {
      name: "greet",
      description: "Greet someone",
      params: [{ name: "name", description: "Name", type: "string" }],
      handler: async params => {
        this.ui.success(`Hello, ${params.name}!`);
      }
    };
  }
}

export default CliCommandFactory.createImplementation({
  implementation: MyCommandImpl,
  dependencies: [Ui]
});
```

### Pulumi Handler with DI

```typescript
import { Ui } from "webiny/infra";
import { CorePulumi } from "webiny/infra/core";

class MyPulumiImpl implements CorePulumi.Interface {
  constructor(private ui: Ui.Interface) {}

  execute(app: any) {
    this.ui.info("Deploying with environment:", app.env);
  }
}

export default CorePulumi.createImplementation({
  implementation: MyPulumiImpl,
  dependencies: [Ui]
});
```

## Key Rules

1. Always import from the **feature path**, not the package root.
2. Use `Feature.Interface` for constructor parameter types.
3. The `dependencies` array order must match the constructor parameter order.
4. Read the `abstractions.ts` file in the feature folder to see available methods.
5. Extensions with no dependencies use `dependencies: []`.
6. `BuildParams.get<T>(name)` returns `T | null` — always type the receiving property/variable as nullable (e.g. `string | null`) and handle the `null` case.
7. **BuildParam declarations belong inside the extension's `Extension.tsx`**, not in `webiny.config.tsx`. Expose required params as React props on the extension component so the consumer decides where values come from (see `webiny-full-stack-architect` skill for the full pattern).

## Related Skills

- `webiny-custom-graphql-api` -- DI in GraphQL schema extensions
- `webiny-cli-extensions` -- DI in CLI command extensions
- `webiny-full-stack-architect` -- Full-stack extension skeleton and registration pattern
- `webiny-api-architect` -- API-side architecture using DI
- `webiny-admin-architect` -- Admin-side architecture using DI

Related Skills

webiny-v5-to-v6-migration

7955
from webiny/webiny-js

Migration patterns for converting v5 Webiny code to v6 architecture. Use this skill when migrating existing v5 plugins to v6 features, converting context plugins to DI services, adapting v5 event subscriptions to v6 EventHandlers, or understanding how v5 patterns translate to v6. Targeted at AI agents performing migrations.

webiny-api-permissions

7955
from webiny/webiny-js

Schema-based permission system for API features. Use this skill when implementing authorization in use cases, defining permission schemas with createPermissionSchema, creating injectable permissions via createPermissionsAbstraction/createPermissionsFeature, checking read/write/delete/publish permissions, handling own-record scoping, or testing permission scenarios. Covers the full pattern from schema definition to use case integration to test matrices.

webiny-admin-permissions

7955
from webiny/webiny-js

Admin-side permission UI registration and DI-backed permission checking. Use this skill when adding permission controls to the admin UI — schema-based auto-generated forms, injectable permissions via createPermissionsAbstraction/ createPermissionsFeature, typed hooks (createUsePermissions), the HasPermission component (createHasPermission), and the Security.Permissions component props. Covers both simple apps and complex multi-entity permission schemas.

webiny-sdk

7955
from webiny/webiny-js

Using @webiny/sdk to read and write CMS data from external applications. Use this skill when the developer is building a Next.js, Vue, Node.js, or any external app that needs to fetch or write content to Webiny, set up the SDK, use the Result pattern, list/get/create/update/publish entries, filter and sort queries, use TypeScript generics for type safety, work with the File Manager, or create API keys programmatically. Covers read vs preview mode, the `values` wrapper requirement, correct method names, and the `fields` required parameter.

webiny-project-structure

7955
from webiny/webiny-js

Webiny project layout, webiny.config.tsx anatomy, and extension registration. Use this skill when the developer asks about folder structure, where custom code goes, how to register extensions, what webiny.config.tsx does, or how the project is organized. Also use when they need to understand the relationship between extensions/, webiny.config.tsx, and the different extension types (Api, Admin, Infra, CLI).

webiny-local-development

7955
from webiny/webiny-js

Deploying, developing locally, managing environments, and debugging Webiny projects. Use this skill when the developer asks about deployment commands (deploy, destroy, info), local development with watch mode (API or Admin), the Local Lambda Development system, environment management (long-lived vs short-lived, production vs dev modes), build parameters, state files, debugging API/Admin/Infrastructure errors, or the redeploy-after-watch requirement.

webiny-infrastructure-extensions

7955
from webiny/webiny-js

Modifying AWS infrastructure using Pulumi handlers and declarative Infra components. Use this skill when the developer wants to customize AWS infrastructure, add Pulumi handlers, configure OpenSearch, VPC, resource tags, regions, custom domains, blue-green deployments, environment-conditional config, or manage production vs development infrastructure modes. Covers CorePulumi.Interface, all <Infra.*> declarative components, and <Infra.Env.Is>.

webiny-infra-catalog

7955
from webiny/webiny-js

Infrastructure — 33 abstractions. Infrastructure extensions.

webiny-extensions-catalog

7955
from webiny/webiny-js

extensions — 5 abstractions.

webiny-cli-command-catalog

7955
from webiny/webiny-js

cli/command — 1 abstractions.

webiny-cli-catalog

7955
from webiny/webiny-js

cli — 2 abstractions.

webiny-api-tenant-manager-catalog

7955
from webiny/webiny-js

API — Tenant Manager — 2 abstractions. Tenant management event handlers and use cases.