DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Designing Scalable Java APIs With GraphQL
  • Understanding API Technologies: A Comparative Analysis of REST, GraphQL, and Asynchronous APIs
  • Rest API vs GraphQL
  • Optimizing Natural Language Queries for Multi-Service Information Retrieval

Trending

  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • Optimize Deployment Pipelines for Speed, Security and Seamless Automation
  • Scaling Mobile App Performance: How We Cut Screen Load Time From 8s to 2s
  • Build an MCP Server Using Go to Connect AI Agents With Databases
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. When It’s Time to Give REST a Rest

When It’s Time to Give REST a Rest

Based upon the underlying requirement, sometimes GraphQL with Apollo Server is the best approach over using a traditional RESTful API.

By 
John Vester user avatar
John Vester
DZone Core CORE ·
May. 10, 24 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
75.3K Views

Join the DZone community and get the full member experience.

Join For Free

Through my years of building services, the RESTful API has been my primary go-to. However, even though REST has its merits, that doesn’t mean it’s the best approach for every use case. Over the years, I’ve learned that, occasionally, there might be better alternatives for certain scenarios. Sticking with REST just because I’m passionate about it — when it’s not the right fit — only results in tech debt and a strained relationship with the product owner.

One of the biggest pain points with the RESTful approach is the need to make multiple requests to retrieve all the necessary information for a business decision. 

As an example, let’s assume I want a 360-view of a customer. I would need to make the following requests:

  • GET /customers/{some_token} provides the base customer information
  • GET /addresses/{some_token} supplies a required address
  • GET /contacts/{some_token} returns the contact information
  • GET /credit/{some_token} returns key financial information

While I understand the underlying goal of REST is to keep responses laser-focused for each resource, this scenario makes for more work on the consumer side. Just to populate a user interface that helps an organization make decisions related to future business with the customer, the consumer must make multiple calls 

In this article, I’ll show why GraphQL is the preferred approach over a RESTful API here, demonstrating how to deploy Apollo Server (and Apollo Explorer) to get up and running quickly with GraphQL.

I plan to build my solution with Node.js and deploy my solution to Heroku.

When To Use GraphQL Over REST?

There are several common use cases when GraphQL is a better approach than REST:

  • When you need flexibility in how you retrieve data: You can fetch complex data from various resources but all in a single request. (I will dive down this path in this article.)
  • When the frontend team needs to evolve the UI frequently: Rapidly changing data requirements won’t require the backend to adjust endpoints and cause blockers.
  • When you want to minimize over-fetching and under-fetching: Sometimes REST requires you to hit multiple endpoints to gather all the data you need (under-fetching), or hitting a single endpoint returns way more data than you actually need (over-fetching).
  • When you’re working with complex systems and microservices: Sometimes multiple sources just need to hit a single API layer for their data. GraphQL can provide that flexibility through a single API call.
  • When you need real-time data pushed to you: GraphQL features subscriptions, which provide real-time updates. This is useful in the case of chat apps or live data feeds. (I will cover this benefit in more detail in a follow-up article.)

What Is Apollo Server?

Since my skills with GraphQL aren’t polished, I decided to go with Apollo Server for this article.

Apollo Server is a GraphQL server that works with any GraphQL schema. The goal is to simplify the process of building a GraphQL API. The underlying design integrates well with frameworks such as Express or Koa. I will explore the ability to leverage subscriptions (via the graphql-ws library) for real-time data in my next article.

Where Apollo Server really shines is the Apollo Explorer, a built-in web interface that developers can use to explore and test their GraphQL APIs. The studio will be a perfect fit for me, as it allows for the easy construction of queries and the ability to view the API schema in a graphical format.

My Customer 360 Use Case

For this example, let’s assume we need the following schema to provide a 360-view of the customer:

TypeScript
 
  type Customer {
    token: String
    name: String
    sic_code: String
  }
  
  type Address {
    token: String
    customer_token: String
    address_line1: String
    address_line2: String
    city: String
    state: String
    postal_code: String
  }
  
  type Contact {
    token: String
    customer_token: String
    first_name: String
    last_name: String
    email: String
    phone: String
  }
  
  type Credit {
    token: String
    customer_token: String
    credit_limit: Float
    balance: Float
    credit_score: Int
  }


I plan to focus on the following GraphQL queries:

TypeScript
 
type Query {
    addresses: [Address]
    address(customer_token: String): Address
    contacts: [Contact]
    contact(customer_token: String): Contact
    customers: [Customer]
    customer(token: String): Customer
    credits: [Credit]
    credit(customer_token: String): Credit
  }


Consumers will provide the token for the Customer they wish to view. We expect to also retrieve the appropriate Address, Contact, and Credit objects. The goal is to avoid making four different API calls for all this information rather than with a single API call.

Getting Started With Apollo Server

I started by creating a new folder called graphql-server-customer on my local workstation. Then, using the Get Started section of the Apollo Server documentation, I followed steps one and two using a Typescript approach.

Next, I defined my schema and also included some static data for testing. Ordinarily, we would connect to a database, but static data will work fine for this demo.

Below is my updated index.ts file:

TypeScript
 
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

const typeDefs = `#graphql
  type Customer {
    token: String
    name: String
    sic_code: String
  }
  
  type Address {
    token: String
    customer_token: String
    address_line1: String
    address_line2: String
    city: String
    state: String
    postal_code: String
  }
  
  type Contact {
    token: String
    customer_token: String
    first_name: String
    last_name: String
    email: String
    phone: String
  }
  
  type Credit {
    token: String
    customer_token: String
    credit_limit: Float
    balance: Float
    credit_score: Int
  }

  type Query {
    addresses: [Address]
    address(customer_token: String): Address
    contacts: [Contact]
    contact(customer_token: String): Contact
    customers: [Customer]
    customer(token: String): Customer
    credits: [Credit]
    credit(customer_token: String): Credit
  }
`;

const resolvers = {
    Query: {
        addresses: () => addresses,
        address: (parent, args, context) => {
            const customer_token = args.customer_token;
            return addresses.find(address => address.customer_token === customer_token);
        },
        contacts: () => contacts,
        contact: (parent, args, context) => {
            const customer_token = args.customer_token;
            return contacts.find(contact => contact.customer_token === customer_token);
        },
        customers: () => customers,
        customer: (parent, args, context) => {
            const token = args.token;
            return customers.find(customer => customer.token === token);
        },
        credits: () => credits,
        credit: (parent, args, context) => {
            const customer_token = args.customer_token;
            return credits.find(credit => credit.customer_token === customer_token);
        }
    },
};

const server = new ApolloServer({
    typeDefs,
    resolvers,
});

const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
});

console.log(`Apollo Server ready at: ${url}`);

const customers = [
    {
        token: 'customer-token-1',
        name: 'Acme Inc.',
        sic_code: '1234'
    },
    {
        token: 'customer-token-2',
        name: 'Widget Co.',
        sic_code: '5678'
    }
];

const addresses = [
    {
        token: 'address-token-1',
        customer_token: 'customer-token-1',
        address_line1: '123 Main St.',
        address_line2: '',
        city: 'Anytown',
        state: 'CA',
        postal_code: '12345'
    },
    {
        token: 'address-token-22',
        customer_token: 'customer-token-2',
        address_line1: '456 Elm St.',
        address_line2: '',
        city: 'Othertown',
        state: 'NY',
        postal_code: '67890'
    }
];

const contacts = [
    {
        token: 'contact-token-1',
        customer_token: 'customer-token-1',
        first_name: 'John',
        last_name: 'Doe',
        email: 'jdoe@example.com',
        phone: '123-456-7890'
    }
];

const credits = [
    {
        token: 'credit-token-1',
        customer_token: 'customer-token-1',
        credit_limit: 10000.00,
        balance: 2500.00,
        credit_score: 750
    }
];


With everything configured as expected, we run the following command to start the server:

Shell
 
$ npm start


With the Apollo server running on port 4000, I used the http://localhost:4000/ URL to access Apollo Explorer. Then I set up the following example query:

TypeScript
 
query ExampleQuery {
  addresses {
    token
  }
  contacts {
    token
  }
  customers {
    token
  }
}


This is how it looks in Apollo Explorer:

Apollo Explorer

Pushing the Example Query button, I validated that the response payload aligned with the static data I provided in the index.ts:

JSON
 
{
  "data": {
    "addresses": [
      {
        "token": "address-token-1"
      },
      {
        "token": "address-token-22"
      }
    ],
    "contacts": [
      {
        "token": "contact-token-1"
      }
    ],
    "customers": [
      {
        "token": "customer-token-1"
      },
      {
        "token": "customer-token-2"
      }
    ]
  }
}


Before going any further in addressing my Customer 360 use case, I wanted to run this service in the cloud.

Deploying Apollo Server to Heroku

Since this article is all about doing something new, I wanted to see how hard it would be to deploy my Apollo server to Heroku.

I knew I had to address the port number differences between running locally and running somewhere in the cloud. I updated my code for starting the server as shown below:

TypeScript
 
const { url } = await startStandaloneServer(server, {
    listen: { port: Number.parseInt(process.env.PORT) || 4000 },
});


With this update, we’ll use port 4000 unless there is a PORT value specified in an environment variable.

Using Gitlab, I created a new project for these files and logged into my Heroku account using the Heroku command-line interface (CLI):

Shell
 
$ heroku login


You can create a new app in Heroku with either their CLI or the Heroku dashboard web UI. For this article, we’ll use the CLI:

Shell
 
$ heroku create jvc-graphql-server-customer


The CLI command returned the following response:

Shell
 
Creating jvc-graphql-server-customer... done
https://jvc-graphql-server-customer-b62b17a2c949.herokuapp.com/ | 
https://git.heroku.com/jvc-graphql-server-customer.git


The command also added the repository used by Heroku as a remote automatically:

Shell
 
$ git remote
heroku
origin


By default, Apollo Server disables Apollo Explorer in production environments. For my demo, I want to leave it running on Heroku. To do this, I need to set the NODE_ENV environment variable to development. I can set that with the following CLI command:

Shell
 
$ heroku config:set NODE_ENV=development


The CLI command returned the following response:

Shell
 
Setting NODE_ENV and restarting jvc-graphql-server-customer... done, v3
NODE_ENV: development


Now we’re in a position to deploy our code to Heroku:

Shell
 
$ git commit --allow-empty -m 'Deploy to Heroku'
$ git push heroku


A quick view of the Heroku Dashboard shows my Apollo Server running without any issues:

Heroku

If you’re new to Heroku, this guide will show you how to create a new account and install the Heroku CLI.

Acceptance Criteria Met: My Customer 360 Example

With GraphQL, I can meet the acceptance criteria for my Customer 360 use case with the following query:

TypeScript
 
query CustomerData($token: String) {
  customer(token: $token) {
    name
    sic_code
    token
  },
  address(customer_token: $token) {
    token
    customer_token
    address_line1
    address_line2
    city
    state
    postal_code
  },
  contact(customer_token: $token) {
    token,
    customer_token,
    first_name,
    last_name,
    email,
    phone
  },
  credit(customer_token: $token) {
    token,
    customer_token,
    credit_limit,
    balance,
    credit_score
  }
}


All I need to do is pass in a single Customer token variable with a value of customer-token-1:

JSON
 
{
  "token": "customer-token-1"
}


We can retrieve all of the data using a single GraphQL API call:

JSON
 
{
  "data": {
    "customer": {
      "name": "Acme Inc.",
      "sic_code": "1234",
      "token": "customer-token-1"
    },
    "address": {
      "token": "address-token-1",
      "customer_token": "customer-token-1",
      "address_line1": "123 Main St.",
      "address_line2": "",
      "city": "Anytown",
      "state": "CA",
      "postal_code": "12345"
    },
    "contact": {
      "token": "contact-token-1",
      "customer_token": "customer-token-1",
      "first_name": "John",
      "last_name": "Doe",
      "email": "jdoe@example.com",
      "phone": "123-456-7890"
    },
    "credit": {
      "token": "credit-token-1",
      "customer_token": "customer-token-1",
      "credit_limit": 10000,
      "balance": 2500,
      "credit_score": 750
    }
  }
}


Below is a screenshot from Apollo Explorer running from my Heroku app:

Apollo Explorer running from my Heroku app

Conclusion

I recall earlier in my career when Java and C# were competing against each other for developer adoption. Advocates on each side of the debate were ready to prove that their chosen tech was the best choice … even when it wasn’t.

In this example, we could have met my Customer 360 use case in multiple ways. Using a proven RESTful API would have worked, but it would have required multiple API calls to retrieve all of the necessary data. Using Apollo Server and GraphQL allowed me to meet my goals with a single API call.

I also love how easy it is to deploy my GraphQL server to Heroku with just a few commands in my terminal. This allows me to focus on implementation—offloading the burdens of infrastructure and running my code to a trusted third-party provider. Most importantly, this falls right in line with my personal mission statement:

“Focus your time on delivering features/functionality that extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.” – J. Vester

If you are interested in the source code for this article, it is available on GitLab.

But wait… there’s more! 

In my follow-up post, we will build out our GraphQL server further, to implement authentication and real-time data retrieval with subscriptions.

Have a really great day!

API GraphQL REST

Opinions expressed by DZone contributors are their own.

Related

  • Designing Scalable Java APIs With GraphQL
  • Understanding API Technologies: A Comparative Analysis of REST, GraphQL, and Asynchronous APIs
  • Rest API vs GraphQL
  • Optimizing Natural Language Queries for Multi-Service Information Retrieval

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!