Skip to content
Content
Skill

/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

BOOST
From plugin
webiny-js
8k76 skills3 MCP
Install
$ npx -y skills add webiny/webiny-js --skill dependency-injection --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/dependency-injection

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

SKILL.md

dependency-injection.SKILL.md
name: webiny-dependency-injection
description: >
  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.

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.

Working Context

This skill applies to both **extension developers** (working in `extensions/`) and **core developers** (working in `packages/`). The DI pattern is universal — only the import paths differ.

| | Extensions (`extensions/`) | Core (`packages/`) | | ----------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | **`createAbstraction`** | `from "webiny/api"` or `from "webiny/admin"` | `from "@webiny/feature/api"` or `from "@webiny/feature/admin"` | | **`createFeature`** | `from "webiny/api"` or `from "webiny/admin"` | `from "@webiny/feature/api"` or `from "@webiny/feature/admin"` | | **Factory imports** | `from "webiny/api/graphql"`, `from "webiny/api/cms/model"`, etc. | Direct package paths: `from "@webiny/handler-graphql/..."`, `from "@webiny/api-headless-cms/..."` | | **Catalog paths** | Use the `Import:` path | Use the `Source:` path |

Detect which context you're in by checking the file path: `extensions/` → extension mode, `packages/` → core mode. The `dependencies` array, constructor injection, scoping rules, and all other patterns are identical in both contexts.

The Universal Pattern

import { SomeFactory } from "webiny/some/path";
import { Logger, BuildParams } from "webiny/api";

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.

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

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

class MyCommandImpl implements CliCommandFactory.Interface<{ name: string }> {
  constructor(private
Read more
Ships withwebiny-js

Open-source content platform. Self-hosted on AWS serverless. Built as a TypeScript framework you extend with code, not a closed product you configure through a UI. Runs on Lambda, DynamoDB, S3, and CloudFront inside your own AWS account. Scales automatically.

Get the whole plugin
Stats
8,048
Stars
682
Forks
Active
Maintenance
TypeScript
Language
2h ago
Last commit
8y ago
Created
9h ago
Added

Repo: webiny/webiny-js

Other skills on webiny-js.