StepZen gives developers the ability to turn any REST API into a GraphQL API with the power of the @rest directive. However, this requires figuring out how to map the JSON response of a REST API into a GraphQL schema that StepZen understands. StepZen's JSON2SDL tool transforms a JSON response into GraphQL by generating the schema for you in GraphQL's Schema Definition Language.

In this example, we'll look at JSON responses from the Stripe API and convert them into GraphQL schemas. Our use case is a customer management platform. Imagine you are creating an internal app that allows the customers on your site to view their balances and charges.

With the JSON2SDL tool we can quickly create GraphQL schemas that are deployed to a unified endpoint. This endpoint can be queried either directly with API calls or from a UI created with a frontend framework that connects to that GraphQL endpoint.

Project Structure

I'll provide a brief description of the different files required for this project. For a more in depth explanation of how to structure your StepZen project, see Write Schema in Code in our documentation.

Schema Files

The Stripe API includes a huge range of information that can be queried from many different endpoints. We'll only focus on a few specific endpoints that provide information about the customer's balance and charges. We'll need to convert two different JSON objects into GraphQL schemas with JSON2SDL.

Those schemas are placed into files in our StepZen project called balance.graphql and charge.graphql. It is useful to separate our concerns into different files so that as our project grows we can more easily navigate the different types and queries present in our complete schema.

Graph Entry Point

In a new blank project, create a directory called schema and create two files inside that directory called balance.graphql and charge.graphql respectively. You must create a file named index.graphql in your working directory to tell StepZen what files hold your types and queries.

Your schema files can be organized however you want, but in this example each type that is provided by JSON2SDL has its own schema file inside a folder called schema. The following code includes the two schema files we have created in our schema file.

# index.graphql

schema @sdl(
  files: [
    "schema/charge.graphql",
    "schema/balance.graphql"
  ]
) {
  query: Query
}

Configuration Files

The other necessary files we need to create are .gitignore to ensure that we don't commit our API keys to our version control system and config.yaml for loading our API keys that authenticate our requests to the Stripe API.

The Stripe API uses API keys to authenticate requests that are viewable in the Stripe Dashboard. We need to use these keys in the config.yaml file that StepZen uses to manage secrets and other configuration information.

configurationset:
  - configuration:
      name: stripe_config
      Authorization: Bearer xxxx

Replace xxxx with your own API key that starts with sk_test_. Also make sure to include any files you don't want to commit such as config.yaml and node_modules in the .gitignore file.

Complete Project Structure

Once you have created all of these files our complete structure looks like so:

/
├── schema
│   ├── balance.graphql
│   └── charge.graphql
├── .gitignore
├── config.yaml
└── index.graphql

Transform JSON to GraphQL

We are now ready to convert JSON objects into GraphQL schemas.

View Account Balance

The balance endpoint returns attributes including available funds that are transferred or paid out and pending funds that are not yet available in the balance. You can find an example balance object in the Stripe documentation.

{
  "object": "balance",
  "available": [
    {
      "amount": 0,
      "currency": "usd",
      "source_types": {
        "card": 0
      }
    }
  ],
  "livemode": false,
  "pending": [
    {
      "amount": 0,
      "currency": "usd",
      "source_types": {
        "card": 0
      }
    }
  ]
}

Copy the JSON object and paste it into the Enter JSON field at JSON2SDL.com. Click Convert to see the SDL output.

JSON2SDL editor screenshot with example code

After plugging the object into the JSON2SDL convertor, copy the SDL and include it in the balance.graphql file.

# schema/balance.graphql

type Source_types {
  card: Int
}

type OneAvailable {
  amount: Int
  currency: String
  source_types: Source_types
}

type Source_types_G8 {
  card: Int
}

type OnePending {
  amount: Int
  currency: String
  source_types: Source_types_G8
}

type BalanceRoot {
  available: [OneAvailable]
  livemode: Boolean
  object: String
  pending: [OnePending]
}

The only change we needed to make was renaming the Root object to BalanceRoot. If we want to query for the balance, we write a GraphQL query type called getBalance that returns the BalanceRoot type. This is accomplished with the @rest directive, a custom StepZen directive for connecting to REST APIs.

# schema/balance.graphql

type Query {
  getBalance: BalanceRoot
    @rest(
      endpoint: "https://api.stripe.com/v1/balance"
      configuration: "stripe_config"
    )
}

endpoint determines the REST endpoint that is used to populate the query result. configuration tells StepZen which configuration provided in config.yaml to use for this endpoint.

Charge a Credit Card or Debit Card

The charge endpoint is used to charge a credit or debit card. Individual charges are retrieved or refunded and all charges can be listed. Charges are identified by a unique, random ID. As before, you can find an example charge object in the Stripe documentation.

{
  "id": "ch_3KJkTAL1QJOeRmDV0Ehnez2i",
  "object": "charge",
  "amount": 100,
  "amount_captured": 0,
  "amount_refunded": 0,
  "application": null,
  "application_fee": null,
  "application_fee_amount": null,
  "balance_transaction": "txn_3KJkTAL1QJOeRmDV0mbUs6O1",
  "billing_details": {
    "address": {
      "city": null,
      "country": null,
      "line1": null,
      "line2": null,
      "postal_code": null,
      "state": null
    },
    "email": null,
    "name": null,
    "phone": null
  },
  "calculated_statement_descriptor": null,
  "captured": false,
  "created": 1642622964,
  "currency": "usd",
  "customer": null,
  "description": "My First Test Charge (created for API docs)",
  "disputed": false,
  "failure_code": null,
  "failure_message": null,
  "fraud_details": {},
  "invoice": null,
  "livemode": false,
  "metadata": {},
  "on_behalf_of": null,
  "order": null,
  "outcome": null,
  "paid": true,
  "payment_intent": null,
  "payment_method": "card_1KJkTAL1QJOeRmDV7sxsWJOx",
  "payment_method_details": {
    "card": {
      "brand": "visa",
      "checks": {
        "address_line1_check": null,
        "address_postal_code_check": null,
        "cvc_check": "pass"
      },
      "country": "US",
      "exp_month": 8,
      "exp_year": 2023,
      "fingerprint": "SgEpKVoEKEudtzOa",
      "funding": "credit",
      "installments": null,
      "last4": "4242",
      "network": "visa",
      "three_d_secure": null,
      "wallet": null
    },
    "type": "card"
  },
  "receipt_email": null,
  "receipt_number": null,
  "receipt_url": "https://pay.stripe.com/receipts/acct_1CVAo5L1QJOeRmDV/ch_3KJkTAL1QJOeRmDV0Ehnez2i/rcpt_KzjxJkKeQFHK1TdA6oCeLMZc8XIOfaL",
  "refunded": false,
  "refunds": {
    "object": "list",
    "data": [],
    "has_more": false,
    "url": "/v1/charges/ch_3KJkTAL1QJOeRmDV0Ehnez2i/refunds"
  },
  "review": null,
  "shipping": null,
  "source_transfer": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "succeeded",
  "transfer_data": null,
  "transfer_group": null
}

After copying this into JSON2SDL we receive the following schema (as before we rename Root to ChargeRoot):

# schema/charge.graphql

type Address {
  city: JSON
  country: JSON
  line1: JSON
  line2: JSON
  postal_code: JSON
  state: JSON
}

type Billing_details {
  address: Address
  email: JSON
  name: JSON
  phone: JSON
}

type Checks {
  address_line1_check: JSON
  address_postal_code_check: JSON
  cvc_check: String
}

type Card {
  brand: String
  checks: Checks
  country: String
  exp_month: Int
  exp_year: Int
  fingerprint: String
  funding: String
  installments: JSON
  last4: String
  network: String
  three_d_secure: JSON
  wallet: JSON
}

type Payment_method_details {
  card: Card
  type: String
}

type Refunds {
  data: JSON
  has_more: Boolean
  object: String
  url: String
}

type ChargeRoot {
  amount: Int
  amount_captured: Int
  amount_refunded: Int
  application: JSON
  application_fee: JSON
  application_fee_amount: JSON
  balance_transaction: String
  billing_details: Billing_details
  calculated_statement_descriptor: JSON
  captured: Boolean
  created: Int
  currency: String
  customer: JSON
  description: String
  disputed: Boolean
  failure_code: JSON
  failure_message: JSON
  fraud_details: JSON
  id: String
  invoice: JSON
  livemode: Boolean
  metadata: JSON
  object: String
  on_behalf_of: JSON
  order: JSON
  outcome: JSON
  paid: Boolean
  payment_intent: JSON
  payment_method: String
  payment_method_details: Payment_method_details
  receipt_email: JSON
  receipt_number: JSON
  receipt_url: String
  refunded: Boolean
  refunds: Refunds
  review: JSON
  shipping: JSON
  source_transfer: JSON
  statement_descriptor: JSON
  statement_descriptor_suffix: JSON
  status: String
  transfer_data: JSON
  transfer_group: JSON
}

To query for the charges, create a query type called getCharges that returns the ChargeRoot type.

# schema/charge.graphql

type Query {
  getCharges: [ChargeRoot]
    @rest(
      endpoint: "https://api.stripe.com/v1/charges"
      configuration: "stripe_config"
    )
}

Deploy and Query API Endpoint

Make sure you have the StepZen CLI installed and deploy your endpoint with stepzen start.

stepzen start

Use the following GraphQL query to query for the available balance and the pending balance:

query GET_BALANCE {
  getBalance {
    available {
      amount
      currency
    }
    pending {
      amount
      currency
    }
  }
}

This outputs the following:

{
  "data": {
    "getBalance": {
      "available": [
        {
          "amount": 0,
          "currency": "usd"
        }
      ],
      "pending": [
        {
          "amount": 0,
          "currency": "usd"
        }
      ]
    }
  }
}

Use the following GraphQL query to query for the amount of charges:

query GET_CHARGES {
  getCharges {
    data {
      amount
    }
  }
}

This outputs the following:

{
  "data": {
    "getCharges": [
      {
        "data": [
          {
            "amount": 100
          }
        ]
      }
    ]
  }
}

Connecting a Frontend Client

With our GraphQL API created and deployed on StepZen, we are able to hook up any kind of frontend framework we want. For a range of samples with projects such as Next.js, Gatsby, Redwood, Nuxt.js, and SvelteKit, see the StepZen Examples repository.

Many of these frameworks include built-in abstractions for serverless functions and API routes. This gives the developer the ability to authenticate their frontend client without exposing their API keys.

Conclusion

We have seen that Stripe's API returns a wide range of different objects with many different attributes that can be queried. With StepZen's JSON2SDL tool it is much more manageable to query for individual fields on these objects without having to dive into the minutiae of Stripe's REST API.

With this GraphQL endpoint you are able to send queries for any part of the data that you want to query. A frontend application can be connected to your endpoint allowing an end user to query for their data without needing to write any code themselves.

Want to learn more about StepZen? Try it out here or ask any question on Discord.