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
HTTP Request
Client sends request to Express server
Route Matching
EverShop routes the request based on route.json files in extensions
Middleware Chain
Request passes through middleware functions (named with [middleware] prefix)
Business Logic
Extension handles the request (API endpoint, page component, etc.)
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
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
Directory Purpose Editable 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