Skip to content

Conversation

@cexbrayat
Copy link
Member

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.io application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

It's currently possible to use any string as the HTTP method.
As discussed with Pete and @AndrewKushnir it could be interesting to have a more strictly typed API,
and only allow valid HTTP methods (both in lowercase and uppercase to preserve the existing behavior)

What is the new behavior?

This commit tightens the API to only accept one of the possible HTTP verb, either in lowercase or in uppercase (to preserve the existing behavior).

Does this PR introduce a breaking change?

  • Yes
  • No

The HTTP method must now be one of 'GET'|'HEAD'|'POST'|'PUT'|'DELETE'|'CONNECT'|'OPTIONS'|'TRACE'|'PATCH'|'JSONP' (or one of these in lowercase) instead of a string. This helps developers to catch possible typos like http.request('POTS', ...), but can result in compilation errors when updating if you have such typos in your code. In the rare cases where developers used a temporary string variable to store the method, the type of this variable must be updated to be a string literal, for example const method = 'GET' as const.

Other information

Copy link
Member Author

Choose a reason for hiding this comment

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

I exposed the type to reuse it in common/http/testing for RequestMatch, but maybe there is a better way?

Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than having this union everywhere, how about making the HttpMethod type cover both upper and lower case?

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @petebacondarwin. This would really simplify things.

Copy link
Member Author

Choose a reason for hiding this comment

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

I went back and forth on how to declare this, I've changed it to remove the union type 👍

Copy link
Contributor

@jessicajaniuk jessicajaniuk left a comment

Choose a reason for hiding this comment

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

This looks very breaking to me as this service is used by everyone and would require everyone to change to a type. We'd definitely need a migration.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @petebacondarwin. This would really simplify things.

@dylhunn dylhunn added area: common/http Issues related to HTTP and HTTP Client cross-cutting: types labels Dec 1, 2021
@ngbot ngbot bot added this to the Backlog milestone Dec 1, 2021
The current API accepts `string` for the HTTP method.
This commit tightens the API to only accept one of the possible HTTP verb, either in lowercase or in uppercase (to preserve the existing behavior).

BREAKING CHANGE: The HTTP method must now be one of `'GET'|'HEAD'|'POST'|'PUT'|'DELETE'|'CONNECT'|'OPTIONS'|'TRACE'|'PATCH'|'JSONP'` (or one of these in lowercase) instead of a `string`. This helps developers to catch possible typos like `http.request('POTS', ...)`, but can result in compilation errors when updating if you have such typos in your code. In the rare cases where developers used a temporary string variable to store the method, the type of this variable must be updated to be a string literal, for example `const method = 'GET' as const`.
@cexbrayat cexbrayat force-pushed the fix/request-matcher-method branch from 79a0060 to 8fc399e Compare December 1, 2021 18:56
@cexbrayat
Copy link
Member Author

@jessicajaniuk yeah, we were wondering on Slack with Pete and Andrew how much that would be breaking: if you or another Googler can run a presubmit, that would be great! It might not be that bad as most people are probably not using request that much and, if they do, probably call it with a string literal already. Same for tests and RequestMatch. But let's see!

@jessicajaniuk
Copy link
Contributor

@cexbrayat I ran a presubmit and it's definitely breaking. The presubmit failed pretty significantly.

}): Observable<T>;
request<R>(req: HttpRequest<any>): Observable<HttpEvent<R>>;
request(method: string, url: string, options: {
request(method: HttpMethod, url: string, options: {
Copy link
Contributor

Choose a reason for hiding this comment

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

To avoid this being breaking, this should really be string|HttpMethod anywhere HttpMethod would be accepted. This would ensure everything currently still works, but also can be typed.

withCredentials?: boolean;
});
constructor(method: string, url: string, body: T | null, init?: {
constructor(method: HttpMethod, url: string, body: T | null, init?: {
Copy link
Contributor

Choose a reason for hiding this comment

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

I suggest changing method to be string|HttpMethod and then attempting to coerce the string version to the proper HttpMethod option. If it can't be coerced, then we throw. That would be the least breaky and would still allow for strong typing.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's the same as being just string which kind of defeats the point (types arithmetic)

* The outgoing HTTP request method.
*/
readonly method: string;
readonly method: HttpMethod;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing here with string|HttpMethod

@AndrewKushnir
Copy link
Contributor

@cexbrayat just wanted to provide a quick update after team's discussion:

  1. We'd need to run a wider set of tests in Google's codebase to better understand the impact. We'll keep you updated.
  2. Could you please include some information on the source of the HTTP method list (how can we make sure that it's complete)? We'd need to include this information into the commit message.
  3. Having strict list of HTTP method makes it difficult to use custom ones. If I understand correctly, this is not very common (and in some cases not considered a good practice), but we'd need to do some research to make sure we are not restricting valid use-cases. Could you please try to find quotes from HTTP specs on custom methods?
  4. It'd be great to check how this change is aligned with other popular HTTP libs. Could you please help with this item? Here are some quick examples:

Once we get more info based on the items above, we can make a final decision on how to proceed.

@cexbrayat
Copy link
Member Author

@AndrewKushnir Sure

  1. OK let's see
  2. I used the HTTP methods referenced in RFC 7231, + PATCH (RFC 5789) and JSONP.
    The RFC also mentions:
 Additional methods, outside the scope of this specification, have
   been standardized for use in HTTP.  All such methods ought to be
   registered within the "Hypertext Transfer Protocol (HTTP) Method
   Registry" maintained by IANA, as defined in Section 8.1.

The IANA registry defines a lot of other methods with their own RFC, see https://www.iana.org/assignments/http-methods/http-methods.xhtml
3. I don't think something forbids creating a custom HTTP method in the existing RFCs
4. TS lib.dom uses string (as the backing Web IDL uses string as well).
Axios does offer a typed version of request, with all the methods of this PR plusLINK, UNLINK and PURGE (this one is not in the IANA registry). Superagent is using string (in its DT typings).

If this is too breaking, we can definitely settle for a union type HttpMethod|string as @jessicajaniuk suggested: this would still improve DX.

@cexbrayat
Copy link
Member Author

@AndrewKushnir @jessicajaniuk I opened #44363 as an alternative based on a union type, if you want to check how less breaky it is.

@AndrewKushnir
Copy link
Contributor

Just a quick update on:

We'd need to run a wider set of tests in Google's codebase to better understand the impact. We'll keep you updated.

We've ran all affected tests and there are ~23 places that would require a cleanup in Google's codebase. It looks feasible to prepare Google's codebase to this change, but we are trying to evaluate the impact on 3rd party libraries as well. We'll keep this thread updated.

@dylhunn
Copy link
Contributor

dylhunn commented Mar 26, 2022

Catching up on some triage -- it seems we could provide a schematic which adds a cast anywhere one of these methods is called, WDYT?

request('POTS') -> request('POTS' as AnyForTypedHttpMethod)

We'd probably want to land #44363 first, then the migration and eliminate the string types.

@dylhunn dylhunn self-requested a review March 26, 2022 01:07
@josephperrott josephperrott added detected: breaking change PR contains a commit with a breaking change and removed flag: breaking change labels Nov 14, 2022
@thePunderWoman
Copy link
Contributor

@cexbrayat We're going to close this and #44363 for now. We totally see the benefit of the type strictness, but it's just too challenging for us to prioritize landing. As usual, thanks for the effort!

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jul 6, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area: common/http Issues related to HTTP and HTTP Client cla: yes cross-cutting: types detected: breaking change PR contains a commit with a breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants