Convert the Stripe API into a GraphQL Schema with the JSON2SDL Tool
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.
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, from the Explorer in the StepZen dashboard or from your GraphQL client:
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.