- Firecamp is a modern client for Real-time, GraphQL, REST and other 15+ routine tech stacks and platforms. It serves the perfect testing, debugging and management utility platform for modern development which demands multiple technologies and diverse skill-set. Real-time clients like WebSocket Testing Client, Socket.io testing client, Pusher Testing, Ably Clients are enable the visual testing.
- CodeSandbox is an online code editor and prototyping tool that makes creating and sharing web apps faster.
Chrome extension for GraphQL Playground. GraphQL Playground for Chrome offered by Dustin R. Callaway (4) 4,000+ users. Download Google Chrome.
SECURITY WARNING: both
graphql-playground-html
and all four (4) of it's middleware dependents until [email protected]
were subject to an XSS Reflection attack vulnerability only to unsanitized user input strings to the functions therein. This was resolved in graphql-playground-html@^1.6.22
. More InformationFuture of this repository: see the announcement issue for details.
GraphQL IDE for better development workflows (GraphQL Subscriptions, interactive docs & collaboration).
Installation
Features
✨ Context-aware autocompletion & error highlighting? Interactive, multi-column docs (keyboard support)⚡️ Supports real-time GraphQL Subscriptions⚙ GraphQL Config support with multiple Projects & Endpoints? Apollo Tracing support
Security Details
NOTE: only unsanitized user input to the functions in these packages is vulnerable to the recently reported XSS Reflection attack.
Impact
Impacted are any and all unsanitized user-defined input to:-
renderPlaygroundPage()
-koaPlayground()
-expressPlayground()
-koaPlayground()
-`lambdaPlayground()If you used static values, such as
graphql-playground-electron
does in it's webpack config, as well as the most common middleware implementations out there, they were not vulnerable to the attack.The only reason this vulnerability exists is because we are using template strings in
renderPlaygroundPage()
with potentially unsanitized user defined variables. This allows an attacker to inject html and javascript into the page.Common examples may be user-defined path parameters, query string, unsanitized UI provided values in database, etc., that are used to build template strings or passed directly to a
renderPlaygroundPage()
or the matching middleware function equivalent listed above.Impacted Packages
All versions of these packages are impacted until the ones specified below, which are now safe for user defined input:
graphql-playground-html
:☔ safe @1.6.22
graphql-playground-express
☔ safe @1.7.16
graphql-playground-koa
☔ safe @1.6.15
graphql-playground-hapi
☔ safe @1.6.13
graphql-playground-lambda
☔ safe @1.7.17
graphql-playground-electron
has always been☔ safe from XSS attacks! This is because configuration is statically defined it's webpack configgraphql-playground-react
is safe because it does not userenderPlaygroundPage()
anywhere, and thus is not susceptible to template string XSS reflection attacks.
More Information
See the security docs for more details on how your implementation might be impacted by this vulnerability. It contains safe examples, unsafe examples, workarounds, and more details.
We've also provided 'an example of the xss using the express middleware
FAQ
How is this different from GraphiQL?
GraphQL Playground uses components of GraphiQL under the hood but is meant as a more powerful GraphQL IDE enabling better (local) development workflows. Compared to GraphiQL, the GraphQL Playground ships with the following additional features:
- Interactive, multi-column schema documentation
- Automatic schema reloading
- Support for GraphQL Subscriptions
- Query history
- Configuration of HTTP headers
- Tabs
See the following question for more additonal features.
What's the difference between the desktop app and the web version?
The desktop app is the same as the web version but includes these additional features:
- Partial support for graphql-config enabling features like multi-environment setups (no support for sending HTTP headers).
- Double click on
*.graphql
files.
How does GraphQL Bin work?
You can easily share your Playgrounds with others by clicking on the 'Share' button and sharing the generated link. You can think about GraphQL Bin like Pastebin for your GraphQL queries including the context (endpoint, HTTP headers, open tabs etc).
You can also find the announcement blog post here.
Settings
In the top right corner of the Playground window you can click on the settings icon.These are the settings currently available:
Usage
Properties
The React component
<Playground />
and all middlewares expose the following options:props
(Middlewares & React Component)endpoint
string
- the GraphQL endpoint url.subscriptionEndpoint
string
- the GraphQL subscriptions endpoint url.workspaceName
string
- in case you provide a GraphQL Config, you can name your workspace hereconfig
string
- the JSON of a GraphQL Config. See an example heresettings
ISettings
- Editor settings in json format as described here
schema
IntrospectionResult
- The result of an introspection query (an object of this form:{__schema: {...}}
) The playground automatically fetches the schema from the endpoint. This is only needed when you want to override the schema.tabs
Tab[]
- An array of tabs to inject. Note: When using this feature, tabs will be resetted each time the page is reloaded
In addition to this, the React app provides some more properties:
props
(React Component)createApolloLink
[(session: Session, subscriptionEndpoint?: string) => ApolloLink
] - this is the equivalent to thefetcher
of GraphiQL. For each query that is being executed, this function will be called
createApolloLink
is only available in the React Component and not the middlewares, because the content must be serializable as it is being printed into a HTML template.As HTML Page
If you simply want to render the Playground HTML on your own, for example when implementing a GraphQL Server, there are 2 options for you:
Note: In case you do not want to serve assets from a CDN (like jsDelivr) and instead use a local copy, you will need to install
graphql-playground-react
from npm, and then replace all instances of //cdn.jsdelivr.net/npm
with ./node_modules
. An example can be found hereAs React Component
Install
Use
GraphQL Playground provides a React component responsible for rendering the UI and Session management.There are 3 dependencies needed in order to run the
graphql-playground-react
React component.- Open Sans and Source Code Pro fonts
- Rendering the
<Playground />
component
The GraphQL Playground requires React 16.
Including Fonts (
1.
)Including stylesheet and the component (
2., 3.
)As Server Middleware
Install
Usage with example
We have a full example for each of the frameworks below:
- Express: See packages/graphql-playground-middleware-express/examples/basic
- Hapi: See packages/graphql-playground-middleware-hapi
- Koa: See packages/graphql-playground-middleware-koa
- Lambda (as serverless handler): See serverless-graphql-apollo or a quick example below.
As serverless handler
Install
Usage
handler.js
serverless.yml
Security Issue
There is an XSS Reflection Vulnerability when using these middlewares with unsanitized user input before
Development
Openlocalhost:3000/localDev.html?endpoint=https://api.graph.cool/simple/v1/swapi for local development!
Custom Theme
From
[email protected]
on you can provide a codeTheme
property to the React Component to customize your color theme.These are the available options:Versions
This is repository is a 'mono repo' and contains multiple packages using Yarn workspaces. Please be aware that versions are not synchronised between packages. The versions of the release page refer to the electron app.
Packages
In the folder
packages
you'll find the following packages:graphql-playground-electron
: Cross-platform electron app which usesgraphql-playground-react
graphql-playground-html
: Simple HTML page rendering a version ofgraphql-playground-react
hosted on JSDelivergraphql-playground-middleware-express
: Express middleware usinggraphql-playground-html
graphql-playground-middleware-hapi
: Hapi middleware usinggraphql-playground-html
graphql-playground-middleware-koa
: Koa middleware usinggraphql-playground-html
graphql-playground-middleware-lambda
: AWS Lambda middleware usinggraphql-playground-html
graphql-playground-react
: Core of GraphQL Playground built with ReactJS
Join our Discord Server if you run into issues or have questions. We love talking to you!
by Sacha Greif
Learn to unlock the power of GraphQL without suffering its drawbacks
GraphQL is all the rage these days, and for good reason: it’s an elegant approach that solves many of the problems associated with traditional REST APIs.
Yet I’d be lying if I told you that GraphQL doesn’t come with its own set of issues. And if you’re not careful, these issues might not only lead to a bloated codebase, but even to a dramatically slowed-down app.
I’m talking about problems such as:
- Schema duplication
- Server/client data mismatch
- Superfluous database calls
- Poor performance
- Boilerplate overdose
I’m willing to bet your app suffers from at least one of them. Good news is, none of them are incurable!
For each issue, I’ll describe the problem, and then explain how I’m addressing it inside Vulcan, a React/GraphQL open-source framework I’ve been working on over the past year (you should check it out!). But hopefully, you’ll be able to apply the same strategies to your own codebase whether you use Vulcan or not.
Problem: Schema Duplication
One of the first things you realize when coding a GraphQL back-end from scratch is that it involves a lot of similar-but-not-quite-identical code, especially when it comes to schemas.
Namely, you need one schema for your database, and another one for your GraphQL endpoint. Not only is it frustrating to have to write more or less the same thing twice, but you now have two independent sources of truths that you need to constantly keep in sync.
Solution: GraphQL Schema Generation
A number of solutions to this problem have emerged in the GraphQL ecosystem. For example, PostGraphile generates a GraphQL schema from your PostgreSQL database, and Prisma will also help you generate types for your queries and mutations.
I also remember hearing Laney Zamore & Adam Kramer from the GraphQL team describe how they directly generated their GraphQL schema from their PHP type definitions.
For Vulcan, I independently stumbled on a very similar solution. I was using SimpleSchema to describe my schemas as JavaScript objects, and I started simply by converting JavaScript’s
String
type into a GraphQL String
, Number
into Int
or Float
, and so on.So this JavaScript field:
Would become this GraphQL field:
But of course, a GraphQL schema can also have custom types:
User
, Comment
, Event
, and so on.I didn’t want to add too much magic to the schema generation step, so I came up with field resolvers, a simple way to let you specify these custom types. So that this JavaScript field:
Becomes:
As you can see, we’re defining the actual resolver function on the field as well, since it’s also directly related to the GraphQL field.
So whether you use something like PostGraphile or write your own schema generation code, I encourage you to avoid schema duplication in your own app.
Or of course, you can also use a hosted service such as Graphcool to manage your schema using their dashboard and bypass that issue entirely.
Problem: Server/Client Data Mismatch
As we’ve just seen, your database and GraphQL API will have different schemas, which translate into different document shapes.
GraphQL Playground
So while a
post
fresh out of the database will have a userId
property, the same post
as fetched through your API will instead have a user
property.This means that getting a post author’s name on the client will look like:
But on the server, it’ll be a different story altogether:
This can be a problem anytime you’re trying to share code between client and server to simplify your codebase. And even beyond that, it means you’re missing out on GraphQL’s great approach to data querying on the server.
I recently felt that pain when trying to build a system to generate weekly newsletters: each newsletter was composed of multiple posts and comments, along with info about their authors; in other words, a perfect use case for GraphQL. But this newsletter generation was happening on the server, meaning I didn’t have a way to query my GraphQL endpoint…
Solution: Server-to-Server GraphQL Queries
Or did I? It turns out that you can run server-to-server GraphQL queries just fine! Just pass your GraphQL executable schema to the
graphql
function, along with your GraphQL query:In Vulcan, I generalized this pattern into a
runQuery
helper, and I also added queryOne
functions to each collection. These act just like MongoDB’s findOne
except they return the document as fetched through the GraphQL API:Server-to-server GraphQL queries have helped me drastically simplify my code. It let me refactor my newsletter generation call from a mess of successive database calls and loops to a single GraphQL query:
The takeaway here: don’t see GraphQL as just a pure client-server protocol. GraphQL can be used to query data in any situation, including client-to-client with Apollo Link State or even during a static build process with Gatsby.
Problem: Superfluous Database Calls
Imagine a list of posts, each of which has a user attached to it. You now want to display 10 of these posts, along with the name of their author.
With a typical implementation, this would mean two database calls. One to get the 10 posts, and one to get the 10 users corresponding to these posts.
But what about GraphQL? Assuming our posts have a
user
field with its own resolver, we still have one initial database call to get the list of posts. But we now have an extra call to fetch each user per resolver, for a total of 11 database calls!Now imagine that each post also has 5 comments, each of which has an author. Our number of calls has now ballooned to:
- 1 for the list of posts
- 10 for the post authors
- 10 for each sub-list of 5 comments
- 50 for the comment authors
For a grand total of 71 database calls to display a single view!
Nobody wants to have to explain to their boss why the homepage is taking 25 seconds to load. Thankfully, there’s a solution: Dataloader.
Solution: Dataloader
Dataloader will let you batch and cache database calls.
- Batching means that if Dataloader figures out that you’re hitting the same database table multiple times, it’ll batch all calls together. In our example, the 10 post authors’ and 50 comment authors’ calls would all be batched into a single call.
- Caching means that if Dataloader detects that two posts (or a post and a comment) have the same author, it will reuse the user object it already has in memory instead of making a new database call.
In practice you don’t always achieve perfect caching and batching, but Dataloader is still a huge help.
And Vulcan makes using Dataloader extra easy. Out of the box, every Vulcan model includes Dataloader functions as alternatives to the “normal” MongoDB query functions.
So in addition to
collection.findOne
and collection.find
, you can use collection.loader.load
and collection.loader.loadMany
.The one limitation is that Dataloader only works when querying using document IDs. So you can use it to query for a document whose ID is already known, but you’ll still need to hit your database if you want to ask for, say, the most recently created post.
Problem: Poor Performance
Even with Dataloader enabled, complex views can still trigger multiple database calls, which in turn can make for slow loading times.
This can be frustrating: on one hand you want to take full advantage of GraphQL’s graph traversal features (“show me the authors of the comments of the author of the post of…” etc.). But on the other hand, you don’t want your app to become slow and unresponsive.
Solution: Query Caching
There is a solution though, which is to cache the entire GraphQL query response. Unlike Dataloader, whose scope is limited to the current request (meaning it will only cache documents within the same query), I’m talking here about caching the whole query for a period of time.
Apollo Engine is a great way to do just that. It’s a hosted service that provides analytics about your GraphQL queries, but it also has a very useful caching feature.
Vulcan comes with a built-in Engine integration, you just need to add your API key to your settings file. You can then add the
enableCache: true
argument to your GraphQL queries to cache them using Engine.Or, using Vulcan’s built-in data-loading higher-order components:
The beauty of this approach is that you can easily control which queries are cached and which aren’t, even for the same resolver. For example, you might want to cache the list of recent posts featured on your frequently-accessed homepage, but not the full list of posts available on your archives page.
A final note: caching might not always be possible. For example, it’s not advisable for data that changes frequently, or for data that depends on the currently logged-in user.
Problem: Boilerplate Overdose
This is by no means an issue exclusive to GraphQL apps, but it’s true that they generally require you to write a lot of similar boilerplate code.
Typically, adding a new model (e.g.
Comments
) to your app will involve the following steps:- Writing a resolver to get a list of comments.
- Writing a higher-order component (a.k.a. container) to load that list of comments.
- Optionally, writing a resolver to get a single comment by ID or slug along with the corresponding higher-order component.
- Writing mutations for inserting a new comment, editing a comment, and deleting a comment.
- Adding the corresponding forms and form-handling code.
Graphql Playground Download
That’s a whole lot of CRUD!
Solution: Generic Resolvers, Mutations, and Higher-Order Components
Vulcan’s approach is to give you smart, easy-to-use generic options for each of these. You’ll get:
- Default resolvers for displaying lists of documents and single documents.
- Pre-made higher-order components for loading a list of documents or a single document.
- Default mutation resolvers for inserting, editing, and removing documents.
- Generated forms based on your schema that work with all the above.
These are all written in a generic enough way that they’ll work with any new model out of the box.
To be sure, this “one-size-fits-all” approach is not without its downsides. For example, because queries are generated dynamically by the generic higher-order components, it’s a bit harder to use static queries.
But this strategy is still a great way to get started, at least until you have time to refactor each part of your app to a more tailor-made solution.
GraphQL is still relatively new, which means that while everybody is busy extolling its virtues, it’s easy to overlook the very real challenges involved with building GraphQL apps.
Thankfully these challenges all have solutions, and the more we discuss them (the Vulcan Slack is a great place for that by the way!), the better these solutions will become!