RESTful API Routes
EverShop provides a comprehensive system for building RESTful API endpoints that power your store's backend functionality. This document explains how to create, structure, and secure these endpoints.
Before diving into API routes, it's recommended to familiarize yourself with the extension structure to understand how extensions are organized in EverShop.
API Organization
EverShop's RESTful APIs are located in the api folder of each module. Below is an example of the API structure in the catalog module:
catalog
├── api
│ ├── createProduct
│ │ ├── ... // Middleware functions
│ │ └── route.json
│ ├── updateProduct
│ │ ├── ... // Middleware functions
│ │ └── route.json
│ ├── createCategory
│ │ ├── ... // Middleware functions
│ │ └── route.json
│ ├── updateCategory
│ │ ├── ... // Middleware functions
│ │ └── route.json
│ ├── createAttribute
│ │ ├── ... // Middleware functions
│ │ └── route.json
│ └── updateAttribute
│ ├── ... // Middleware functions
│ └── route.json
API Folder Structure
The api folder contains the following organization:
-
global- Contains middleware functions that apply to all API endpoints. For example, theauthmiddleware that handles authentication for all secured endpoints. -
<apiID>- Individual folders representing specific API endpoints. For example, thecreateProductfolder contains all the components needed for the product creation API.
Single API Endpoint Structure
Each API endpoint folder contains middleware functions and a route definition file. Here's an example structure:
├── createProduct
│ ├── [context]bodyParser[auth].ts
│ ├── saveProduct.ts
│ └── route.json
The middleware functions handle the request processing, while the route.json file defines how the endpoint is accessed.
API Route Definition
Every API endpoint requires a route.json file that specifies the HTTP methods, path, and access restrictions. For example, here's the route definition for a product deletion API:
{
"methods": ["DELETE"],
"path": "/products/:id",
"access": "private"
}
The folder name is used as the routeId, so ensure each folder name is unique and contains only URL-safe characters. The routeId is important for correctly routing and identifying API endpoints.
For more detailed information on routing patterns and options, refer to the routing system documentation.
API Authentication
The access property in the route.json file controls endpoint security:
public- The endpoint can be accessed without authenticationprivate- The endpoint requires authentication (admin user credentials)
For security best practices, endpoints without an explicitly defined access property are treated as private by default, requiring authentication to access.
API Request Data Validation
EverShop allows you to define validation schemas for API request data using a payloadSchema.json file in the API endpoint folder. This ensures that incoming data meets your requirements before processing.
Here's an illustrative schema — it is not the schema EverShop ships for createProduct, which declares only description, metafields and duplicate_of and has no required array. It does demonstrate the two conventions the real file uses: skipEscape: true and per-property errorMessage.
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"short_description": {
"type": "string"
},
"url_key": {
"type": "string",
"pattern": "^\\S+$"
},
"meta_title": {
"type": "string"
},
"meta_description": {
"type": "string"
},
"meta_keywords": {
"type": "string"
},
"status": {
"type": ["integer", "string"],
"enum": [0, 1, "0", "1"]
},
"sku": {
"type": "string"
},
"price": {
"type": ["string", "number"],
"pattern": "^\\d+(\\.\\d{1,2})?$"
},
"weight": {
"type": ["string", "number"],
"pattern": "^[0-9]+(\\.[0-9]{1,2})?$"
},
"qty": {
"type": ["string", "number"],
"pattern": "^[0-9]+$"
},
"manage_stock": {
"type": ["string", "number"],
"enum": [0, 1, "0", "1"]
},
"stock_availability": {
"type": ["string", "number"],
"enum": [0, 1, "0", "1"]
},
"group_id": {
"type": ["string", "integer"],
"pattern": "^[0-9]+$"
},
"visibility": {
"type": ["integer", "string"],
"enum": [0, 1, "0", "1"]
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"attributes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"attribute_code": {
"type": "string"
},
"value": {
"type": ["string", "array"],
"items": {
"type": "string"
}
}
}
}
},
"categories": {
"type": "array",
"items": {
"type": ["string", "integer"],
"pattern": "^[0-9]+$"
}
},
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"option_name": {
"type": "string"
},
"option_type": {
"type": "string",
"enum": ["select", "multiselect"]
},
"is_required": {
"type": ["string", "integer"],
"enum": [0, 1, "0", "1"],
"default": 0
},
"values": {
"type": "array",
"items": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"extra_price": {
"type": ["string", "number"],
"pattern": "^\\d+(\\.\\d{1,2})?$"
}
}
}
}
},
"required": ["option_name", "option_type", "values"],
"additionalProperties": true
}
}
},
"required": [
"name",
"meta_title",
"url_key",
"status",
"sku",
"qty",
"price",
"weight",
"group_id",
"visibility"
],
"additionalProperties": true
}
When a request is made to the API endpoint, the request data is automatically validated against this schema. If validation fails, an appropriate error response is sent to the client with details about the validation issues.
EverShop uses Ajv JSON schema validator for request payload validation, providing robust and flexible validation capabilities.
API Middleware Functions
EverShop's API endpoints are powered by middleware functions that process requests sequentially. You can create as many middleware functions as needed for each API endpoint, allowing for modular and maintainable code.
For more details on how middleware functions work in EverShop, refer to the middleware system documentation.
Sharing Middleware Across Endpoints
For efficiency, you may need to reuse middleware functions across multiple API endpoints. EverShop provides a convenient way to share middleware:
-
Endpoint-Specific Sharing: Create a special folder named after the endpoints that should share middleware (e.g.,
createProduct+updateProduct) in theapidirectory. Place shared middleware functions in this folder, and they'll be executed for both endpoints. -
Global Sharing: For middleware that should run for all API endpoints, place it in the
api/globalfolder.
These special shared middleware folders should contain only middleware functions without a route.json file, as they don't define endpoints themselves but enhance existing ones.
Rate Limiting
Every API endpoint sits behind a per-client-IP rate limiter that is mounted globally, before any module middleware runs. You do not opt in, and you cannot opt out from a route.json or a middleware.
| Scope | Limit (per IP) |
|---|---|
/api and /api/** | 120 requests per minute |
Credential endpoints — POST to /api/customers, /api/customers/reset-password, /api/customers/password, /customer/login, /admin/user/login | 8 requests per 15 minutes |
| Page routes (storefront and admin) | 300 requests per minute |
Requests for static assets, HMR, /backend/, /health and /healthz are exempt. The limiter is disabled when NODE_ENV=test.
The 429 Contract
When a limit is exceeded, an API request gets 429 Too Many Requests with EverShop's standard error envelope:
{
"error": {
"status": 429,
"message": "Too many requests. Please slow down and try again later."
}
}
Two response headers matter to clients:
Retry-After— seconds until the window resets. Honour it rather than retrying immediately.RateLimit-*— the standard limit/remaining/reset triple, so a well-behaved client can back off before it gets rejected.
Page routes get the same status with a plain-text body instead of JSON.
Behind a reverse proxy, the limiter only sees the true client IP if Express trust proxy matches your topology. Set the TRUST_PROXY_HOPS environment variable to the number of proxy layers in front of the app (default 1); otherwise all traffic looks like one IP and a single visitor can lock everyone out.
Best Practices for API Development
When developing APIs for EverShop, consider these best practices:
- Proper Validation: Always define comprehensive validation schemas for request data
- Access Control: Set appropriate
accesslevels for your endpoints - Modular Design: Break complex operations into multiple middleware functions
- Error Handling: Provide clear error responses with appropriate HTTP status codes
- Handle 429s: Clients should respect
Retry-Afterand theRateLimit-*headers rather than retrying in a tight loop - Documentation: Comment your code and keep API contracts consistent