Skip to main content

Introduction

EverShop is built on a modular, extension-based architecture that separates business logic from presentation. This design allows you to extend functionality without modifying core files, ensuring upgradability and maintainability.

Core Technology Stack

Backend

  • Runtime: Node.js
  • Framework: Express.js
  • Database: PostgreSQL
  • API: REST + GraphQL

Frontend

  • Library: React 19
  • Styling: Tailwind CSS
  • Rendering: SSR (Server-Side Rendering)
  • Build: Webpack + Parcel

Architectural Principles

1. Modular Extension System

EverShop uses a plugin architecture where all custom functionality is encapsulated in extensions:
// config/default.json
{
  "system": {
    "extensions": [
      {
        "name": "productCatalog",
        "resolve": "extensions/productCatalog",
        "enabled": true
      },
      {
        "name": "offlinePayments",
        "resolve": "extensions/offlinePayments",
        "enabled": true
      }
    ]
  }
}
Extensions are isolated modules that can provide APIs, GraphQL types, page components, event subscribers, and scheduled jobs.

2. Theme System

Themes handle visual presentation only and are separate from business logic:
// config/default.json
{
  "system": {
    "theme": "anasuplements"
  },
  "shop": {
    "language": "es",
    "currency": "USD"
  }
}
Never put business logic in themes. Use extensions for APIs, database operations, and business rules.

3. Component-Based Pages

Pages are assembled from React components using an area-based layout system:
// Example: themes/anasuplements/src/pages/all/Header.tsx
export default function Header({ storeName = "Ana's Suplements", categories = [] }) {
  return (
    <header className="bg-[#F8FAF9] border-b border-[#E8F5E9]">
      <div className="container mx-auto px-4 py-4">
        <a href="/" className="text-2xl font-bold text-[#2D5A3D]">
          {storeName}
        </a>
      </div>
    </header>
  );
}

export const layout = {
  areaId: 'header',
  sortOrder: 1
};
The layout export determines where (areaId) and when (sortOrder) the component renders.

Request Flow

1

HTTP Request

Client sends request to Express server
2

Route Matching

EverShop routes the request based on route.json files in extensions
3

Middleware Chain

Request passes through middleware functions (named with [middleware] prefix)
4

Business Logic

Extension handles the request (API endpoint, page component, etc.)
5

Response

  • API: Returns JSON response
  • Page: Renders React components server-side with GraphQL data

API Request Example

// extensions/sample/src/api/createFoo/route.json
{
  "methods": ["POST"],
  "path": "/foos",
  "access": "private"
}

// 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 };
  
  response.status(201).json({
    success: true,
    data: { foo: newFoo }
  });
};
Middleware functions are executed in alphabetical order based on their [middleware] prefix.

Page Request Example

// extensions/sample/src/pages/frontStore/homepage/FooList.tsx
export default function FooList({ foos }: FooListProps) {
  return (
    <div className="foo-list container mx-auto px-4 py-8">
      {foos?.map((foo) => (
        <div key={foo.id} className="bg-white rounded-lg shadow-md p-6">
          <h3>{foo.name}</h3>
          <p>{foo.description}</p>
        </div>
      ))}
    </div>
  );
}

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

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

Event-Driven Architecture

EverShop includes an event system for decoupled functionality:
// extensions/sample/src/subscribers/product_created/productCreatedHandler.ts
export default async function productCreatedHandler(data) {
  console.log('Product created:', data);
  // Send email, update cache, log analytics, etc.
}
Subscribers respond to events like product_created, order_placed, customer_registered, etc.

Scheduled Jobs (Cron)

Extensions can register scheduled tasks:
// extensions/sample/src/bootstrap.ts
import { registerJob } from '@evershop/evershop/lib/cronjob';

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

// extensions/sample/src/crons/everyMinute.ts
export default function EveryMinute() {
  console.log('This cron job runs every minute');
}

GraphQL Integration

EverShop uses GraphQL for data fetching in page components:
# extensions/sample/src/graphql/types/Foo/Foo.graphql
type Foo {
  id: ID!
  name: String!
  description: String
}

type Query {
  foo(id: ID!): Foo
  foos: [Foo!]!
}
Components can query this data using the query export:
export const query = `
  query Query {
    foos {
      id
      name
      description
    }
  }
`;

Build Process

npm run dev
  • Hot-reload enabled
  • TypeScript compilation on-the-fly
  • File watching for extensions and themes
  • Source maps enabled
npm run build
npm run start
  • Compiles all TypeScript to JavaScript
  • Bundles frontend assets
  • Optimizes for performance
  • Output goes to .evershop/ and dist/ directories
Never edit files in .evershop/ or dist/ directories. These are generated during build and will be overwritten.

Key Directories

DirectoryPurposeEditable
config/Configuration files✅ Yes
extensions/Custom business logic✅ Yes
themes/Visual components✅ Yes
public/Static assets✅ Yes
translations/i18n files✅ Yes
.evershop/Build output❌ No
node_modules/Dependencies❌ No

Next Steps

Project Structure

Explore the directory layout and file organization

Configuration System

Learn how to configure EverShop using JSON files