Guide
Getting started
Three commands to a running API with OpenAPI docs, a seeded user, and a MongoDB configured so transactions actually work.
Requirements
| Node | ≥ 20.19 |
| MongoDB | ≥ 7.0.16 for parse-server 9. A replica set is required for transactions |
| TypeScript | Any version, with experimentalDecorators: true |
Create a project
npx parse-server-kit new my-api
cd my-api
npm run db:up
npm run dev
The generated project prints a ready-to-paste curl on startup, including a
session token for a seeded demo user, so your first request works
immediately rather than answering 400.
@Transactional and @ParseVersionField
would fail against one. The generated docker-compose.yml configures a
single-node replica set to avoid that. Not using Docker? Put a MongoDB Atlas
connection string in DATABASE_URI and skip the compose step; Atlas is
already a replica set.
What was generated
my-api/
├── docker-compose.yml # MongoDB as a replica set
├── tsconfig.json # decorator flags already set
├── .env
└── src/
├── app.ts # boot order — the sequence matters
├── roles.ts # your roles, declared once
├── seed.ts # roles, first admin, sample rows
├── models/Note.ts # @ParseClass + @ParseField + a trigger
├── functions/note.ts # @Route + five endpoints
└── server/ # plumbing: banner, dashboard mount
Your API is at /api/notes/* and its documentation at /api-docs.
Your first request
Both of these are real exchanges against a freshly generated project — the responses below are what the server actually returned.
curl -X POST "http://localhost:1337/api/notes/createNote" \
-H "X-Parse-Application-Id: my-api" \
-H "X-Parse-Session-Token: <token>" \
-H "Content-Type: text/plain" \
-d '{"title":"Quarterly report","body":"Revenue up.","status":"published"}'
{ "title": "Quarterly report",
"body": "Revenue up.",
"status": "published",
"slug": "quarterly-report", // derived
"views": 0, // defaulted
"objectId": "21R47fDVEB",
"createdAt": "2026-08-24T09:17:13.202Z",
"updatedAt": "2026-08-24T09:17:13.202Z" }
slug was derived and views defaulted by the model's
@BeforeSave trigger, which also validated the whole object. That happens
on every save path, not only this endpoint.
curl "http://localhost:1337/api/notes/listNotes?limit=2&status=published" \
-H "X-Parse-Application-Id: my-api"
Query-string values arrive as strings — validateEntityRoutes
merges them into the body. Declare them {type: String} and convert:
Number(req.params.limit).
{ "results": [
{ "objectId": "MJq00knE1O", "title": "Welcome",
"slug": "welcome", "status": "published", "views": 0,
"ACL": { "*": { "read": true },
"role:Editor": { "read": true, "write": true } } }
], "count": 1 }
The seeded draft note exists too and is simply invisible here — no
where clause did that, the row's ACL did. See
Permissions & ACL.
Parse Dashboard
Parse has an official admin console — browse and edit every class, run queries, inspect users and roles, send push. It is not installed by default, and getting it is one command:
npm install parse-dashboard
npm run dev
Restart and it is at http://localhost:1337/dashboard. The generated
app.ts already detects it, so there is nothing to wire, and the startup
banner tells you whether it came up.
DASHBOARD_USER and DASHBOARD_PASS in .env before it
goes anywhere. Unset in production it refuses to mount at all.
The full page on the dashboard covers read-only operators,
HTTPS, and how to expose it safely.
Adding your own entity
psk g resource Product
Writes src/models/Product.ts and src/functions/product.ts
with the edit points marked, and changes nothing else — there is no module to
register into, because importFiles discovers both at boot.
TypeScript setup
If you are adding this to an existing project rather than generating one, one compiler flag is mandatory:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
This library uses legacy decorators. TypeScript 5 defaults to the standard
(TC39) ones, which are a different feature — without the flag every decorator
misbehaves. emitDecoratorMetadata is not needed.
declare name: string rather than
name!: string. @ParseField installs an accessor on the
prototype, and a real class field would shadow it. @ParseClass repairs
that automatically, but declare states what is actually true: the storage
belongs to Parse, not the instance. The full
explanation.
Where Parse's own docs take over
This site covers what this library adds. For Parse Server itself — query operators, ACL semantics, LiveQuery, file adapters, push, authentication providers — the Parse Platform documentation remains the reference, and nothing here replaces it.