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.
API endpoints allow you to create custom RESTful routes for your EverShop store. They’re perfect for handling form submissions, webhooks, integrations, and custom business logic.
Overview
EverShop uses a file-based routing system where:
Directory name = endpoint path
route.json = route configuration (methods, access)
Middleware files = request handlers with execution order
Directory Structure
API endpoints are created in the api folder of your extension:
extensions/[extension-name]/
└── src/
└── api/
└── [endpoint-name]/
├── route.json # Route configuration
├── [middleware1]handler.ts # Middleware with order
└── middleware1.ts # Middleware function
Creating Your First API Endpoint
Create the Endpoint Directory
Create a directory for your endpoint:
mkdir -p extensions/my-extension/src/api/createFoo
The directory name determines the route path. For example, createFoo would be accessible at /foos.
Define the route configuration:
{
"methods" : [ "POST" ],
"path" : "/foos" ,
"access" : "private"
}
Access Levels:
public - Accessible to everyone
private - Requires authentication
Create a body parser middleware:
import bodyParser from 'body-parser' ;
export default ( request , response , next ) => {
bodyParser . json ({ inflate: false })( request , response , next );
};
Create the main handler with middleware ordering:
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' });
}
// Create the new foo item
const newFoo = {
id: Date . now (),
name ,
description
};
// Simulate saving to a database
console . log ( 'Creating new foo:' , newFoo );
// Respond with the created foo item
response . status ( 201 ). json ({
success: true ,
data: {
foo: newFoo
}
});
};
The [bodyParser] prefix ensures the body parser runs before the handler. This is the middleware execution order.
Middleware Execution Order
Middleware execution is controlled by filename prefixes in square brackets:
[middleware1]handler.ts # Runs after middleware1
[middleware1][middleware2]handler.ts # Runs after both
Example Middleware Chain
src/api/createProduct/
├── route.json
├── authenticate.ts
├── [authenticate]validateInput.ts
└── [authenticate][validateInput]createProduct.ts
Execution order:
authenticate.ts
validateInput.ts
createProduct.ts
Route Configuration
HTTP Methods
Specify which HTTP methods are allowed:
{
"methods" : [ "GET" , "POST" , "PUT" , "DELETE" ],
"path" : "/api/items" ,
"access" : "public"
}
Custom Paths
Override the default path:
{
"methods" : [ "POST" ],
"path" : "/api/v2/custom/endpoint" ,
"access" : "private"
}
Dynamic Parameters
Use URL parameters:
{
"methods" : [ "GET" ],
"path" : "/api/items/:id" ,
"access" : "public"
}
Access in handler:
export default ( request , response ) => {
const { id } = request . params ;
// ...
};
Real-World Examples
GET Endpoint
Fetch data from the database:
src/api/getFoos/getFoos.ts
import { EvershopRequest , EvershopResponse } from '@evershop/evershop' ;
export default async ( request : EvershopRequest , response : EvershopResponse ) => {
try {
// Fetch from database (example)
const foos = [
{ id: 1 , name: 'Foo' , description: 'This is a Foo object' },
{ id: 2 , name: 'Bar' , description: 'This is a Bar object' }
];
response . json ({
success: true ,
data: { foos }
});
} catch ( error ) {
response . status ( 500 ). json ({
success: false ,
error: 'Failed to fetch foos'
});
}
};
src/api/getFoos/route.json
{
"methods" : [ "GET" ],
"path" : "/foos" ,
"access" : "public"
}
POST with Validation
Create a resource with input validation:
src/api/createFoo/validateInput.ts
import { EvershopRequest , EvershopResponse } from '@evershop/evershop' ;
export default ( request : EvershopRequest , response : EvershopResponse , next ) => {
const { name , description } = request . body ;
const errors = [];
if ( ! name || name . trim (). length === 0 ) {
errors . push ({ field: 'name' , message: 'Name is required' });
}
if ( ! description || description . trim (). length < 10 ) {
errors . push ({
field: 'description' ,
message: 'Description must be at least 10 characters'
});
}
if ( errors . length > 0 ) {
return response . status ( 400 ). json ({
success: false ,
errors
});
}
next ();
};
src/api/createFoo/[bodyParser][validateInput]createFoo.ts
import { EvershopRequest , EvershopResponse } from '@evershop/evershop' ;
export default async ( request : EvershopRequest , response : EvershopResponse ) => {
try {
const { name , description } = request . body ;
// Save to database (example)
const newFoo = {
id: Date . now (),
name ,
description ,
createdAt: new Date (). toISOString ()
};
response . status ( 201 ). json ({
success: true ,
data: { foo: newFoo }
});
} catch ( error ) {
response . status ( 500 ). json ({
success: false ,
error: 'Failed to create foo'
});
}
};
PUT/PATCH Endpoint
Update a resource:
src/api/updateFoo/[bodyParser]updateFoo.ts
import { EvershopRequest , EvershopResponse } from '@evershop/evershop' ;
export default async ( request : EvershopRequest , response : EvershopResponse ) => {
try {
const { id } = request . params ;
const { name , description } = request . body ;
// Update in database (example)
const updatedFoo = {
id: parseInt ( id ),
name ,
description ,
updatedAt: new Date (). toISOString ()
};
response . json ({
success: true ,
data: { foo: updatedFoo }
});
} catch ( error ) {
response . status ( 500 ). json ({
success: false ,
error: 'Failed to update foo'
});
}
};
src/api/updateFoo/route.json
{
"methods" : [ "PUT" , "PATCH" ],
"path" : "/foos/:id" ,
"access" : "private"
}
DELETE Endpoint
Delete a resource:
src/api/deleteFoo/deleteFoo.ts
import { EvershopRequest , EvershopResponse } from '@evershop/evershop' ;
export default async ( request : EvershopRequest , response : EvershopResponse ) => {
try {
const { id } = request . params ;
// Delete from database (example)
console . log ( `Deleting foo with id: ${ id } ` );
response . json ({
success: true ,
message: 'Foo deleted successfully'
});
} catch ( error ) {
response . status ( 500 ). json ({
success: false ,
error: 'Failed to delete foo'
});
}
};
src/api/deleteFoo/route.json
{
"methods" : [ "DELETE" ],
"path" : "/foos/:id" ,
"access" : "private"
}
Authentication & Authorization
Private Endpoints
Endpoints with "access": "private" require authentication:
{
"methods" : [ "POST" ],
"path" : "/api/admin/action" ,
"access" : "private"
}
Custom Authentication
Create an authentication middleware:
src/api/protected/authenticate.ts
import { EvershopRequest , EvershopResponse } from '@evershop/evershop' ;
export default ( request : EvershopRequest , response : EvershopResponse , next ) => {
const token = request . headers . authorization ?. replace ( 'Bearer ' , '' );
if ( ! token ) {
return response . status ( 401 ). json ({
success: false ,
error: 'Authentication required'
});
}
// Verify token (example)
try {
// Validate token logic here
request . user = { id: 1 , email: 'user@example.com' };
next ();
} catch ( error ) {
return response . status ( 401 ). json ({
success: false ,
error: 'Invalid token'
});
}
};
Error Handling
Standard Error Response
try {
// Your logic
} catch ( error ) {
console . error ( 'Error:' , error );
response . status ( 500 ). json ({
success: false ,
error: 'An unexpected error occurred'
});
}
Validation Errors
const errors = [];
if ( ! email || ! email . includes ( '@' )) {
errors . push ({ field: 'email' , message: 'Valid email is required' });
}
if ( errors . length > 0 ) {
return response . status ( 400 ). json ({
success: false ,
errors
});
}
Testing API Endpoints
Using cURL
# GET request
curl http://localhost:3000/foos
# POST request
curl -X POST http://localhost:3000/foos \
-H "Content-Type: application/json" \
-d '{"name":"New Foo","description":"A new foo item"}'
# PUT request
curl -X PUT http://localhost:3000/foos/123 \
-H "Content-Type: application/json" \
-d '{"name":"Updated Foo","description":"Updated description"}'
# DELETE request
curl -X DELETE http://localhost:3000/foos/123
Using Postman
Create a new request
Set the method (GET, POST, PUT, DELETE)
Enter the URL: http://localhost:3000/foos
Add headers: Content-Type: application/json
Add body (for POST/PUT): {"name":"Foo","description":"Description"}
Send the request
Best Practices
Use TypeScript : Always define types for request and response data to catch errors early.
Validate Input - Always validate and sanitize user input
Error Handling - Use try-catch blocks and return meaningful error messages
HTTP Status Codes - Use appropriate status codes (200, 201, 400, 401, 404, 500)
Consistent Response Format - Return consistent JSON structure
Security - Never expose sensitive data or internal errors
Logging - Log errors for debugging but don’t expose details to clients
Success Response
{
"success" : true ,
"data" : {
"item" : { ... }
}
}
Error Response
{
"success" : false ,
"error" : "Error message"
}
Validation Error Response
{
"success" : false ,
"errors" : [
{ "field" : "name" , "message" : "Name is required" },
{ "field" : "email" , "message" : "Invalid email format" }
]
}
Troubleshooting
Endpoint Not Found (404)
Verify route.json exists and is valid
Check the path matches your request URL
Run npm run build to rebuild
Check extension is enabled in config/default.json
Middleware Not Executing
Verify middleware filename uses bracket notation
Check middleware exports a function
Ensure middleware calls next() when done
Body Parser Not Working
Verify body parser middleware is first in chain
Check Content-Type header is application/json
Ensure body parser is imported correctly
Next Steps
Event Subscribers React to API events with subscribers
GraphQL Types Learn about GraphQL API