Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Antony-Figueroa/my-evershop-app/llms.txt

Use this file to discover all available pages before exploring further.

Extensions

Extensions are the core building blocks of EverShop’s modular architecture. They encapsulate features, business logic, and functionality in isolated, reusable packages that can be enabled or disabled independently.

What are Extensions?

Extensions in EverShop allow you to:
  • Add new API endpoints
  • Create custom page components
  • React to system events with subscribers
  • Schedule background jobs with cron tasks
  • Extend GraphQL schema with new types and resolvers
  • Organize business logic separately from the core application
Extensions focus on business logic and functionality, while themes focus on visual presentation. This separation allows you to switch themes without losing functionality.

Directory Structure

Each extension lives in the extensions/ directory with the following structure:
extensions/[extension-name]/
├── src/
│   ├── api/              # REST API endpoints
│   ├── pages/            # Page components (React/TSX)
│   ├── subscribers/      # Event subscribers
│   ├── crons/            # Scheduled jobs
│   ├── graphql/          # GraphQL types and resolvers
│   └── bootstrap.ts      # Extension initialization
├── dist/                 # Compiled files (auto-generated)
├── package.json
└── tsconfig.json

Registering Extensions

Extensions must be registered in config/default.json to be loaded:
config/default.json
{
  "system": {
    "extensions": [
      {
        "name": "offlinePayments",
        "resolve": "extensions/offlinePayments",
        "enabled": true
      },
      {
        "name": "productCatalog",
        "resolve": "extensions/productCatalog",
        "enabled": true
      }
    ]
  }
}
Set "enabled": false to disable an extension without removing it from your project.

Creating an Extension

1
Create the directory structure
2
Create a new directory under extensions/ with your extension name:
3
mkdir -p extensions/myExtension/src
4
Add package.json
5
Create a package.json file:
6
{
  "name": "myExtension",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "tsc": "tsc"
  }
}
7
Configure TypeScript
8
Add a tsconfig.json:
9
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}
10
Register the extension
11
Add it to config/default.json:
12
{
  "system": {
    "extensions": [
      {
        "name": "myExtension",
        "resolve": "extensions/myExtension",
        "enabled": true
      }
    ]
  }
}

Extension Components

API Endpoints

Create REST API endpoints in src/api/[endpoint]/:
extensions/sample/src/api/createFoo/[bodyParser]createFoo.ts
import { EvershopRequest, EvershopResponse } from '@evershop/evershop';

export default (request: EvershopRequest, response: EvershopResponse, next) => {
  const { name, description } = request.body;

  if (!name || !description) {
    return response
      .status(400)
      .json({ error: 'Name and description are required' });
  }

  const newFoo = {
    id: Date.now(),
    name,
    description
  };

  console.log('Creating new foo:', newFoo);

  response.status(201).json({
    success: true,
    data: { foo: newFoo }
  });
};
With a corresponding route.json:
extensions/sample/src/api/createFoo/route.json
{
  "methods": ["POST"],
  "path": "/foos",
  "access": "private"
}
The filename [bodyParser]createFoo.ts indicates that bodyParser middleware runs before createFoo. See Routing for details.

Page Components

Add React components in src/pages/[area]/[page]/:
extensions/sample/src/pages/frontStore/homepage/FooList.tsx
import React from 'react';

type FooListProps = {
  foos?: {
    id: number;
    name: string;
    description: string;
  }[];
};

export default function FooList({ foos }: FooListProps) {
  return (
    <div className="foo-list container mx-auto px-4 py-8">
      <h2 className="font-bold text-center mb-8">Foo List</h2>
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        {foos?.map((foo) => (
          <div key={foo.id} className="bg-white rounded-lg shadow-md p-6">
            <h3 className="font-semibold mb-3">{foo.name}</h3>
            <p className="text-gray-600">{foo.description}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

export const layout = {
  areaId: 'content',
  sortOrder: 30
};

export const query = `
  query Query {
    foos {
      id
      name
      description
    }
  }
`;

Event Subscribers

React to system events in src/subscribers/[event]/:
extensions/sample/src/subscribers/product_created/consoleLog.js
export default function consoleLog(data) {
  console.log('Product Created:', data);
}
Subscriber functions are automatically called when the corresponding event is emitted. The directory name (product_created) determines which event to subscribe to.

Cron Jobs

Schedule background tasks in src/crons/:
extensions/sample/src/crons/everyMinute.ts
export default function EveryMinute() {
  console.log('This cron job runs every minute');
}
Register the job in bootstrap.ts:
extensions/sample/src/bootstrap.ts
import path from 'path';
import { registerJob } from '@evershop/evershop/lib/cronjob';

export default function () {
  registerJob({
    name: 'sampleJob',
    schedule: '*/1 * * * *', // Runs every minute
    resolve: path.resolve(import.meta.dirname, 'crons', 'everyMinute.js'),
    enabled: true
  });
}

GraphQL Extensions

Extend the GraphQL schema in src/graphql/types/[TypeName]/:
extensions/sample/src/graphql/types/Foo/Foo.graphql
type Foo {
  id: ID!
  name: String!
  description: String
}

type Query {
  foo(id: ID!): Foo
  foos: [Foo!]!
}
With corresponding resolvers:
extensions/sample/src/graphql/types/Foo/Foo.resolvers.js
const fooList = [
  { id: 1, name: 'Foo', description: 'This is a Foo object' },
  { id: 2, name: 'Bar', description: 'This is a Bar object' },
  { id: 3, name: 'Baz', description: 'This is a Baz object' }
];

export default {
  Query: {
    foo: (root, { id }) => {
      return fooList.find((foo) => foo.id === id);
    },
    foos: () => {
      return fooList;
    }
  },
  Foo: {
    id: (foo) => foo.id,
    name: (foo) => foo.name,
    description: (foo) => foo.description
  }
};

Real-World Examples

offlinePayments Extension

Provides payment methods like Cash on Delivery and Bank Transfer:
extensions/offlinePayments/src/pages/frontStore/checkout/cod/CashOnDelivery.tsx
import React from 'react';
import { useCheckout } from '@evershop/evershop';

export default function CashOnDeliveryMethod({ setting }) {
  const checkout = useCheckout();
  const { paymentMethods, setPaymentMethods } = checkout;
  const selectedPaymentMethod = paymentMethods?.find(pm => pm.code === 'cod');

  return (
    <div className="bg-[#F8FAF9] border border-[#E8F5E9] rounded-lg p-4 mb-4">
      {/* Payment method selection UI */}
    </div>
  );
}

export const layout = {
  areaId: 'checkoutPaymentMethod',
  sortOrder: 10
};

productCatalog Extension

Extends the Product type with supplement-specific fields:
extensions/productCatalog/src/graphql/types/ProductExtension/ProductExtension.graphql
type Supplement {
  ingredients: String
  benefits: [String]
  presentation: String
  dosage: String
  warnings: String
  storage: String
}

type ProductExtension {
  supplement: Supplement
}

extend type Product {
  extension: ProductExtension
}

Best Practices

Always use .ts and .tsx files for new code. Define proper interfaces for your props and data structures.
  • Extension names: camelCase (e.g., offlinePayments, productCatalog)
  • API endpoints: descriptive names (e.g., createOrder, updateProduct)
  • Components: PascalCase (e.g., ProductReviews, CashOnDelivery)
Each extension should have a single, well-defined purpose. If you’re adding multiple unrelated features, consider splitting them into separate extensions.
All custom code should live in extensions. Never edit files in .evershop/ or node_modules/ as they will be overwritten.

Next Steps

  • Learn about Themes for customizing the visual presentation
  • Understand Routing to configure API endpoints and pages
  • Explore the GraphQL API for data fetching

Build docs developers (and LLMs) love