Swagger-to-graphql allows you to generate a GraphQL schema given a Swagger (OpenAPI) schema. It avoids the need to code a GraphQL schema so you can get started with GraphQL quickly. It can run in your datacenter using NodeJS, but it also runs in the browser for experimentation. That means you can try it online with your own schema. There are also public Swagger schemas available in the APIs.guru OpenAPI directory.
Why swagger-to-graphql?
Swagger-to-graphql is ready for production use, but it’s also ideal for quick experimentation. When you run it in the browser you can experiment with GraphQL without having to do any backend coding. GraphQL calls will be translated to Ajax calls. Note that for production use you want to run GraphQL in your datacenter. When you’re running GraphQL in the browser you are shipping a parser and interpreter. You also don’t get the efficient data fetching of GraphQL.
To get all the benefits of GraphQL, you run swagger-to-graphql in your datacenter. When you still expose your existing REST API you end up with a hybrid API with both REST and GraphQL. This can be a step in a migration plan to full GraphQL or you can keep this hybrid approach long-term.
When running in production you want to have full control over how API calls are made. That is why swagger-to-graphql does not perform any data fetching itself. You provide the API client and perform the actual call. This allows you to pick which HTTP headers are proxied to the backend, how the query parameters are serialized and which connection pool is used. To get started quickly you can copy/paste one of the provided example implementations.
Recently I rewrote a big part of swagger-to-graphql to make it production ready. The code is refactored for readability, converted to TypeScript and has good test coverage. This makes it easier to contribute improvements which ensures the longevity of the project.
How to get started?
To get started you need to point swagger-to-graphql to your Swagger schema and provide a callback which performs data fetching. You will get a GraphQL schema which can make REST calls. The data fetching callback is called with the data you need to make a REST call.
// requestOptions: { method: 'post', baseUrl: 'https://petstore.swagger.io/v2', path: '/pet', bodyType: 'json', body: { name: 'new dog' }, headers: { api_key: '1234', }, };
In the browser this can be passed to a fetch call. On the server you could use node-fetch or some other HTTP client.
import { createSchema, CallBackendArguments } from 'swagger-to-graphql';
async function callBackend({
requestOptions: { method, body, baseUrl, path, query, headers },
}: CallBackendArguments<{}>) {
const url = `${baseUrl}${path}?${new URLSearchParams(query as Record<
string,
string
>)}`;
const response = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
...headers,
},
body: JSON.stringify(body),
});
const text = await response.text();
if (200 <= response.status && response.status < 300) {
try {
return JSON.parse(text);
} catch (e) {
return text;
}
}
throw new Error(Response: ${response.status} - ${text}
);
}
const schemaPromise = createSchema({
swaggerSchema: 'https://my-origin.com/myservice/swagger.json',
callBackend,
});
Note that in this example, only JSON payloads are supported. Swagger-to-graphql also supports form encoded data. The node-fetch example on Github shows how to implement this.
Code generation with Apollo
The Apollo GraphQL client can generate types based on queries in your frontend code. This means you get end to end type safety. Especially if your Swagger schema was generated by the backend code or when your backend code is generated by your Swagger schema. Both approaches ensure the backend and the frontend must adhere to the same data model.
To generate types Apollo CLI needs to have a GraphQL schema. Typically Apollo downloads this schema from a running GraphQL server using an introspection query. If you don’t have your server deployed yet, you can also generate a schema based on the Swagger file. Swagger-to-graphql provides a CLI tool that outputs a GraphQL schema given a Swagger file. You can configure NPM scripts to generate the schema and types.
"scripts": { "generate-schema": "swagger-to-graphql --swagger-schema https://petstore.swagger.io/v2/swagger.json > src/__generated__/schema.graphql", "precodegen": "npm run generate-schema", "codegen": "apollo client:codegen --target typescript" },
To get this to work you also need to add a configuration file to tell Apollo where it can find the GraphQL schema. Because schema.graphql is in the src/ folder it needs to be excluded, otherwise the codegeneration will interpret it twice and give an error.
// apollo.config.js module.exports = { client: { service: { name: 'petstore', localSchemaFile: 'src/__generated__/schema.graphql' }, excludes: ['src/__generated__/schema.graphql'] } };
To get Apollo to work with the schema generated by swagger-to-graphql in the browser you need to use apollo-link-schema. This will execute queries using the generated GraphQL schema, whereas the default Apollo configuration assumes a remote GraphQL server.
import { ApolloClient } from 'apollo-client'; import { InMemoryCache } from 'apollo-cache-inmemory'; import SchemaLink from 'apollo-link-schema'; function createApolloClient(schema: GraphQLSchema) { return new ApolloClient({ cache: new InMemoryCache(), link: new SchemaLink({ schema, }), }); }
Get the example code!
A fully working example is available on Github. You can also try it online to see the Ajax calls being made in the browser.
Next steps
Now you have an automatically generated GraphQL schema you might want to improve what it looks like. The best approach is to improve the Swagger schema which in turn improves the GraphQL schema. If you need more control then that you could have a look at GraphQL schema transforms. It allows you to modify every part of your GraphQL schema while still being able to leverage swagger-to-graphql and the REST API it’s using.
Eventually you will want to convert your whole company to GraphQL, which means that you want to combine multiple backends from different teams in a single GraphQL endpoint. This is faster than querying multiple GraphQL endpoints because you only have to make a single request to fetch all the data. To do this you can use GraphQL schema stitching. You can even do nested joins of different GraphQL resolvers. That way you can fetch more data with a single API call. Note you do need to configure the logic for these joins in the joining GraphQL service. This could make your teams less autonomous.
A new approach to combining GraphQL schemas is GraphQL federation. It has the same goal as schema stitching, but it doesn’t configure the joining in the centralized gateway, but in the distributed GraphQL schemas themselves. Unfortunately this is not supported with swagger-to-graphql yet. I’m thinking about creating a GraphQL schema transform which will add federation to any GraphQL schema, but I have yet to write any code to make that work. So stay tuned!