Skip to content
Content
Skill

/full-stack-architect

Full-stack extension skeleton and registration pattern. Use this skill when creating an extension that spans both API and Admin — the top-level component with Api.Extension and Admin.Extension entry points, shared domain layer, BuildParam declarations, and package structure.

BOOST
From plugin
webiny-js
8k76 skills3 MCP
Install
$ npx -y skills add webiny/webiny-js --skill full-stack-architect --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/full-stack-architect

Context preview

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

Full-stack extension skeleton and registration pattern. Use this skill when creating an extension that spans both API and Admin — the top-level component with Api.Extension and Admin.Extension entry points, shared domain layer, BuildParam declarations, and package structure.

SKILL.md

full-stack-architect.SKILL.md
name: webiny-full-stack-architect
description: >
  Full-stack extension skeleton and registration pattern. Use this skill when creating
  an extension that spans both API and Admin — the top-level component with Api.Extension
  and Admin.Extension entry points, shared domain layer, BuildParam declarations, and
  package structure. References webiny-api-architect and webiny-admin-architect for layer-specific details.

Full-Stack Extension Skeleton

TL;DR

A full-stack extension bundles **API** and **Admin** into a single package with a shared domain layer. The top-level component registers both sides via `<Api.Extension>` and `<Admin.Extension>`, which point to separate entry-point files. Each side follows its own layered architecture pattern — see **webiny-api-architect** and **webiny-admin-architect** skills for details.

Working Context

This skill applies to both **extension developers** (working in `extensions/`) and **core developers** (working in `packages/`). The architecture patterns are the same — only the import convention and registration mechanism differ.

| | Extensions (`extensions/`) | Core (`packages/`) | | ----------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | **Imports** | `webiny/api`, `webiny/admin`, `webiny/admin/ui` | `@webiny/feature/api`, `@webiny/app-admin`, `@webiny/admin-ui` | | **Catalog paths** | Use the `Import:` path | Use the `Source:` path | | **Registration** | `<Api.Extension src={...}>` / `<Admin.Extension src={...}>` in `webiny.config.tsx` | `createFeature` registered directly by the package's module initializer | | **Entry point export** | Files targeted by Extension `src` MUST use `export default` | No restriction — features are registered programmatically | | **Top-level component** | Required — composes `<Api.Extension>` + `<Admin.Extension>` | Not applicable — each package registers its own features |

Detect which context you're in by checking the file path: `extensions/` → extension mode, `packages/` → core mode.

RULE — Extension Entry Points

> **Admin extensions CANNOT be directly mounted in `webiny.config.tsx` or in any child component tree without going through `<Admin.Extension />`.**

The same rule applies to API extensions — they must go through `<Api.Extension />`.

These entry-point components are the **only** way to register code that runs inside the Admin app or the API runtime. They use the `src` prop to point to a file that will be loaded in the correct execution environment (browser for Admin, Lambda for API). Bypassing these entry points will fail at runtime because the Admin and API contexts (DI containers, routers, GraphQL registries, etc.) are not available outside their respective runtimes.

**YOU MUST include the full file path with the `.ts` or `.tsx` extension in every `src` prop.** For example, use `src={"/extensions/lead/src/index.ts"}`, NOT `src={"/extensions/lead"}`. Omitting the file extension will cause a build failure.

**YOU MUST use `export default` for the `createImplementation()` call** when the file is targeted directly by an Extension `src` prop. Using a named export (`export const Foo = SomeFactory.createImplementation(...)`) will cause a build failure. Named exports are only valid inside files registered via `createFeature`.

// CORRECT — always use entry-point components
<Api.Extension src={import.meta.dirname + "/api/Extension.js"} />
<Admin.Extension src={import.meta.dirname + "/admin/Extension.js"} />

// WRONG — never mount admin/api code directly
<MyAdminComponent />     // Will not have access to Admin DI container
<MyApiFeature />         // Will not have access to API DI container

Package Structure

my-extension/
├── src/
│   ├── index.ts                  # Single public export
│   ├── MyExtension.tsx           # Top-level component (registers Api + Admin)
│   ├── shared/                   # Shared between API and Admin
│   │   ├── constants.ts          # Model IDs, permission names, etc.
│   │   └── types.ts              # Shared types
│   ├── api/                      # API-side code → see webiny-api-architect skill
│   │   ├── Extension.ts
│   │   ├── domain/
│   │   ├── features/
│   │   └── graphql/
│   └── admin/                    # Admin-side code → see webiny-admin-architect skill
│       ├── Extension.tsx
│       ├── features/
│       └── presentation/

Top-Level Component

The top-level component is the single entry point that consumers use. It registers both the API and Admin extensions:

// src/MyExtension.tsx
import React from "react";
import { Api, Admin } from "webiny/extensions";

export const MyExtension = () => {
  return (
    <>
      {/* API extensions — runs in Lambda */}
      <Api.Extension src={import.meta.dirname + "/api/Extension.js"} />

      {/* Admin extensions — runs in browser */}
      <Admin.Extension src={import.meta.dirname + "/admin/Extension.js"} />
    </>
  );
};

Conditional rendering can wrap the entry points (e.g., feature flags, config parameters):

<Infra.Env.Is name={"prod"}>
  <Api.Extension src={import.meta.dirname + "/api/Extension.js"} />
  <Admin.Extension src={import.meta.dirname + "/admin/Extension.js"} />
</Infra.Env.Is>

Shared Domain Layer

The `shared/` directory contains types and value objects used by both API and Admin:

// src/shared/constants.ts
export const MY_MODEL_ID = "myModel";

// src/shared/MyEntity.ts
export interface MyEntityValues {
  name: s
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,049
Stars
682
Forks
Active
Maintenance
TypeScript
Language
23h ago
Last commit
8y ago
Created
3d ago
Added

Repo: webiny/webiny-js

Other skills on webiny-js.