Parse Server REST API vs Public API

Hi guys,

Could any of the seasoned parse server developers please explain what is the difference between the Parse Server REST API documented here: REST API Guide | Parse and the Parse Server Public API described here: parse-server/PublicAPI.spec.js at master 路 parse-community/parse-server 路 GitHub?

From what I understand, invoking REST API goes via parse server top level mount path like https://myserver/parse, and each HTTP(s) request requires presence of X-Parse-Application-Id and X-Parse-REST-API-Key. Body is supplied as JSON.

Based on the supplied appid, the request is routed to a proper app, potentially running at the same mount path (functionality used by original facebook implementation in the past but now largely redundant thanks to docker containerization).

The public API on the other hand resides at mountpath + /apps + /appid, like this: https://myserver/parse/apps/appid and it鈥檚 requires body supplied as form data.

The public API for now exposes endpoints to

  • verify user鈥檚 email
  • change user鈥檚 password based on token previously requested via REST API /requestPasswordReset
  • reset verification email
  • ?? other ??

I wonder why these two are separate and whether they could potentially be merged?

Any insights?
thanks, Martin

You already got it right.

The PublicAPI is simply exposing the endpoints for email verification and password reset. In addition, it serves static assets in the /public directory.

It is not necessary to merge the PublicAPI with the 鈥淧arse API鈥 because they are already one, it is simply a matter of terminology. The endpoints in the PublicAPI are served by the PublicAPIRouter.js, which is just one of many routers, not a separate API. The PublicAPI is also quite versatile, it considers data in the request query, header and body, depending on the step in the email verification flow, for example.

I think the historic reason why this is a dedicated router is because - unlike other Parse Server endpoints - these endpoints serve HTML files and other static assets. They intersect with custom assets served in the /public directory. Hence they have been grouped into this common router.

The PublicAPIRouter will be deprecated soon and replaced by the PagesRouter, which does not contain the word API anymore, to make its name more descriptive according to its purpose. In the PagesRouter, the benefits of keeping these endpoints grouped in a dedicated router also become more obvious.

The PagesRouter is a completely revamped router that has many improvements over the current PublicAPIRouter, most notably:

  • Ability to inject dynamic content into any served HTML site that could previously only be served as static asset.
  • Localization of feature pages (password reset, email verification) and custom pages (any HTML page). This was the last piece to make Parse Server fully localizable as it plays nicely with the Parse Server API Mail Adapter that allows to localize emails.
  • Reduces data traffic by directly serving webpages instead of serving redirects.
  • The router has been restructured to be easily extended for other features such as a change email process, which is in discussion.

The PagesRouter will also easily allow for the username to be removed from the password reset and email verification URLs, which is currently in discussion, feel free to join in.

The PagesRouter is currently in experimental state but it has been tested thoroughly for some time already and you can feel free to enable it in non-critical environments. Your feedback is much appreciated.

For more details, see the Parse Server docs or the related PR.

Note: future development will likely only happen in the PagesRouter instead of in the PublicAPIRouter, due to its future depreciation.

Thanks for the explanation @Manuel. The Pages Router looks like a step in the right direction.

I guess my confusion comes from the fact that you have to initiate the email verification and password reset via REST API by using POST to:

  • /parse/verificationEmailRequest (or should it be called requestEmailVerification???)
  • /parse/requestPasswordReset (or should be called passwordResetEmailRequest ???)

and it can not therefore be triggered by simply including a link somewhere, you need to actually do at minimum XHR POST.

And then you actually verify the email and change the password by going via (now called) Public API:

  • /parse/apps/APP_ID/request_password_reset with token, new_password, and username
  • /parse/apps/APP_ID/verify_email with token, and username

This is mostly where my confusion comes from.

Also, what I noted going via all these, there is currently no way to initiate password reset via simple GET request. Correct?

Looks like GET/POST to /parse/apps/APP_ID/request_password_reset both expect token, username, and new_password to actually change password.

is my understanding correct?
Martin

This is historic terminology, which as not been changed as part of the PagesRouter for compatibility reasons. They are defined in the internal UsersRouter and have to be changed there, not in the PagesRouter / PublicAPIRouter, and changing them possibly requires also changes in Parse client SDKs, so I鈥檓 not sure yet whether it鈥檚 worth the effort. But you are right, it makes sense to review the endpoint names, this is already a potential ToDo in the PR when taking the feature out of experimental status.

It is a combination of GET and POST requests (for the new PagesRouter):

In the old PublicAPIRouter the routes are more complicated (more routes, like choose_password) because the router cannot directly respond with webpage content in some cases and requires redirects to maintain the PRG pattern for POST requests. This has been dramatically simplified in the PagesRouter.

You initiate the password reset flow with GET, see example URLs above. While it would be technically possible to also set a new password using a GET request, it would be semantically incorrect and violate idempotency principles as well as RFC 2616:

Authors of services which use the HTTP protocol SHOULD NOT use GET based forms for the submission of sensitive data, because this will cause this data to be encoded in the Request-URI.

See my example URLs above. The GET request requires username (soon to be removed) and token. The POST request requires username (soon to be removed), token, new password.

Thanks for the explanation.

Now what would be the easiest way to create a static page where a user can request his/her password to be reset? An input field and button.

If I understand the scheme correctly, you will need to create a simple html page with form to enter email address and on submit perform AJAX request to /parse/requestPasswordReset (with appropriate HTTP headers) and handle the JSON response to indicate that email has been sent.

After the email is sent, all logic and page rendering is handled via Parse Server, either via Public API or new Pages Router, but the Step 1 needs to be done separately, correct?

What I鈥檓 looking for is a simpler way to trigger the password reset.

For example like this:

  1. GET /parse/apps/APP_ID/request_password_reset without any query params will render a custom page (or send a redirect to custom page) where user can enter email address for which to reset password.

  2. GET /parse/apps/APP_ID/request_password_reset?username=XXX will render a custom page (or send a redirect to custom page) that instructions were sent to email address.

  3. GET /parse/apps/APP_ID/request_password_reset?username=XXX&token=YYY will render a custom page (or send a redirect to custom page) where to enter the new password.

  4. POST /parse/apps/APP_ID/request_password_reset?username=XXX&token=YYY&new_password will change a password and then render (or send a redirect to custom page) that password was changed.

For now 3 and 4 are handled by the Parse Server. I did not find examples of 1 and 2 anywhere.

Am I missing something here?
Martin

Essentially what I鈥檓 looking for is a functionality already implemented for email verification logic here: parse-server/PublicAPIRouter.js at e6ac3b6932186a6a85601c65143305f86f63e948 路 parse-community/parse-server 路 GitHub

You can initiate email (re-)verification by simple POST to /parse/apps/APP_ID/resend_verification_email and get a 302 redirect to custom page invalidLink when username is missing, send failed when something went wrong, or send succeeded when verification email was sent.

If I understand it correctly, there is no way to do the same for password reset for now.

Correct?
Martin

Your analysis is correct.

For a hypothetical web app, the user would enter the email verification flow with a API request (e.g. click on 鈥渞e-send verification email鈥 button) but the password reset flow with an empty web form.

The current logic requires an API request before displaying the web form, in which the generated token has to be already present. You could write a custom route and emulate that request before processing the web form, therefore receiving the webform first, then generating an 鈥渁libi鈥 token, which is actually useless at that point and just satisfies the logic.

I will try to make a PR to simplify this once I get the git #master of parse server running in Docker so that I can start making changes. I鈥檓 now stuck at getting my app to depend on forked version of parse-server.

thanks,
Martin

The PR I would suggest is the following:

It would be an easy extension to modify the POST endpoint that the form is using to also accept a session token alternatively to a password reset token. This way a user that is logged in could visit a form and change the password without a previous API request.

I have started with a PR that modifies the /request_password_reset endpoint to accept POST with URL encoded username to trigger the flow and redirect to a newly introduced customPages URL called passwordResetInitiated (defaulting to password_reset_initiated.html).

It鈥檚 just a start to avoid using Javascript and sticking to plain old HTML to trigger the password reset flow, the same way we can trigger resend of the verification email.

I was thinking about the sessionToken use, and because I think it is a good security measure to anyway validate old password before changing to the new password, it is not enough to just present a session token. To directly change password, one would probably need to present an old password, and a new password at the same time.

thoughts?
Martin

Let鈥檚 continue the PR related discussion in the PR.