-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
feat: Add support for custom HTTP status code and headers to Cloud Function response with Express-style syntax #9980
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
Conversation
|
Important Review skippedBot user detected. To trigger a single review, invoke the You can disable this status message by setting the 📝 WalkthroughWalkthroughThis change adds support for Express-style cloud functions that accept (req, res) parameters with HTTP response status codes. The router now detects function arity, creates enhanced response objects supporting status() method, and prevents duplicate response sends. Tests validate both Express-style and traditional function patterns with status codes. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Router as FunctionsRouter
participant Handler as Cloud Function
participant Response as ResponseObject
Client->>Router: POST /functions/:name
Router->>Router: Detect function.length (1 or 2 params)
alt Express-style (2 params)
Router->>Response: createResponseObject(resolve, reject, statusCode)
activate Response
Response->>Response: Initialize httpStatusCode, responseSent
deactivate Response
Router->>Handler: invoke(request, responseObject)
activate Handler
Handler->>Response: status(code)
Handler->>Response: success(result)
Response->>Response: Set httpStatusCode, mark responseSent=true
Handler-->>Router: return
deactivate Handler
Router->>Router: Check responseSent, skip auto-send
Router-->>Client: HTTP 200/custom + result
else Traditional (1 param)
Router->>Handler: invoke(request)
activate Handler
Handler-->>Router: return result
deactivate Handler
Router->>Response: success(result)
activate Response
Response->>Response: mark responseSent=true
deactivate Response
Router-->>Client: HTTP 200 + result
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Key areas requiring attention:
Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
Comment |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Co-authored-by: mtrezza <[email protected]>
Co-authored-by: mtrezza <[email protected]>
Co-authored-by: mtrezza <[email protected]>
Co-authored-by: mtrezza <[email protected]>
Co-authored-by: mtrezza <[email protected]>
Co-authored-by: mtrezza <[email protected]>
Co-authored-by: mtrezza <[email protected]>
|
🚀 Thanks for opening this pull request! |
Parse.Cloud.define(name, (request, response) => {});
Parse.Cloud.define(name, (request, response) => {});Parse.Cloud.define(..., (request, response) => {});
|
@copilot fix the failing tests:
|
…ning undefined Co-authored-by: mtrezza <[email protected]>
Fixed in commit eeff6f9. The issue was that traditional cloud functions returning |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/Routers/FunctionsRouter.js (1)
226-241: Verify behavior when Express-style function both returns a value and callsres.success().The logic correctly prevents double-send when
_isResponseSent()is true. However, there's a subtle edge case:Parse.Cloud.define('myFunc', async (req, res) => { res.success({ a: 1 }); return { b: 2 }; // This return value is silently ignored });This is probably the intended behavior, but consider adding a debug-level log or throwing an error when a value is returned after response was already sent, to help developers catch unintentional mistakes.
spec/CloudCode.spec.js (2)
4832-4872: Tests for 401 and 404 status codes don't verify the actual HTTP status.The tests verify that the request is rejected but don't assert that the HTTP status code was actually set to 401 or 404. Consider enhancing these tests:
it('should support 401 unauthorized status code with error', async () => { Parse.Cloud.define('unauthorizedFunction', (req, res) => { if (!req.user) { res.status(401).error('Unauthorized access'); } else { res.success({ message: 'Authorized' }); } }); - await expectAsync( - request({ + try { + await request({ method: 'POST', url: 'http://localhost:8378/1/functions/unauthorizedFunction', headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest', }, json: true, body: {}, - }) - ).toBeRejected(); + }); + fail('Expected request to be rejected'); + } catch (error) { + expect(error.status).toBe(401); + expect(error.data.error).toBe('Unauthorized access'); + } });
4792-4949: Consider adding tests for edge cases and error scenarios.The test suite covers the happy path well. Consider adding tests for:
- Double-send error: Calling
res.success()twice should throw- Mixed response calls: Calling
res.success()thenres.error()should throw- Hanging request scenario: Express-style function that neither calls response methods nor returns a value (to document current behavior)
Example test for double-send:
it('should throw error when calling success() twice', async () => { Parse.Cloud.define('doubleSend', (req, res) => { res.success({ first: true }); res.success({ second: true }); // Should throw }); await expectAsync(Parse.Cloud.run('doubleSend', {})).toBeRejectedWith( jasmine.objectContaining({ message: jasmine.stringMatching(/already been sent/) }) ); });src/cloud-code/Parse.Cloud.js (1)
822-827: Consider documenting the single-response constraint.The
FunctionResponseinterface documentation is good, but could mention thatsuccess()anderror()can only be called once per request. This is an important constraint that developers should be aware of./** * @interface Parse.Cloud.FunctionResponse - * @property {function} success Call this function to return a successful response with an optional result. Usage: `response.success(result)` - * @property {function} error Call this function to return an error response with an error message. Usage: `response.error(message)` + * @property {function} success Call this function to return a successful response with an optional result. Can only be called once per request. Usage: `response.success(result)` + * @property {function} error Call this function to return an error response with an error message. Can only be called once per request. Usage: `response.error(message)` * @property {function} status Call this function to set a custom HTTP status code for the response. Returns the response object for chaining. Usage: `response.status(code).success(result)` or `response.status(code).error(message)` */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
spec/CloudCode.spec.js(1 hunks)src/Routers/FunctionsRouter.js(3 hunks)src/cloud-code/Parse.Cloud.js(2 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:446-454
Timestamp: 2025-08-27T09:08:34.252Z
Learning: When analyzing function signature changes in Parse Server codebase, verify that call sites are actually incorrect before flagging them. Passing tests are a strong indicator that function calls are already properly aligned with new signatures.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1312-1338
Timestamp: 2025-05-04T20:41:05.147Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`.
📚 Learning: 2025-10-16T19:27:05.311Z
Learnt from: Moumouls
Repo: parse-community/parse-server PR: 9883
File: spec/CloudCodeLogger.spec.js:410-412
Timestamp: 2025-10-16T19:27:05.311Z
Learning: In spec/CloudCodeLogger.spec.js, the test "should log cloud function triggers using the silent log level" (around lines 383-420) is known to be flaky and requires the extra `await new Promise(resolve => setTimeout(resolve, 100))` timeout after awaiting `afterSavePromise` for reliability, even though it may appear redundant.
Applied to files:
spec/CloudCode.spec.js
📚 Learning: 2025-05-04T20:41:05.147Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1312-1338
Timestamp: 2025-05-04T20:41:05.147Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`.
Applied to files:
spec/CloudCode.spec.js
📚 Learning: 2025-08-27T09:08:34.252Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:446-454
Timestamp: 2025-08-27T09:08:34.252Z
Learning: When analyzing function signature changes in Parse Server codebase, verify that call sites are actually incorrect before flagging them. Passing tests are a strong indicator that function calls are already properly aligned with new signatures.
Applied to files:
spec/CloudCode.spec.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: Tests in the parse-server repository should use promise-based approaches rather than callback patterns with `done()`. Use a pattern where a Promise is created that resolves when the event occurs, then await that promise.
Applied to files:
spec/CloudCode.spec.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`. The preferred pattern is to create a Promise that resolves when an expected event occurs, then await that Promise.
Applied to files:
spec/CloudCode.spec.js
📚 Learning: 2025-04-30T19:31:35.344Z
Learnt from: RahulLanjewar93
Repo: parse-community/parse-server PR: 9744
File: spec/ParseLiveQuery.spec.js:0-0
Timestamp: 2025-04-30T19:31:35.344Z
Learning: In the Parse Server codebase, the functions in QueryTools.js are typically tested through end-to-end behavior tests rather than direct unit tests, even though the functions are exported from the module.
Applied to files:
spec/CloudCode.spec.js
📚 Learning: 2025-11-08T13:46:04.940Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: For new Parse Server options, verify that the option is documented in src/Options/index.js and that npm run definitions has been executed to reflect changes in src/Options/docs.js and src/Options/Definitions.js. README.md documentation is a bonus but not required for new options.
Applied to files:
src/cloud-code/Parse.Cloud.js
📚 Learning: 2025-11-08T13:46:04.940Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Applied to files:
src/cloud-code/Parse.Cloud.js
📚 Learning: 2025-12-02T06:55:53.808Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-12-02T06:55:53.808Z
Learning: When reviewing Parse Server PRs that add or modify Parse Server options, always verify that changes are properly reflected in three files: src/Options/index.js (where changes originate), src/Options/Definitions.js, and src/Options/docs.js. The correct workflow is: make changes in index.js first, then run `npm run definitions` to automatically replicate the changes to Definitions.js and docs.js.
Applied to files:
src/cloud-code/Parse.Cloud.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: Node 22
- GitHub Check: MongoDB 8, ReplicaSet
- GitHub Check: MongoDB 7, ReplicaSet
- GitHub Check: PostgreSQL 17, PostGIS 3.5
- GitHub Check: Redis Cache
- GitHub Check: Node 20
- GitHub Check: PostgreSQL 18, PostGIS 3.6
- GitHub Check: PostgreSQL 16, PostGIS 3.5
- GitHub Check: Docker Build
- GitHub Check: Benchmarks
🔇 Additional comments (7)
src/Routers/FunctionsRouter.js (2)
106-144: Response object implementation looks correct with proper guards.The
createResponseObjectcorrectly implements:
- Status code tracking with chaining support via
status()- Double-send prevention with
responseSentflag- Status propagation to both success and error responses
One consideration: The error thrown when double-sending (Lines 112-113, 127-128) will be caught by the outer promise and may cause confusing error messages. Consider whether these should be logged warnings instead, or if the current behavior is intentional.
228-236: Request will hang if Express-style function neither sends response nor returns a value.When an Express-style function doesn't call
res.success()/res.error()and also returnsundefined, the promise never resolves:Parse.Cloud.define('broken', (req, res) => { // Forgot to call res.success() or return anything console.log('done'); });This will cause the HTTP request to hang until timeout. Consider adding a timeout or at minimum logging a warning to help developers debug this issue.
.then(result => { // For Express-style functions, only send response if not already sent if (theFunction.length >= 2) { if (!responseObject._isResponseSent()) { // If Express-style function returns a value without calling res.success/error if (result !== undefined) { success(result); + } else { + // Log warning and send empty success to prevent hang + logger.warn( + `Cloud function ${functionName} using Express-style (req, res) did not send a response. ` + + `Call res.success() or res.error(), or return a value.` + ); + success(undefined); } - // If no response sent and no value returned, this is an error in user code - // but we don't handle it here to maintain backward compatibility } } else {⛔ Skipped due to learnings
Learnt from: Moumouls Repo: parse-community/parse-server PR: 9883 File: spec/CloudCodeLogger.spec.js:410-412 Timestamp: 2025-10-16T19:27:05.311Z Learning: In spec/CloudCodeLogger.spec.js, the test "should log cloud function triggers using the silent log level" (around lines 383-420) is known to be flaky and requires the extra `await new Promise(resolve => setTimeout(resolve, 100))` timeout after awaiting `afterSavePromise` for reliability, even though it may appear redundant.Learnt from: mtrezza Repo: parse-community/parse-server PR: 9445 File: spec/ParseLiveQuery.spec.js:1340-1375 Timestamp: 2025-05-09T09:59:06.289Z Learning: Tests in the parse-server repository should use promise-based approaches rather than callback patterns with `done()`. Use a pattern where a Promise is created that resolves when the event occurs, then await that promise.Learnt from: EmpiDev Repo: parse-community/parse-server PR: 9770 File: src/triggers.js:446-454 Timestamp: 2025-08-27T09:08:34.252Z Learning: When analyzing function signature changes in Parse Server codebase, verify that call sites are actually incorrect before flagging them. Passing tests are a strong indicator that function calls are already properly aligned with new signatures.spec/CloudCode.spec.js (3)
4910-4918: Good test coverage for async Express-style functions.The test correctly validates that async functions work with the Express-style
(req, res)signature.
4894-4908: Good backward compatibility test coverage.Both traditional single-parameter and implicit return function styles are validated. This ensures existing cloud functions continue to work after the change. Based on learnings, the async/await pattern used here is appropriate for new tests.
4930-4948: Test properly handles user session cleanup.Good practice to call
Parse.User.logOut()after the test to clean up the session state.src/cloud-code/Parse.Cloud.js (2)
110-143: Documentation is clear and provides helpful examples.The updated documentation clearly explains both traditional and Express-style patterns with practical code examples. The examples for custom status codes (201, 401) demonstrate real-world use cases.
808-820: FunctionRequest interface documentation is comprehensive.The updated interface correctly documents all available properties on the request object. This provides helpful information for developers writing cloud functions.
|
@copilot Add setting custom headers via the new express-style response. |
…res.header() Co-authored-by: mtrezza <[email protected]>
Added in commit 5fc7b58. Custom headers can now be set using Example: Parse.Cloud.define('myFunction', (request, response) => {
response
.status(201)
.header('X-Resource-Id', '12345')
.success({ created: true });
}); |
Parse.Cloud.define(_, (request, response) => {});Parse.Cloud.define(_, (request, response) => {});
Parse.Cloud.define(_, (request, response) => {});|
@copilot fix the lint issue |
…ntation Co-authored-by: mtrezza <[email protected]>
# [9.1.0-alpha.2](9.1.0-alpha.1...9.1.0-alpha.2) (2025-12-14) ### Features * Add support for custom HTTP status code and headers to Cloud Function response with Express-style syntax ([#9980](#9980)) ([8eeab8d](8eeab8d))
|
🎉 This change has been released in version 9.1.0-alpha.2 |
# [9.1.0](9.0.0...9.1.0) (2025-12-14) ### Bug Fixes * Cross-Site Scripting (XSS) via HTML pages for password reset and email verification [GHSA-jhgf-2h8h-ggxv](https://github.com/parse-community/parse-server/security/advisories/GHSA-jhgf-2h8h-ggxv) ([#9985](#9985)) ([3074eb7](3074eb7)) ### Features * Add option `logLevels.signupUsernameTaken` to change log level of username already exists sign-up rejection ([#9962](#9962)) ([f18f307](f18f307)) * Add support for custom HTTP status code and headers to Cloud Function response with Express-style syntax ([#9980](#9980)) ([8eeab8d](8eeab8d)) * Log more debug info when failing to set duplicate value for field with unique values ([#9919](#9919)) ([a23b192](a23b192))
|
🎉 This change has been released in version 9.1.0 |
Summary
Fixed ESLint linting errors:
let customHeaders = {}toconst customHeaders = {}since the variable is never reassignedOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.