SlideShare a Scribd company logo
1 of 46
Download to read offline
Building custom GraphQL
tooling for your team
Sashko Stubailo
Engineering Manager, Dashboard Discovery
@stubailo, sashko@stripe.com
1. What has made GraphQL impactful in the
Stripe Dashboard
2. Novel tools we've built internally
3. How you can build your own GraphQL tooling
Everything I'm about to present has been a
team effort at Stripe!
It takes a village to create a great
development environment.
Developing in the
Stripe Dashboard
• Lots of product teams
contributing at once
• Needs to work together as a
unified whole
S T R I P E D A S H B O A R D S TA C K
• React
• Flow
• Jest
• Webpack
• Ruby
• GraphQL and Apollo
PA R T 1
What does it take to make
product development
better at scale?
P R O D U C T D E V E L O P M E N T
Frontend
API
Backend
P R O D U C T D E V E L O P M E N T
Components
API
Backend
State
management
Tests
Backend
Component
explorer
Editor
CI
Monitoring
Type
generation
P R O D U C T D E V E L O P M E N T
Components
GraphQL API
Backend
Apollo
State Management
Tests
Backend
Component
explorer
Editor
CI
Monitoring
Type
generation
P R O D U C T D E V E L O P M E N T
Components
GraphQL API
Backend
Apollo
State Management
Tests
Backend
Component
explorer
Editor
CI
Monitoring
Type
generation
GQL
GQL
GQL
GQL
GQL
GQL
GQL
Components
GraphQL API
Backend
Apollo
State Management
Tests
Backend
Component
explorer
Editor
CI
Monitoring
Type
generation
GQL
GQL
GQL
GQL
GQL
GQL
GQL
The most impactful technologies improve
all parts of the product development process.
Main benefit of GraphQL for us:
The wealth of community tools
and content
T O O L S W E U S E M O S T LY O F F T H E S H E L F :
• Apollo Client: Simplifies data fetching and
management in React
• GraphQL Ruby: Makes it easy to build a typed API
• Apollo CLI: Static type generation
• graphql-tools: Easy data mocking
Content and documentation
Less content to maintain, easier to onboard new engineers
Why does GraphQL have such a big impact?
The schema defines capabilities
type Author {
id: Int!
firstName: String
lastName: String
posts: [Post]
}
type Post {
id: Int!
title: String
author: Author
votes: Int
}
type Query {
posts: [Post]
author(id: ID!): Author
}
query PostsForAuthor {
author(id: "1") {
firstName
posts {
title
votes
}
}
}
Query describes requirements
What makes it possible to build great tools?
• Tooling integrates between frontend and backend
• Can rely on schema being present and correct
• Stable spec and ecosystem means you can build tools once and
they work for a long time
The last point is why it actually makes sense to have internal tooling
around GraphQL!
PA R T 2
Tools at Stripe
Tools at Stripe: Mocking
Write unit tests and examples without
having to call a real API
• Faster, more resilient tests
• Possible to develop components
before backend is built
• Easy to generate edge case states
Frontend
Fake API
GraphQL enables automatic mocking
Because GraphQL is strongly typed, libraries like graphql-tools can
generate correctly-shaped mock results automatically for any query.
Any valid
query
Schema + mocks
for each type
Result of the
correct shape
What about edge cases?
A single globally mocked schema is convenient, but isn't well suited
to test specific pathological cases:
• Error states
• Loading states
• Rendering specific values
Per-request
mocking?
Allows you to specify
edge cases, but a lot of
boilerplate for each test.
Example from the Apollo docs →
Best of both worlds: Global mocking with overrides
const mocks = {
Todo: () => ({
text: () => faker.sentence(),
completed: () => faker.boolean(),
}),
User: () => ({
name: () => faker.findName()
})
}
const customResolvers = {
Query: () => ({
todoItems: [
{ completed: true },
{ completed: false },
]
})
};
G L O B A L M O C K S O V E R R I D E S
Now, results are automatically filled in from
global mocks, but we can override specific
values for individual tests.
type Todo {
text: String
completed: Boolean
user: User
}
type User {
name: String
}
type Query {
todoItems: [Todo]
}
S C H E M A
Example: default mocks
In this example, we're just trying to check if the component renders
correctly. The specific data isn't important, so we don't need to
specify anything.
const wrapper = mount(
<ApolloTestProvider>
<ConnectOverviewPage />
</ApolloTestProvider>,
);
expect(wrapper.find('ConnectLandingPage'))
.toHaveLength(1);
Example:
Overriding fields
We want to assert for
specific values in the
rendered component.
We don't want to rely on
values in the global mocks, so
we specify them in the test.
it('renders populated state', async () => {
const customResolvers = {
Merchant: () => ({
balanceSummaries: () => [{
currency: 'usd',
available: 1000,
pending: 2000,
}],
defaultCurrency: () => 'usd',
}),
};
const wrapper = mount(
<ApolloTestProvider
customResolvers={customResolvers}>
<ApolloExample />
</ApolloTestProvider>,
);
await jestHelpers.asyncWait();
const text = wrapper.text();
expect(text).toMatch(/Default currency: usd/);
expect(text).toMatch(/Currency.*Available.*Pending/);
expect(text).toMatch(/$10.*$20/);
});
Mock for loading/error states
We've also added helpers for a very common type of edge case:
Errors and loading state.
it('renders loading state', async () => {
const wrapper = mount(
<LoadingProvider>
<ApolloExample />
</LoadingProvider>,
);
await jestHelpers.asyncWait();
expect(wrapper.text())
.toBe('Loading...');
});
it('renders error state', async () => {
const wrapper = mount(
<ErrorProvider>
<ApolloExample />
</ErrorProvider>,
);
await jestHelpers.asyncWait();
expect(wrapper.text())
.toBe('Error! Oh no!');
});
GraphQL mocks for prototyping
Designers and developers can combine our component system with
fake data with no additional effort.
Mocking overview
✅ Automatic global mocks
✅ Specific overrides to test edge cases
✅ Error states
✅ Loading states
✅ Built in mocks for prototypes
Read the blog post at bit.ly/graphql-mocking
Tools at Stripe:
Schema management
• There's one GraphQL schema for the Dashboard
• Anyone at Stripe should be able to independently make changes
• We want to rely on tooling to prevent breakages
Our schema.graphql file
encodes the current state of
the schema
It's automatically generated
from the API code and
checked in to Git
Detecting breaking
changes in CI
Frontend builds stay in the
wild for a while, so we need to
be carefun about backwards
compatibility.
Custom GraphQL tools
• Stable: Built on a standard spec
• Convenient: Leverage community tools like
GraphQL.js, Apollo Codegen, and Babel
• Tailored: Uniquely designed for our team, codebase,
and development environment
PA R T 3
Building our own
tooling
Let's build a GraphQL field usage finder together
How would we build a tool that searches our codebase for usage of
a specific GraphQL field?
.jsx
.jsx
.jsx
User.name
?
GraphQL
Schema
Let's build a GraphQL field usage finder together
How would we build a tool that searches our codebase for usage of
a specific GraphQL field?
1. Get the GraphQL schema
2. Find queries in our codebase
3. Search those queries for usage of the desired field
Getting our schema
Super easy using graphql-config. With a .graphqlconfig in your repo:
const { getGraphQLProjectConfig } = require("graphql-config");
const schema = getGraphQLProjectConfig().getSchema();
Accessing it is as easy as:
{
"schemaPath": "src/graphql/schema.graphql"
}
Finding GraphQL queries in the codebase
If we're using graphql-tag, we can look for gql template literals:
github.com/apollographql/graphql-tag
const ExampleQuery = gql`
query ExampleQuery {
currentMerchant {
id, defaultCurrency
balanceSummaries {
currency, available, pending
}
}
}
`;
Finding GraphQL queries in the codebase
const fs = require("fs");
const { parse } = require("@babel/parser");
// Read a file
const fileContents = fs.readFileSync(filename, { encoding: "utf-8" });
// Create an AST by parsing the file using Babel
const ast = parse(fileContents);
Using Babel to create an Abstract Syntax Tree (AST):
Finding GraphQL queries in the codebase
const traverse = require("@babel/traverse").default;
const graphqlStrings = [];
traverse(ast, {
TaggedTemplateExpression: function(path) {
if (path.node.tag.name === "gql") {
graphqlStrings.push(path.node.quasi.quasis[0]);
}
}
});
Looking for TaggedTemplateExpressions named "gql":
Useful tool: AST Explorer
Searching for fields in each query
const { parse } = require("graphql");
const ast = parse(jsNode.value.raw);
Parsing the GraphQL query:
Searching for fields in each query
const { visit, visitWithTypeInfo, TypeInfo } = require("graphql");
const typeInfo = new TypeInfo(schema);
visit(ast, visitWithTypeInfo(typeInfo, {
Field(graphqlNode) {
const fieldName = typeInfo.getParentType().name +
"." + graphqlNode.name.value;
// Now, check if it's the field we're looking for
}
}));
Looking through all the fields:
Searching for fields in each query
if (fieldName === fieldWeAreLookingFor) {
const operationName = ast.definitions[0].name.value,
const line = jsNode.loc.start.line;
console.log(`${filename}:${line} ${operationName}`);
}
Compare to the field we're looking for and print results:
Searching for fields in each query
const { parse, visit, visitWithTypeInfo, TypeInfo } = require("graphql");
const ast = parse(jsNode.value.raw);
const typeInfo = new TypeInfo(schema);
visit(ast, visitWithTypeInfo(typeInfo, {
Field(graphqlNode) {
const fieldName = typeInfo.getParentType().name + "." + graphqlNode.name.value;
if (fieldName === fieldWeAreLookingFor) {
const operationName = ast.definitions[0].name.value,
const line = jsNode.loc.start.line;
console.log(`${filename}:${line} ${operationName}`);
}
}
}));
The whole GraphQL part:
Putting it all together
Once we add some plumbing to read in files and accept an
argument, it looks like this:
$ node graphql-field-finder.js Query.user
src/components/customers/CardsSection.js:40 AllCardsQuery
src/graphql/queries/UnauthedClientsQuery.js:13 UnauthedClientsQuery
src/user_settings/TwoFactorAuthenticationSettings.js:46 TwoFactorSettingsQuery
tests/functional/full_flows_test.jsx:401 UnauthedClientsQuery
tests/lib/ApolloTestProvider.test.jsx:13 ApolloTestProviderQuery
tests/lib/ApolloTestProvider.test.jsx:49 ApolloTestProviderUsernameQuery
R E C A P : W H AT W E U S E D
• graphql-config to get the schema
• graphql-tag to identify queries
• Babel to extract queries from code
• GraphQL.js to find fields in queries
See the code at bit.ly/graphql-field-finder
Building custom GraphQL tooling for your team
Sashko Stubailo
@stubailo, sashko@stripe.com
We're hiring at Stripe in Dublin, SF, Singapore,
Seattle, and remote in North America!
Go forth and build your own GraphQL tools to make product
development better at your company!
Get this presentation: bit.ly/custom-graphql-tooling

More Related Content

Recently uploaded

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 

Recently uploaded (20)

MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
الأمن السيبراني - ما لا يسع للمستخدم جهله
الأمن السيبراني - ما لا يسع للمستخدم جهلهالأمن السيبراني - ما لا يسع للمستخدم جهله
الأمن السيبراني - ما لا يسع للمستخدم جهله
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
The Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightThe Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and Insight
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Stronger Together: Developing an Organizational Strategy for Accessible Desig...
Stronger Together: Developing an Organizational Strategy for Accessible Desig...Stronger Together: Developing an Organizational Strategy for Accessible Desig...
Stronger Together: Developing an Organizational Strategy for Accessible Desig...
 
Quantum Leap in Next-Generation Computing
Quantum Leap in Next-Generation ComputingQuantum Leap in Next-Generation Computing
Quantum Leap in Next-Generation Computing
 
JavaScript Usage Statistics 2024 - The Ultimate Guide
JavaScript Usage Statistics 2024 - The Ultimate GuideJavaScript Usage Statistics 2024 - The Ultimate Guide
JavaScript Usage Statistics 2024 - The Ultimate Guide
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Top 10 CodeIgniter Development Companies
Top 10 CodeIgniter Development CompaniesTop 10 CodeIgniter Development Companies
Top 10 CodeIgniter Development Companies
 
API Governance and Monetization - The evolution of API governance
API Governance and Monetization -  The evolution of API governanceAPI Governance and Monetization -  The evolution of API governance
API Governance and Monetization - The evolution of API governance
 
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
 
Choreo: Empowering the Future of Enterprise Software Engineering
Choreo: Empowering the Future of Enterprise Software EngineeringChoreo: Empowering the Future of Enterprise Software Engineering
Choreo: Empowering the Future of Enterprise Software Engineering
 
Intro to Passkeys and the State of Passwordless.pptx
Intro to Passkeys and the State of Passwordless.pptxIntro to Passkeys and the State of Passwordless.pptx
Intro to Passkeys and the State of Passwordless.pptx
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 

Featured

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
ThinkNow
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Building custom GraphQL tooling for your team

  • 1. Building custom GraphQL tooling for your team Sashko Stubailo Engineering Manager, Dashboard Discovery @stubailo, sashko@stripe.com
  • 2. 1. What has made GraphQL impactful in the Stripe Dashboard 2. Novel tools we've built internally 3. How you can build your own GraphQL tooling
  • 3. Everything I'm about to present has been a team effort at Stripe! It takes a village to create a great development environment.
  • 4. Developing in the Stripe Dashboard • Lots of product teams contributing at once • Needs to work together as a unified whole
  • 5. S T R I P E D A S H B O A R D S TA C K • React • Flow • Jest • Webpack • Ruby • GraphQL and Apollo
  • 6. PA R T 1 What does it take to make product development better at scale?
  • 7. P R O D U C T D E V E L O P M E N T Frontend API Backend
  • 8. P R O D U C T D E V E L O P M E N T Components API Backend State management Tests Backend Component explorer Editor CI Monitoring Type generation
  • 9. P R O D U C T D E V E L O P M E N T Components GraphQL API Backend Apollo State Management Tests Backend Component explorer Editor CI Monitoring Type generation
  • 10. P R O D U C T D E V E L O P M E N T Components GraphQL API Backend Apollo State Management Tests Backend Component explorer Editor CI Monitoring Type generation GQL GQL GQL GQL GQL GQL GQL
  • 12. Main benefit of GraphQL for us: The wealth of community tools and content
  • 13. T O O L S W E U S E M O S T LY O F F T H E S H E L F : • Apollo Client: Simplifies data fetching and management in React • GraphQL Ruby: Makes it easy to build a typed API • Apollo CLI: Static type generation • graphql-tools: Easy data mocking
  • 14. Content and documentation Less content to maintain, easier to onboard new engineers
  • 15. Why does GraphQL have such a big impact? The schema defines capabilities type Author { id: Int! firstName: String lastName: String posts: [Post] } type Post { id: Int! title: String author: Author votes: Int } type Query { posts: [Post] author(id: ID!): Author } query PostsForAuthor { author(id: "1") { firstName posts { title votes } } } Query describes requirements
  • 16. What makes it possible to build great tools? • Tooling integrates between frontend and backend • Can rely on schema being present and correct • Stable spec and ecosystem means you can build tools once and they work for a long time The last point is why it actually makes sense to have internal tooling around GraphQL!
  • 17. PA R T 2 Tools at Stripe
  • 18. Tools at Stripe: Mocking Write unit tests and examples without having to call a real API • Faster, more resilient tests • Possible to develop components before backend is built • Easy to generate edge case states Frontend Fake API
  • 19. GraphQL enables automatic mocking Because GraphQL is strongly typed, libraries like graphql-tools can generate correctly-shaped mock results automatically for any query. Any valid query Schema + mocks for each type Result of the correct shape
  • 20. What about edge cases? A single globally mocked schema is convenient, but isn't well suited to test specific pathological cases: • Error states • Loading states • Rendering specific values
  • 21. Per-request mocking? Allows you to specify edge cases, but a lot of boilerplate for each test. Example from the Apollo docs →
  • 22. Best of both worlds: Global mocking with overrides const mocks = { Todo: () => ({ text: () => faker.sentence(), completed: () => faker.boolean(), }), User: () => ({ name: () => faker.findName() }) } const customResolvers = { Query: () => ({ todoItems: [ { completed: true }, { completed: false }, ] }) }; G L O B A L M O C K S O V E R R I D E S Now, results are automatically filled in from global mocks, but we can override specific values for individual tests. type Todo { text: String completed: Boolean user: User } type User { name: String } type Query { todoItems: [Todo] } S C H E M A
  • 23. Example: default mocks In this example, we're just trying to check if the component renders correctly. The specific data isn't important, so we don't need to specify anything. const wrapper = mount( <ApolloTestProvider> <ConnectOverviewPage /> </ApolloTestProvider>, ); expect(wrapper.find('ConnectLandingPage')) .toHaveLength(1);
  • 24. Example: Overriding fields We want to assert for specific values in the rendered component. We don't want to rely on values in the global mocks, so we specify them in the test. it('renders populated state', async () => { const customResolvers = { Merchant: () => ({ balanceSummaries: () => [{ currency: 'usd', available: 1000, pending: 2000, }], defaultCurrency: () => 'usd', }), }; const wrapper = mount( <ApolloTestProvider customResolvers={customResolvers}> <ApolloExample /> </ApolloTestProvider>, ); await jestHelpers.asyncWait(); const text = wrapper.text(); expect(text).toMatch(/Default currency: usd/); expect(text).toMatch(/Currency.*Available.*Pending/); expect(text).toMatch(/$10.*$20/); });
  • 25. Mock for loading/error states We've also added helpers for a very common type of edge case: Errors and loading state. it('renders loading state', async () => { const wrapper = mount( <LoadingProvider> <ApolloExample /> </LoadingProvider>, ); await jestHelpers.asyncWait(); expect(wrapper.text()) .toBe('Loading...'); }); it('renders error state', async () => { const wrapper = mount( <ErrorProvider> <ApolloExample /> </ErrorProvider>, ); await jestHelpers.asyncWait(); expect(wrapper.text()) .toBe('Error! Oh no!'); });
  • 26. GraphQL mocks for prototyping Designers and developers can combine our component system with fake data with no additional effort.
  • 27. Mocking overview ✅ Automatic global mocks ✅ Specific overrides to test edge cases ✅ Error states ✅ Loading states ✅ Built in mocks for prototypes Read the blog post at bit.ly/graphql-mocking
  • 28. Tools at Stripe: Schema management • There's one GraphQL schema for the Dashboard • Anyone at Stripe should be able to independently make changes • We want to rely on tooling to prevent breakages
  • 29. Our schema.graphql file encodes the current state of the schema It's automatically generated from the API code and checked in to Git
  • 30. Detecting breaking changes in CI Frontend builds stay in the wild for a while, so we need to be carefun about backwards compatibility.
  • 31. Custom GraphQL tools • Stable: Built on a standard spec • Convenient: Leverage community tools like GraphQL.js, Apollo Codegen, and Babel • Tailored: Uniquely designed for our team, codebase, and development environment
  • 32. PA R T 3 Building our own tooling
  • 33. Let's build a GraphQL field usage finder together How would we build a tool that searches our codebase for usage of a specific GraphQL field? .jsx .jsx .jsx User.name ? GraphQL Schema
  • 34. Let's build a GraphQL field usage finder together How would we build a tool that searches our codebase for usage of a specific GraphQL field? 1. Get the GraphQL schema 2. Find queries in our codebase 3. Search those queries for usage of the desired field
  • 35. Getting our schema Super easy using graphql-config. With a .graphqlconfig in your repo: const { getGraphQLProjectConfig } = require("graphql-config"); const schema = getGraphQLProjectConfig().getSchema(); Accessing it is as easy as: { "schemaPath": "src/graphql/schema.graphql" }
  • 36. Finding GraphQL queries in the codebase If we're using graphql-tag, we can look for gql template literals: github.com/apollographql/graphql-tag const ExampleQuery = gql` query ExampleQuery { currentMerchant { id, defaultCurrency balanceSummaries { currency, available, pending } } } `;
  • 37. Finding GraphQL queries in the codebase const fs = require("fs"); const { parse } = require("@babel/parser"); // Read a file const fileContents = fs.readFileSync(filename, { encoding: "utf-8" }); // Create an AST by parsing the file using Babel const ast = parse(fileContents); Using Babel to create an Abstract Syntax Tree (AST):
  • 38. Finding GraphQL queries in the codebase const traverse = require("@babel/traverse").default; const graphqlStrings = []; traverse(ast, { TaggedTemplateExpression: function(path) { if (path.node.tag.name === "gql") { graphqlStrings.push(path.node.quasi.quasis[0]); } } }); Looking for TaggedTemplateExpressions named "gql":
  • 39. Useful tool: AST Explorer
  • 40. Searching for fields in each query const { parse } = require("graphql"); const ast = parse(jsNode.value.raw); Parsing the GraphQL query:
  • 41. Searching for fields in each query const { visit, visitWithTypeInfo, TypeInfo } = require("graphql"); const typeInfo = new TypeInfo(schema); visit(ast, visitWithTypeInfo(typeInfo, { Field(graphqlNode) { const fieldName = typeInfo.getParentType().name + "." + graphqlNode.name.value; // Now, check if it's the field we're looking for } })); Looking through all the fields:
  • 42. Searching for fields in each query if (fieldName === fieldWeAreLookingFor) { const operationName = ast.definitions[0].name.value, const line = jsNode.loc.start.line; console.log(`${filename}:${line} ${operationName}`); } Compare to the field we're looking for and print results:
  • 43. Searching for fields in each query const { parse, visit, visitWithTypeInfo, TypeInfo } = require("graphql"); const ast = parse(jsNode.value.raw); const typeInfo = new TypeInfo(schema); visit(ast, visitWithTypeInfo(typeInfo, { Field(graphqlNode) { const fieldName = typeInfo.getParentType().name + "." + graphqlNode.name.value; if (fieldName === fieldWeAreLookingFor) { const operationName = ast.definitions[0].name.value, const line = jsNode.loc.start.line; console.log(`${filename}:${line} ${operationName}`); } } })); The whole GraphQL part:
  • 44. Putting it all together Once we add some plumbing to read in files and accept an argument, it looks like this: $ node graphql-field-finder.js Query.user src/components/customers/CardsSection.js:40 AllCardsQuery src/graphql/queries/UnauthedClientsQuery.js:13 UnauthedClientsQuery src/user_settings/TwoFactorAuthenticationSettings.js:46 TwoFactorSettingsQuery tests/functional/full_flows_test.jsx:401 UnauthedClientsQuery tests/lib/ApolloTestProvider.test.jsx:13 ApolloTestProviderQuery tests/lib/ApolloTestProvider.test.jsx:49 ApolloTestProviderUsernameQuery
  • 45. R E C A P : W H AT W E U S E D • graphql-config to get the schema • graphql-tag to identify queries • Babel to extract queries from code • GraphQL.js to find fields in queries See the code at bit.ly/graphql-field-finder
  • 46. Building custom GraphQL tooling for your team Sashko Stubailo @stubailo, sashko@stripe.com We're hiring at Stripe in Dublin, SF, Singapore, Seattle, and remote in North America! Go forth and build your own GraphQL tools to make product development better at your company! Get this presentation: bit.ly/custom-graphql-tooling