Skip to content
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

Auto generate api types with openapi specs #443

Merged
merged 12 commits into from
Dec 20, 2023
Merged

Conversation

gregjopa
Copy link
Contributor

@gregjopa gregjopa commented Nov 27, 2023

Summary

PayPal released the Open API 3.0 specs earlier this year: https://github.com/paypal/paypal-rest-api-specifications. Now we can auto-generate the TypeScript types for PayPal API requests and responses. This PR uses openapi-typescript to auto generate the v2 Orders API types and the v1 Subscriptions API types.

Details

These new API types will make it much easier for TypeScript developers to understand the PayPal APIs. It will also make it much easier for us to maintain. We will no longer need to manually write types to stay in sync with what's documented on https://developer.paypal.com/api/rest/.

The best way to understand this change is to take a look at this real world example of writing server-side code in TypeScript that wraps the PayPal APIs: paypal-examples/paypal-sdk-server-side-integration#37

Here's a snippet with wrapping the create order api call for reference:

// use this library to get the types for the request headers, request body, response body, and more
import type {
  CreateOrderRequestBody,
  OrderSuccessResponseBody,
  OrderSuccessResponseBodyMinimal,
  OrderErrorResponseBody,
  CreateOrder,
} from "@paypal/paypal-js";

type CreateOrderSuccessResponse = {
  status: "ok";
  data: OrderSuccessResponseBody | OrderSuccessResponseBodyMinimal;
  httpStatusCode: 200 | 201;
};

type CreateOrderErrorResponse = {
  status: "error";
  data: OrderErrorResponseBody;
  httpStatusCode: number;
};

// the Authorization header is missing from the headers list
type CreateOrderRequestHeaders = Partial<
  CreateOrder["parameters"]["header"]
> & {
  Authorization?: string;
};

type CreateOrderOptions = {
  body: CreateOrderRequestBody;
  headers?: CreateOrderRequestHeaders;
};

export default async function createOrder({
  body,
  headers = {},
}: CreateOrderOptions): Promise<
  CreateOrderSuccessResponse | CreateOrderErrorResponse
> {

  const requestHeaders = {
    "Content-Type": "application/json",
    Authorization: `Bearer ${accessToken}`,
    "Accept-Language": "en_US",
    Prefer: "return=minimal",
    ...headers,
  };

  let response;
  try {
    response = await fetch(`${apiBaseUrl}/v2/checkout/orders`, {
      method: "POST",
      headers: requestHeaders,
      body: JSON.stringify(body),
    });

    const data = await response.json();

    if (response.ok) {
      return {
        status: "ok",
        data:
          requestHeaders.Prefer === "return=minimal"
            ? (data as OrderSuccessResponseBodyMinimal)
            : (data as OrderSuccessResponseBody),
        httpStatusCode: response.status,
      } as CreateOrderSuccessResponse;
    } else {
      return {
        status: "error",
        data,
        httpStatusCode: response.status,
      } as CreateOrderErrorResponse;
    }
  } catch (error) {
    const httpError: HttpErrorResponse =
      error instanceof Error ? error : new Error(defaultErrorMessage);
    httpError.statusCode = response?.status;
    throw httpError;
  }
}

Is this a breaking change?

Yes

How can I test this?

Use this beta to test out the new type contracts.

npm install @paypal/[email protected]

types/tests/openapi.test.ts Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
@gregjopa gregjopa force-pushed the auto-generate-api-types branch from 515a31f to a800cb3 Compare December 7, 2023 19:08
Copy link

codecov bot commented Dec 7, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (350d9d6) 100.00% compared to head (2582d27) 100.00%.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #443   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            2         2           
  Lines          261       261           
  Branches        47        47           
=========================================
  Hits           261       261           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@gregjopa gregjopa changed the title Auto generate types with openapi specs (POC) Auto generate api types with openapi specs Dec 8, 2023
@gregjopa gregjopa marked this pull request as ready for review December 11, 2023 22:42
@gregjopa gregjopa requested a review from a team as a code owner December 11, 2023 22:42
@gregjopa
Copy link
Contributor Author

@paypal/checkout-sdk please review this PR.

Copy link
Contributor

@nbierdeman nbierdeman left a comment

Choose a reason for hiding this comment

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

Wow, so much less to maintain. Nice!!! 🥇

Copy link
Member

@wsbrunson wsbrunson left a comment

Choose a reason for hiding this comment

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

pretty dang cool

@gregjopa gregjopa merged commit efb26f8 into main Dec 20, 2023
4 checks passed
@gregjopa gregjopa deleted the auto-generate-api-types branch December 20, 2023 18:01
@subvertallchris
Copy link

I was so relieved to find this, thank you! It would be great to add the original example in your PR to the docs. It provides great
background and example usage. For anyone struggling with errors related to import, you might need to use import type like this:

import type { CheckoutOrdersV2, CreateOrderRequestBody, OrderResponseBody } from '@paypal/paypal-js';

With an OpenAPI spec, you could go a step further and generate a type-safe API client. Is there any interest in that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants