Skip to content

feat: Add typescript support #595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# Emacs
*~
.eslintcache

# build folder
dist

# Docker files
Docker*
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
APP_ID=myAppId
MASTER_KEY=myMasterKey
MONGODB_URI=mongodb://localhost:27017/parse
PORT=1337
SERVER_URL=http://localhost:1337
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ node_modules
# Emacs
*~
.eslintcache

# build folder
dist

# env files
.env*
!.env.example
34 changes: 18 additions & 16 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
FROM node:latest
# Builder stage
FROM node:22.12.0-alpine AS builder

RUN mkdir parse
WORKDIR /usr/src/parse

COPY package*.json .

ADD . /parse
WORKDIR /parse
RUN npm install

ENV APP_ID setYourAppId
ENV MASTER_KEY setYourMasterKey
ENV DATABASE_URI setMongoDBURI
COPY . .

# Optional (default : 'parse/cloud/main.js')
# ENV CLOUD_CODE_MAIN cloudCodePath
RUN npm run build

# Optional (default : '/parse')
# ENV PARSE_MOUNT mountPath
# latest supported node version when this Dockerfile was written
FROM node:22.12.0-alpine

EXPOSE 1337
WORKDIR /usr/src/parse

# Uncomment if you want to access cloud code outside of your container
# A main.js file must be present, if not Parse will not start
# Copy only the required files from the builder stage
COPY --from=builder /usr/src/parse/node_modules ./node_modules
COPY --from=builder /usr/src/parse/dist ./
COPY --from=builder /usr/src/parse/public ./public

# VOLUME /parse/cloud
VOLUME ["/usr/src/parse/cloud", "/usr/src/parse/logs"]

EXPOSE 1337

CMD [ "npm", "start" ]
CMD ["node", "index.js"]
38 changes: 34 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The [Parse Server guide](https://docs.parseplatform.org/parse-server/guide/) is
---

- [Local Development](#local-development)
- [Docker Deployment](#docker-deployment)
- [Helpful Scripts](#helpful-scripts)
- [Remote Deployment](#remote-deployment)
- [Heroku](#heroku)
Expand Down Expand Up @@ -50,19 +51,48 @@ The [Parse Server guide](https://docs.parseplatform.org/parse-server/guide/) is
7. By default the API route will use `/parse` as a base. You can change this by setting the environment variable `PARSE_MOUNT`, for example in the CLI run run `export PARSE_MOUNT=/app` to set the path to `app`.
8. Your Parse Server is not running and is connected to your local database named `dev` in which the data is stored that you manage via Parse Server.

## Docker Deployment

You can also run Parse Server using Docker:

1. Create a `.env` file with your configuration variables. For example:
```
APP_ID=myAppId
MASTER_KEY=myMasterKey
DATABASE_URI=mongodb://localhost:27017/parse
PORT=1337
PARSE_MOUNT=/parse
```

2. Run Docker with the following command, mounting volumes as needed:
```
docker build -t parse-server .
docker run -p 1337:1337 --env-file .env \
-v $(pwd)/logs:/usr/src/parse/logs \
-v $(pwd)/cloud:/usr/src/parse/cloud \
parse-server
```

This allows you to:
- Use an environment file for configuration
- Mount the logs directory to persist logs outside the container
- Mount the cloud directory to access your Cloud Code files from the container

You can customize the mounted volumes based on your needs, such as mounting config files or other directories that require persistence or runtime modifications.

## Helpful Scripts
These scripts can help you to develop your app for Parse Server:

* `npm run watch` will start your Parse Server and restart if you make any changes.
* `npm run lint` will check the linting of your cloud code, tests and `index.js`, as defined in `.eslintrc.json`.
* `npm run lint-fix` will attempt fix the linting of your cloud code, tests and `index.js`.
* `npm run prettier` will help improve the formatting and layout of your cloud code, tests and `index.js`, as defined in `.prettierrc`.
* `npm run lint` will check the linting of your cloud code, tests and `index.ts`, as defined in `.eslintrc.json`.
* `npm run lint-fix` will attempt fix the linting of your cloud code, tests and `index.ts`.
* `npm run prettier` will help improve the formatting and layout of your cloud code, tests and `index.ts`, as defined in `.prettierrc`.
* `npm test` will run all tests
* `npm run coverage` will run tests and check coverage. Output is available in the `/coverage` folder.

## Configuration

Configuration is located in `config.js`.
Configuration is located in `config.ts`.


# Remote Deployment
Expand Down
6 changes: 5 additions & 1 deletion cloud/functions.js → cloud/functions.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
Parse.Cloud.define('hello', req => {
// @ts-expect-error req.log exists, but it was not added to types/parse
req.log.info(req);
return 'Hi';
});

Parse.Cloud.define('helloAsyncFunction', async req => {
await new Promise(resolve => setTimeout(resolve, 1000));
// @ts-expect-error req.log exists, but it was not added to types/parse
req.log.info(req);
return 'Hi async';
});

Parse.Cloud.beforeSave('TestObject', () => {
throw new Parse.Error(9001, 'Saving test objects is not available.');
throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Saving test objects is not available.');
});

export {};
6 changes: 3 additions & 3 deletions cloud/main.js → cloud/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// It is best practise to organize your cloud functions group into their own file. You can then import them in your main.js.
await Promise.all([
import('./functions.js')
]);
Promise.all([import('./functions.js')]);

export {};
15 changes: 0 additions & 15 deletions cloud/schema.js

This file was deleted.

17 changes: 17 additions & 0 deletions cloud/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const schemaDefinitions = [
{
className: 'TestObject',
fields: {
beforeSave: { type: 'Boolean', defaultValue: false },
additionalData: { type: 'String' },
},
classLevelPermissions: {
find: { '*': true },
count: { '*': true },
get: { '*': true },
update: { '*': true },
create: { '*': true },
delete: { '*': true },
},
},
];
8 changes: 5 additions & 3 deletions config.js → config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { schemaDefinitions } from "./cloud/schema.js";
import { schemaDefinitions } from './cloud/schema.js';

export const config = {
databaseURI: process.env.DATABASE_URI || process.env.MONGODB_URI || 'mongodb://localhost:27017/dev',
cloud: process.env.CLOUD_CODE_MAIN || './cloud/main.js',
databaseURI:
process.env.DATABASE_URI || process.env.MONGODB_URI || 'mongodb://localhost:27017/dev',
cloud: () => import('./cloud/main.js'),
appId: process.env.APP_ID || 'myAppId',
masterKey: process.env.MASTER_KEY || '', //Add your master key here. Keep it secret!
serverURL: process.env.SERVER_URL || 'http://localhost:1337/parse', // Don't forget to change to https if needed
Expand Down
26 changes: 0 additions & 26 deletions eslint.config.js

This file was deleted.

47 changes: 47 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals';
import path from 'node:path';

const __dirname = path.resolve();

export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ['**/*.ts'],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'module',
parser: tseslint.parser,
parserOptions: {
project: ['./tsconfig.json', './spec/tsconfig.json'],
tsconfigRootDir: __dirname,
},
globals: {
...globals.node,
Parse: 'readonly',
},
},
rules: {
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
indent: ['error', 2, { SwitchCase: 1 }],
'linebreak-style': ['error', 'unix'],
'no-trailing-spaces': 'error',
'eol-last': 'error',
'space-in-parens': ['error', 'never'],
'no-multiple-empty-lines': 'warn',
'prefer-const': 'error',
'space-infix-ops': 'error',
'no-useless-escape': 'off',
'require-atomic-updates': 'off',
'no-var': 'warn',
'no-await-in-loop': 'warn',
},
},
{
ignores: ['dist/**/*', 'logs/**/*', 'public/**/*', 'release.config.js'],
}
);
29 changes: 15 additions & 14 deletions index.js → index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@ import { ParseServer } from 'parse-server';
import path from 'path';
import http from 'http';
import { config } from './config.js';
const __dirname = path.resolve();

const app = express();

// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));

// Serve the Parse API on the /parse URL prefix
const mountPath = process.env.PARSE_MOUNT || '/parse';
const server = new ParseServer(config);
await server.start();
app.use(mountPath, server.app);

// Parse Server plays nicely with the rest of your web routes
app.get('/', function (req, res) {
res.status(200).send('I dream of being a website. Please star the parse-server repo on GitHub!');
Expand All @@ -29,11 +23,18 @@ app.get('/test', function (req, res) {
res.sendFile(path.join(__dirname, '/public/test.html'));
});

const port = process.env.PORT || 1337;
const httpServer = http.createServer(app);
httpServer.listen(port, function () {
console.log('parse-server-example running on port ' + port + '.');
// Serve the Parse API on the /parse URL prefix
const mountPath = process.env.PARSE_MOUNT || '/parse';
const server = new ParseServer(config);

server.start().then(async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use top level await here?

Copy link
Author

@abubkr-hago abubkr-hago Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to convert the project to a module by adding type:module in package.json and incrementing the module and target in tsconfig making it not the same as Parse and Parse Server configs.

Should I change it in the tsconfig?

Copy link
Member

@mtrezza mtrezza Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's not necessary, if you feel like doing it, then I'll wait with merging, otherwise let me know and we merge as is.

app.use(mountPath, server.app);
const port = process.env.PORT || 1337;
const httpServer = http.createServer(app);
httpServer.listen(port, function () {
console.log('parse-server-example running on port ' + port + '.');
});
console.log(`Visit http://localhost:${port}/test to check the Parse Server`);
// This will enable the Live Query real-time server
await ParseServer.createLiveQueryServer(httpServer);
});
// This will enable the Live Query real-time server
await ParseServer.createLiveQueryServer(httpServer);
console.log(`Visit http://localhost:${port}/test to check the Parse Server`);
9 changes: 9 additions & 0 deletions nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"ignore": [
"dist"
],
"ext": "json,ts,mjs,cjs,js",
"execMap": {
"ts": "tsx"
}
}
Loading