Guides · For businesses
A secure staff portal with Node.js and SQLite
A place your team signs in to keep customers, jobs, stock or whatever your business tracks, with roles so people only do what they should. This guide shows how the staff portal BackendGen.dev generates is built, what keeps it safe, and how to set it up and look after it.
- Base licence
- Node.js 22+, Express, SQLite
- About an hour to set up
- Runs on Windows, macOS or Linux
Why Node.js and SQLite
A staff portal for a small business has a handful of people signed in at once and a few thousand records. That doesn't need a database server, a cluster or a cloud account. SQLite keeps everything in one file next to the program: nothing extra to install, run or pay for, and a backup is a copy of that file. Node.js with Express is one of the most widely used ways to write a web server, so any web developer can read and change the code.
The whole thing runs as one program: the server, the database and the portal's web pages. You can run it on the computer in your office, or on a small server for a few pounds a month so your team can sign in from anywhere.
What's inside
| File or folder | What it is |
|---|---|
server.js, app.js | Starts the Express app and connects the parts. |
lib/ | Sign-in, sessions, roles, the database, emails, backups. |
routes/ | What the portal can ask the server to do: staff, records and, with Advanced, the shop and bookings. |
portal/ | The staff portal itself: plain HTML, CSS and JavaScript, served by the same server. |
business-config.json | Your settings: business details, record types and fields, security and backups. |
.env | Secrets such as the session key and email password. Never share it. |
business.sqlite | The database, made the first time it starts. |
backups/ | Automatic copies of the database. |
self-test.js, update.js | A check that everything works, and safe updates. |
Roles
Everyone signs in with their own email and password. Each person has one of three roles, and the server checks the role on every request, so hiding a button in the portal is never the only thing standing in the way.
| Role | Can | Can't |
|---|---|---|
| Owner | Everything, including adding and removing managers and owners. | — |
| Manager | Everything else: delete records, export to CSV, see the activity log, manage staff. | Change owners or other managers. |
| Staff | View, add and edit records; day-to-day shop and booking work with Advanced. | Delete (unless you allow it for a record type), export, refund, change prices or see the activity log. |
New people get a temporary password, shown once, and must choose their own before they can do anything else. When someone leaves, switch their account off: their sessions stop working straight away and their history stays in the activity log.
Your records
You decide what the portal keeps. Each record type is a list, such as Customers, Jobs, Suppliers or Tasks, with the fields you need. They're set in the generator, or later in business-config.json:
"collections": [
{
"key": "jobs", "label": "Jobs", "singular": "Job", "staffCanDelete": false,
"fields": [
{ "key": "title", "label": "Job", "type": "text", "required": true, "listed": true },
{ "key": "customer", "label": "Customer", "type": "text", "listed": true },
{ "key": "status", "label": "Status", "type": "select", "listed": true,
"options": ["Quote", "Booked", "In progress", "Done", "Invoiced", "Paid"] },
{ "key": "due", "label": "Due", "type": "date", "listed": true },
{ "key": "price", "label": "Price", "type": "money" },
{ "key": "notes", "label": "Notes", "type": "longtext" }
]
}
]Field types are text, longtext, number, money, date, datetime, email, phone, url, select (with options) and checkbox. "listed": true shows a field as a column in the table. Add a type or a field, restart, and it's there; removing a field hides it but keeps what was stored, so nothing is lost by accident.
What keeps it safe
These are on by default; there's nothing to switch on.
- Passwords are stored scrambled with scrypt, never as they were typed, and weak ones are refused.
- Sign-in uses a session cookie the page's own scripts can't read (
HttpOnly,SameSite=Strict). The server keeps only a scrambled copy of each session key, and sessions end after 12 hours by default. - Guessing passwords doesn't work: after 5 wrong tries an account is locked for 15 minutes, and every failed sign-in is logged.
- Other websites can't act for your staff. Every change the portal makes must carry a portal header and come from the portal's own address.
- Security headers (a content security policy, and HTTPS-only once you're on HTTPS), and rate limits on the public endpoints your website uses.
- The activity log records who signed in, added, changed, deleted or exported what, and when. It notes which fields changed, never their contents, and keeps a year by default.
- CSV exports are managers and owners only, and are written so a spreadsheet can't mistake a value for a formula.
- No telemetry. The portal never contacts BackendGen.dev or anyone else unless you set up email (or, with Advanced, Stripe and PayPal).
The limits are in business-config.json under "security":
"security": {
"sessionHours": 12,
"loginAttempts": 5,
"lockMinutes": 15,
"auditDays": 365
}What it doesn't have: two-step sign-in (codes from an app). If your business needs it, put the portal behind a service that adds it, such as a VPN or a sign-in gateway in front of the server.
Set it up
-
Generate it
In the business generator, pick Staff portal (customers + jobs) for a trade, agency or repair shop, or Start empty to add your own record types. Fill in the business name and the owner's email, then download.
-
Start it
Install Node.js 22 or newer, then double-click
start-local.cmd(Windows) orstart-local.command(Mac). Or in a terminal:npm install npm start -
Sign in as the owner
Open
http://localhost:3000/portaland sign in with the owner's email and theOWNER_PASSWORDfrom.env. Choose your own password, then delete theOWNER_PASSWORDline. Locked yourself out later? Runnode reset-owner.json the server for a new temporary password. -
Add your team
Open Team and add each person with their role. Give them their temporary password in person or by phone, not in the same email as the address of the portal.
-
Check it
npm run self-testThe self-test checks that wrong passwords are refused, that staff can't do what only managers can, that the cross-site protection works, and that records can be added and edited. Then it tidies up after itself.
Before you go live
To let your team sign in from anywhere, put it on a small server with HTTPS. The deploy guide takes about 20 minutes on Ubuntu or Windows Server. Then go through this list:
- HTTPS only. Set
"publicUrl"to yourhttps://address; the server then tells browsers to use HTTPS every time. OWNER_PASSWORDdeleted from.env, and.envnever emailed or put on GitHub.- A firewall that only lets in web traffic (ports 80 and 443) and your own remote access.
- Backups copied off the server now and then. Copies are made every 6 hours into
backups/(the newest 28 are kept), but a copy on the same machine won't help if the machine is lost. - Your privacy notice updated: you keep customers' details, so you're the data controller under GDPR. Say what you keep and why.
Looking after it
- Restore a backup (stop the server first):
node restore-backup.js backups/<file>.sqlite - Update by downloading an update package from your project on BackendGen.dev. It never touches the database, its backups or
.env, saves everything it replaces inupdate-backups/, runs the self-test and rolls back by itself if the server doesn't come back up. - Keep Node.js updated on the server with its usual security updates.
- Check the activity log now and then for sign-ins you don't recognise.
Changing how it works
Most changes need no code: record types, fields, roles allowed to delete, session length and backups are all settings. For anything else, the code is plain, commented JavaScript, and your web developer can change it.
Update packages replace the generated files (app.js, lib/, routes/, portal/). Your copies are saved in update-backups/ first, so nothing is lost, but changes to those files have to be made again after an update. Keep a short note of what you changed, or put bigger additions in files of your own.
Your website, a spreadsheet or another program can use the same server: the full list of endpoints is at /api/openapi.json.
See your portal before you buy
Create a free account, set up your record types in the business generator and see exactly which files you'd get. You only need a licence to download.
Open the business generatorBusiness backendsMore for businesses: online bookings for a clinic or salon · self-hosted vs Firebase vs Retool.