Appearance
main.js — Backend Routes
main.js is the backend entry point for your EA plugin. It runs on the server side and lets you define Express routes, access the plugin database, protect endpoints, and optionally bootstrap a full NestJS application.
To enable it, add "main": "main.js" to your setting.json:
json
{
"versionNumber": "0",
"version": "1.0.0",
"name": "my-plugin",
"init": true,
"type": "EA",
"main": "main.js",
"script": "script.js",
"id": "unique-plugin-id"
}Function Signature
main.js must export a function that receives a context object:
js
module.exports = ({ server, Router, app, verifyToken, databasePath, users, io }) => {
// your backend logic
}Context Parameters
| Parameter | Type | Description |
|---|---|---|
server | Express | The Express server instance. Mount your routes on it |
Router | function | Express Router constructor for creating route groups |
app | object | Plugin utilities — database access, helpers (see below) |
verifyToken | middleware | Express middleware that validates the user's auth token. Use on protected routes |
databasePath | string | Absolute path to the plugin's database/ folder |
users | object | Access to the platform's users service |
io | Socket.IO | Socket.IO server instance for real-time events |
app Utilities
app.database(name)
Returns a database accessor for the given database namespace (folder name inside database/).
js
const db = app.database("my-plugin");.readJson(path[])
Reads and parses a JSON file. Path is an array of path segments relative to the database namespace folder.
js
const data = app.database("my-plugin").readJson(["data.json"]);.writeJson(path[], data)
Serializes and writes data to a JSON file.
js
app.database("my-plugin").writeJson(["data.json"], data);app.getRndString()
Generates a random unique string, useful for creating record IDs.
js
const id = app.getRndString();
// → "_a3f92bc1d4"Pattern 1 — Simple Express Router
Use this pattern for lightweight REST endpoints. All routes are mounted under /api/<your-prefix>.
js
module.exports = ({ server, Router, app, verifyToken }) => {
let API = Router();
// Public route
API.post("/contact", (req, res) => {
let data = app.database("my-plugin").readJson(["contact.json"]);
data.unshift(req.body);
app.database("my-plugin").writeJson(["contact.json"], data);
res.json(true);
});
// Protected routes (require auth token)
API.get("/items/:id", (req, res) => {
let data = app.database("my-plugin").readJson(["items.json"]);
res.json(data[req.params.id]);
})
.post("/items", verifyToken, (req, res) => {
let data = app.database("my-plugin").readJson(["items.json"]);
let body = req.body;
body.id = app.getRndString();
data[body.id] = body;
app.database("my-plugin").writeJson(["items.json"], data);
res.json(true);
})
.put("/items/:id", verifyToken, (req, res) => {
let data = app.database("my-plugin").readJson(["items.json"]);
data[req.params.id] = req.body;
app.database("my-plugin").writeJson(["items.json"], data);
res.json(true);
})
.delete("/items/:id", verifyToken, (req, res) => {
let data = app.database("my-plugin").readJson(["items.json"]);
delete data[req.params.id];
app.database("my-plugin").writeJson(["items.json"], data);
res.json(true);
});
server.use("/api/my-plugin", API);
}Routes are available at
/api/my-plugin/items,/api/my-plugin/contact, etc.
Pattern 2 — NestJS Bootstrap
For complex plugins, you can bootstrap a full NestJS application from main.js. The NestJS app is built separately and its compiled output is placed in database/backend/.
js
const { join } = require("path");
module.exports = async ({ server, databasePath, app, users, io }) => {
const backendPath = join(databasePath, "backend", "dist", "src");
const { bootstrap } = require(join(backendPath, "main"));
// Set environment variables your NestJS app needs
process.env['DATABASE_PATH'] = databasePath;
process.env['APP_SECRET'] = "your-secret";
return await bootstrap({
server,
app,
users,
io,
databasePath
});
};NestJS File Structure
When using the NestJS pattern, your plugin's database/ folder holds the compiled backend output:
my-plugin/
├── 1.0.0/
│ ├── setting.json
│ ├── script.js
│ └── main.js ← bootstraps the NestJS app
└── database/
└── my-plugin/
└── backend/
└── dist/
└── src/
└── main.js ← compiled NestJS entry pointThe NestJS project lives outside the plugin folder and is developed separately. Only the compiled
dist/output is placed indatabase/backend/when deploying.
Using verifyToken
Add verifyToken as middleware on any route that requires the user to be authenticated:
js
// Public — no auth required
API.get("/public-data", (req, res) => { ... });
// Protected — must send a valid token
API.post("/private-data", verifyToken, (req, res) => { ... });
API.put("/private-data/:id", verifyToken, (req, res) => { ... });
API.delete("/private-data/:id", verifyToken, (req, res) => { ... });Requests to protected routes without a valid token receive a 401 Unauthorized response automatically.
Database JSON Structure
Design your database/ JSON files to match what your routes read and write. Example for a simple items store:
json
{
"_a3f92bc1d4": {
"id": "_a3f92bc1d4",
"name": "Item One",
"price": 25
},
"_b7e01cd2f5": {
"id": "_b7e01cd2f5",
"name": "Item Two",
"price": 40
}
}Using an object keyed by ID (from app.getRndString()) makes lookups, updates, and deletes by ID fast and straightforward.