Unleashing the power of GraphQL APIs: Apollo vs. Ballerina
GraphQL's flexibility, efficient data fetching, reduced network traffic, versioning capabilities, backend aggregation, and improved developer experience make it a powerful choice for implementing the Backend for Frontend architecture, providing enhanced performance and simplified data management for frontend applications.
Ballerina is an excellent choice for GraphQL due to its GraphQL-friendly abstractions. Choose Ballerina as your go-to language for building robust and scalable GraphQL APIs.
Ballerina is GraphQL - GraphQL is Ballerina
Ballerina, a strongly-typed language, is perfectly suited for GraphQL's declarative data fetching. With compile-time error handling, Ballerina ensures reliability and efficiency, making it an excellent choice for building robust and user-friendly GraphQL APIs. Ballerina is the perfect choice for developers who want to build reliable, efficient, and easy-to-use GraphQL APIs. The similarities between the Ballerina and GraphQL syntax are astonishing.
Object types: Using Ballerina record types is very much similar to GraphQL object definitions. | |
GraphQL
| Ballerina
|
Nullability: Ballerina supports nullable types similar to GraphQL. | |
GraphQL
| Ballerina
|
Union types: Ballerina supports union types similar to GraphQL union types. | |
GraphQL
| Ballerina
|
Default values: Ballerina has in-built default value support, similar to GraphQL. | |
GraphQL
| Ballerina
|
Clean and simple code
Ballerina provides a simple and clean way to write GraphQL services with fewer lines of code compared to Apollo. Its concise syntax and built-in features allow streamlined development, resulting in more efficient and maintainable GraphQL services. Ballerina's focus on simplicity helps developers achieve their goals with ease and clarity.
Apollo
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const typeDefs = `#graphql
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
const books = [
{
title: 'Harry Potter',
author: 'J. K. Rowling',
},
{
title: 'The Lord of the Rings',
author: 'J. R. R. Tolkien',
},
];
const resolvers = {
Query: {
books: () => books,
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
await startStandaloneServer(server, {
listen: { port: 4000 },
});
Ballerina
import ballerina/graphql;
type Book record {|
string title;
string author;
|};
Book[] books = [
{
title: "Harry Potter",
author: "J. K. Rowling"
},
{
title: "The Lord of the Rings",
author: "J. R. R. Tolkien"
}
];
service on new graphql:Listener(9090) {
resource function get books() returns Book[] => books;
}
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const typeDefs = `#graphql
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
const books = [
{
title: 'Harry Potter',
author: 'J. K. Rowling',
},
{
title: 'The Lord of the Rings',
author: 'J. R. R. Tolkien',
},
];
const resolvers = {
Query: {
books: () => books,
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
await startStandaloneServer(server, {
listen: { port: 4000 },
});
import ballerina/graphql;
type Book record {|
string title;
string author;
|};
Book[] books = [
{
title: "Harry Potter",
author: "J. K. Rowling"
},
{
title: "The Lord of the Rings",
author: "J. R. R. Tolkien"
}
];
service on new graphql:Listener(9090) {
resource function get books() returns Book[] => books;
}
Seamlessly generate schema from code
Ballerina GraphQL simplifies the life of developers by generating the GraphQL schema directly from the Ballerina code. With this automatic schema generation, developers can focus on writing code, streamlining development, and ensuring consistency between the schema and the code. It offers convenience, efficiency, and ease of use in building GraphQL services with Ballerina.
Ballerina code
import ballerina/graphql;
service on new graphql:Listener(9090) {
resource function get profile() returns Profile {
return {
name: "John Doe",
age: 30,
address: {street: "15 Yemen Road", city: "Yemen", country: "YM"}
};
}
}
type Profile record {|
string name;
int age;
Address address;
|};
type Address record {|
string street;
string city;
string country;
|};
Generated schema
type Query {
profile: Profile!
}
type Profile {
name: String!
age: Int!
address: Address!
}
type Address {
street: String!
city: String!
country: String!
}
import ballerina/graphql;
service on new graphql:Listener(9090) {
resource function get profile() returns Profile {
return {
name: "John Doe",
age: 30,
address: {street: "15 Yemen Road", city: "Yemen", country: "YM"}
};
}
}
type Profile record {|
string name;
int age;
Address address;
|};
type Address record {|
string street;
string city;
string country;
|};
type Query {
profile: Profile!
}
type Profile {
name: String!
age: Int!
address: Address!
}
type Address {
street: String!
city: String!
country: String!
}
Out-of-the-box subscription support - No additional libraries are needed
Ballerina offers seamless support for GraphQL subscriptions out-of-the-box, eliminating the need for additional libraries like with Apollo. With Ballerina, you can effortlessly integrate systems like Apache Kafka into your GraphQL subscriptions, enhancing real-time data streaming capabilities. This simplifies development, reduces dependencies, and provides a comprehensive solution for building robust GraphQL subscription-based applications.
Apollo
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import { createServer } from 'http';
import express from 'express';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import bodyParser from 'body-parser';
import cors from 'cors';
import resolvers from './resolvers';
import typeDefs from './typeDefs';
const schema = makeExecutableSchema({ typeDefs, resolvers });
const app = express();
const httpServer = createServer(app);
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql',
});
const serverCleanup = useServer({ schema }, wsServer);
const server = new ApolloServer({
schema,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose();
},
};
},
},
],
});
await server.start();
app.use('/graphql', cors<cors.CorsRequest>(), bodyParser.json(), expressMiddleware(server));
const PORT = 4000;
httpServer.listen(PORT, () => {
console.log(`Server is now running on http://localhost:${PORT}/graphql`);
});
Ballerina
import ballerina/graphql;
import ballerina/uuid;
service on new graphql:Listener(9090) {
resource function get greeting() returns string {
return "Welcome";
}
remote function publishMessage(NewPost newPost) returns string|error {
check publishPost(postData);
return new (postData);
}
resource function subscribe messages() returns stream<Post, error?>|error {
string id = uuid:createType1AsString();
PostStreamGenerator postStreamGenerator = check new (id);
stream<Post, error?> postStream = new (postStreamGenerator);
return postStream;
}
}
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import { createServer } from 'http';
import express from 'express';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import bodyParser from 'body-parser';
import cors from 'cors';
import resolvers from './resolvers';
import typeDefs from './typeDefs';
const schema = makeExecutableSchema({ typeDefs, resolvers });
const app = express();
const httpServer = createServer(app);
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql',
});
const serverCleanup = useServer({ schema }, wsServer);
const server = new ApolloServer({
schema,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose();
},
};
},
},
],
});
await server.start();
app.use('/graphql', cors<cors.CorsRequest>(), bodyParser.json(), expressMiddleware(server));
const PORT = 4000;
httpServer.listen(PORT, () => {
console.log(`Server is now running on http://localhost:${PORT}/graphql`);
});
import ballerina/graphql;
import ballerina/uuid;
service on new graphql:Listener(9090) {
resource function get greeting() returns string {
return "Welcome";
}
remote function publishMessage(NewPost newPost) returns string|error {
check publishPost(postData);
return new (postData);
}
resource function subscribe messages() returns stream<Post, error?>|error {
string id = uuid:createType1AsString();
PostStreamGenerator postStreamGenerator = check new (id);
stream<Post, error?> postStream = new (postStreamGenerator);
return postStream;
}
}
Built-in designer tool for GraphQL
Explore the boundless possibilities with the in-built GraphQL API designer, a visual tool of the Ballerina VS Code plugin. Effortlessly design and prototype GraphQL APIs, unlocking a seamless and intuitive development experience. Empower your GraphQL services with this exceptional visual designer tool.
GraphQL CLI tool - An all-in-one tool for GraphQL
Experience the exhilaration of the Ballerina GraphQL CLI tool. Effortlessly generate custom GraphQL clients from schemas, accessing endpoints with ease. Seamlessly generate and share Ballerina GraphQL service schemas, facilitating collaboration and empowering efficient development workflows.
Better security
Ballerina provides robust security features such as encryption, authentication, and authorization, which are essential for businesses dealing with sensitive data.
listener graphql:Listener graphqlListener = new (9090,
secureSocket = {
key: {
certFile: "../resource/path/to/public.crt",
keyFile: "../resource/path/to/private.key"
}
}
);
@graphql:ServiceConfig {
auth: [
{
oauth2IntrospectionConfig: {
url: "https://mytoken.endpoint/oauth2/introspect",
tokenTypeHint: "access_token",
scopeKey: "scp",
clientConfig: {
customHeaders: {"Authorization": "Basic YWRtaW46YWRtaW4="},
secureSocket: {
cert: "../resource/path/to/public.crt"
}
}
},
scopes: ["admin"]
}
],
// Validate the query depth
maxQueryDepth: 5
}
service /graphql on graphqlListener {
resource function get users() returns User[] {
// ...
}
remote function createPost(graphql:Context context, NewPost newPost) returns Post|error {
// ...
}
}
public type NewPost readonly & record {|
// Validate user inputs
@constraint:String {
maxLength: 25,
minLength: 5
}
string title;
|};
Your typical Git-based workflow: edit, debug, and test in VS Code
Ballerina provides built-in editing, debugging, and testing tools making it easier for developers to test, debug, deploy, and optimize their automation workflows.
Diagram when you need it, Code when you don't
Ballerina diagrams provide great creativity and flexibility in the early stages of development, allowing developers to visualize and iterate on their ideas quickly. However, developers can easily switch to writing code when delivering a more polished product by taking advantage of Ballerina's powerful language features.
Connect with anything
Access thousands of connectors for HTTP APIs (OpenAPI), event APIs (AsyncAPI), GraphQL services, legacy systems, and data stores, allowing seamless data transfer to and from any system, anywhere.
configurable string token = ?;
service /graphql on new graphql:Listener(9090) {
final github:Client githubClient;
function init() returns error? {
self.githubClient = check new ({auth: {token}});
}
resource function get repositories() returns Repository[]|error {
stream<Repository, github:Error?> repositories =
check self.githubClient->getRepositories();
return from github:Repository repository in repositories
select repository;
}
remote function createIssue(CreateIssueInput createIssueInput,
string owner, string repositoryName) returns github:Issue|error {
Issue issue =
check self.githubClient->createIssue(createIssueInput, owner, repositoryName);
check produceIssue(issue, repositoryName);
return issue;
}
resource function subscribe issues(string repositoryName) returns stream<Issue>|error {
IssueStream issueStreamGenerator = check new (repositoryName);
return new (issueStreamGenerator);
}
}
Community-driven development
Ballerina is a community-driven open-source project with contributions from developers around the world. This means that developers have access to a rich ecosystem of libraries, tools, and resources that can enhance their automation workflows.
(Extra!) Trivial hosting in WSO2 Choreo
Manual integrations? Scheduled integrations (cron jobs)? Triggered integrations? Integrations as APIs? No problem! Write the code, attach the repo to WSO2 Choreo, and let it do the rest.