Zuplo logo
Back to all articles

How to Access a REST API Through GraphQL

July 20, 2025
30 min read
Martyn Davies
Martyn DaviesDeveloper Advocate

Here’s why combining GraphQL with REST is useful:

  • Simplified Data Fetching: Avoid over-fetching or under-fetching data common in REST.
  • Single Endpoint: GraphQL consolidates multiple REST endpoints into one.
  • Schema Control: GraphQL schemas provide clear data structures and relationships.
  • Improved Performance: Tools like caching and batching reduce API call overhead.
  • Enhanced Security: Use features like authentication, rate limiting, and input validation.

Quick Setup Steps:#

  1. Define a GraphQL Schema: Map REST API data into structured GraphQL types.
  2. Create Resolvers: Connect GraphQL queries to REST endpoints.
  3. Optimize Performance: Use caching, batching, and rate limiting.
  4. Secure the API: Implement authentication, authorization, and input validation.

Comparison Table: REST vs. GraphQL#

GraphQL

FeatureRESTGraphQL
EndpointsMultiple endpointsSingle endpoint
Data FetchingFixed data structureFlexible, client-defined
Over-fetching/Under-fetchingCommon issuesAvoided with queries
PerformanceHigher API call overheadOptimized with batching

Video: Convert REST APIs to GraphQL in 3 simple steps#

We find video tutorial helpful, so in case you would prefer to watch/listen, here's a video from IBM that covers a lot of the same topics:

Setup Requirements#

When setting up a GraphQL server to work with REST APIs, you'll need specific tools, a proper configuration, and robust security measures.

Required Tools#

To integrate REST APIs with GraphQL, you'll rely on a few key tools:

ToolPrimary FunctionKey Feature
Apollo ServerGraphQL ServerBuilt-in support for REST data sources
Express-GraphQLHTTP MiddlewareEasy integration with REST endpoints
GraphQL Schema ToolsSchema DefinitionGenerates types from REST responses
Zuplo API GatewayRequest ManagementAdvanced caching and rate limiting

GraphQL Server Setup Steps#

To connect your GraphQL server with REST endpoints, tools like Apollo Server simplify the process. Here's an example using TypeScript to create a REST data source for a movie-related API:

class MoviesAPI extends RESTDataSource {
  override baseURL = "https://movies-api.example.com/";

  async getMovie(id: string): Promise<Movie> {
    return this.get<Movie>(`movies/${encodeURIComponent(id)}`);
  }
}

const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources: () => ({
    moviesAPI: new MoviesAPI(),
  }),
});

This example demonstrates how to define a dedicated REST data source, ensuring clear separation of logic and efficient data handling. Once your server is configured, securing it becomes the next priority.

Security Setup#

Protecting your GraphQL server requires a multi-layered security approach:

Security LayerImplementationPurpose
AuthenticationJWT or OAuth2.0Verifies user identity
AuthorizationField-level permissionsControls access to specific data
Rate LimitingRequest throttlingPrevents denial-of-service attacks
Input ValidationSchema checksGuards against malicious injections

For production environments, consider additional safeguards: mask error messages, disable introspection, and set timeouts for REST calls. These measures enhance both stability and security.

Creating GraphQL Schemas from REST#

Learn how to map REST responses to GraphQL types and effectively manage errors during the process.

Schema Definition#

The following GraphQL schema transforms REST API responses into structured GraphQL types:

type Movie {
  id: ID!
  title: String!
  releaseDate: String!
  director: Director!
  ratings: [Rating!]!
}

type Director {
  id: ID!
  name: String!
  biography: String
}

type Rating {
  source: RatingSource!
  score: Float!
}

enum RatingSource {
  IMDB
  ROTTEN_TOMATOES
  METACRITIC
}

union MovieResult = Movie | MovieError

type MovieError {
  code: String!
  message: String!
}

This schema simplifies complex REST responses by organizing them into GraphQL types, making it easier to query and manage relationships between entities.

REST Resolver Creation#

Resolvers serve as the connection between GraphQL queries and REST endpoints. Below is an example implementation using Apollo Server's RESTDataSource:

class MoviesAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = "https://api.movies.example/v1/";
  }

  async getMovie(id: string) {
    try {
      const movie = await this.get(`movies/${id}`);
      return {
        ...movie,
        director: await this.getDirector(movie.directorId),
      };
    } catch (error) {
      throw new GraphQLError("Failed to fetch movie data", {
        extensions: { code: "REST_ERROR" },
      });
    }
  }
}

Here's a quick breakdown of resolver types and their best practices:

Resolver TypePurposeBest Practice
QueryFetches dataKeep resolvers lightweight; delegate to data sources
FieldResolves nested dataUse DataLoader for batching and performance
MutationModifies dataValidate inputs before making REST API calls

Error Handling#

Managing errors in GraphQL differs significantly from traditional REST APIs. Proper error handling ensures smoother debugging and better user experience. Here's how you can handle errors in a query resolver:

const resolvers = {
  Query: {
    movie: async (_, { id }, { dataSources }) => {
      try {
        const movie = await dataSources.moviesAPI.getMovie(id);
        return {
          __typename: "Movie",
          ...movie,
        };
      } catch (error) {
        return {
          __typename: "MovieError",
          code: error.extensions?.code || "UNKNOWN_ERROR",
          message: error.message,
        };
      }
    },
  },
};

Key steps for effective error handling:

  • Explicit error types: Define specific error types in your schema to make issues easy to identify.
  • Shared interfaces: Use GraphQL interfaces for common properties across error types.
  • Logging and monitoring: Implement robust error logging to track issues effectively.
  • Error transformation: Convert REST API errors into formats compatible with GraphQL.

You can also configure error policies to control how errors are handled during queries:

PolicyUse CaseBehavior
noneDefault behaviorReturns undefined for data on errors
ignorePartial data acceptanceIgnores errors and returns available data
allDebuggingReturns both errors and partial data
Tweet

Over 10,000 developers trust Zuplo to secure, document, and monetize their APIs

Learn More

Performance Improvements#

Integrating GraphQL and REST efficiently requires careful attention to performance. By batching requests, managing rate limits, and leveraging monitoring tools, you can significantly improve the responsiveness and scalability of your system. These strategies build on established security and schema practices to deliver a smoother experience.

Request Optimization#

One way to streamline performance is by batching REST requests using tools like DataLoader. Here's an example:

const batchLoadMovies = async (ids) => {
  const movies = await restClient.post("/movies/batch", { ids });
  return ids.map((id) => movies.find((movie) => movie.id === id));
};

const movieLoader = new DataLoader(batchLoadMovies);

As Raja Chattopadhyay explains:

"With GraphQL, the client can request exactly how much data it needs, making sure the application API calls are optimized. This helps significantly to improve overall performance and reduce underlying network and storage costs".

By reducing the overhead of individual API calls, you can also manage the overall request volume more effectively using rate limiting.

Rate Limit Management#

To balance throughput and latency, implement tiered rate limits. For example:

const queryComplexityRule = {
  maxCost: 1000,
  variables: {
    listLimit: 50,
    nestedLimit: 3,
  },
};

const rateLimiter = new TokenBucketRateLimiter({
  tokensPerInterval: 5000,
  interval: "hour",
});

Authenticated users can make up to 5,000 requests per hour, while unauthenticated users are capped at 60 requests per hour. These limits help maintain system stability and ensure fair usage.

Zuplo Performance Optimizations#

The Zuplo API Gateway provides several features to enhance performance:

  • Advanced Rate Limiting: Set limits based on request volume, payload size, query complexity, and authentication status.
  • Performance Monitoring: Use real-time analytics to track latency, error rates, and query patterns.
  • Edge Optimization: Deploy GraphQL gateways closer to users to reduce latency.

Additional Optimizations#

To further improve performance, consider caching frequently accessed data and limiting query depth:

// Cache common data
const cache = new InMemoryLRUCache({
  maxSize: 1000,
  ttl: 300000, // 5 minutes
});

// Limit query depth
const depthLimit = createComplexityLimit({
  maxDepth: 5,
  onLimit: (complexity) => {
    throw new Error(`Query complexity (${complexity}) exceeds limit`);
  },
});

Platforms handling thousands of requests per second benefit greatly from these techniques. By implementing these optimizations, you're setting the stage for a highly efficient production environment.

Deployment and Monitoring#

Deploying and monitoring your GraphQL-REST integration effectively involves a structured production setup and reliable tracking tools.

Production Setup#

To ensure consistent deployment, containerize your application with Docker:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV NODE_ENV=production
CMD ["npm", "start"]

Automate the deployment process using CI/CD pipelines for efficiency:

deploy:
  steps:
    - name: Build and test
      run: |
        npm ci
        npm test
        rover subgraph check
    - name: Deploy
      run: |
        docker build -t graphql-gateway .
        docker push graphql-gateway

Once deployed, focus on monitoring key performance indicators to ensure smooth operation.

Usage Tracking#

Zuplo's dashboard offers tools to track your API's health and performance. Key metrics to monitor include:

  • Request volume and latency
  • Error rates by endpoint
  • Query complexity scores
  • Authentication status
  • Rate limit usage

Keep an eye on error rates and request patterns to proactively address potential issues. For distributed tracing, OpenTelemetry can be a powerful tool:

const tracer = opentelemetry.trace.getTracer("graphql-gateway");
const span = tracer.startSpan("resolveMovie");
try {
  const result = await fetchMovieFromRest(id);
  span.setAttributes({ "movie.id": id });
  return result;
} finally {
  span.end();
}

In tandem with monitoring, handle schema updates carefully to ensure backward compatibility.

Schema Updates#

Updating your schema requires a systematic approach to avoid disruptions. Here’s how you can manage it:

  • Add Deprecation Notices
    Mark outdated fields as deprecated to guide developers toward newer alternatives:

    type Movie {
      id: ID!
      title: String!
      rating: Float @deprecated(reason: "Use 'reviewScore' instead")
      reviewScore: Float
    }
  • Track Field Usage
    Use tools like Zuplo's analytics to monitor the usage of deprecated fields and identify impacted clients:

  • Coordinate Updates
    Plan schema changes during off-peak hours and communicate updates clearly via your developer portal. Allow at least 30 days for clients to adapt to breaking changes.

"GraphQL enables frontend developers or consumers of APIs to request the exact data that they need, with no over-fetching or under-fetching." – Selvaganesh

Zuplo also offers additional features to enhance your deployment and monitoring processes, such as:

  • Blue-green deployment support
  • Automated schema validation
  • Real-time performance monitoring
  • Custom domain configuration
  • Edge deployment options

Conclusion#

GraphQL brings a new level of precision and efficiency to working with REST APIs, making data queries more straightforward and effective. Here's a quick recap of the main takeaways and actionable steps to get started.

Key Points#

GraphQL serves as a powerful query layer for REST APIs, offering several clear benefits:

  • Efficient Data Fetching: Clients can request only the data they need, avoiding the common REST pitfalls of over-fetching or under-fetching.
  • Improved Developer Workflow: A strong type system supports features like auto-completion and real-time query validation, making development smoother.
  • Streamlined Architecture: Instead of juggling multiple REST endpoints, GraphQL consolidates access through a single endpoint.
  • Better Performance: By cutting down on unnecessary data transfer, GraphQL boosts application performance - especially important for mobile apps.

Using these advantages as a foundation, you can start integrating GraphQL to enhance your API workflows and see immediate improvements in efficiency.

Getting Started#

Here’s a step-by-step guide to implementing GraphQL alongside your REST APIs:

  • Set Up the Environment: Install Node.js and GraphQL-related packages to prepare your development environment.
  • Design the Schema: Map out a schema that mirrors the structure of your REST API.
  • Implement Resolvers: Create resolvers to connect GraphQL queries to your REST endpoints.
  • Monitor and Optimize: Use tools like those from Zuplo to track essential metrics and refine performance.

Key metrics to monitor include:

  • Request volume and latency
  • Error rates
  • Query complexity
  • Authentication status
  • Rate limit usage

FAQs#

How does integrating GraphQL with REST APIs improve performance?#

Integrating GraphQL with REST APIs can significantly boost performance by cutting down on the number of requests needed to fetch data. With REST, gathering related information often means hitting multiple endpoints. In contrast, GraphQL lets you pull all the data you need in a single query. This approach avoids over-fetching (getting more data than you need) and under-fetching (not getting enough data), as you can specify exactly what to request.

On top of that, GraphQL supports smart caching strategies. By using both client-side and server-side caching, it reduces redundant data transfers, making API interactions faster and more efficient.

What are the best security practices for setting up a GraphQL server with REST API integration?#

To keep your GraphQL server secure when working with REST APIs, start by focusing on authentication and authorization. Using tools like JSON Web Tokens (JWTs) can help manage user access, ensuring that only the right users can execute specific queries or mutations.

To guard against Denial of Service (DoS) attacks, set limits on query depth and complexity, establish timeouts, and validate all incoming data to block injection attempts. Regular security checks, like audits and penetration testing, are also essential for spotting and fixing vulnerabilities.

These measures help protect your GraphQL server while ensuring smooth integration with REST APIs.

How can developers effectively handle errors when querying REST APIs through GraphQL?#

When working with GraphQL to query REST APIs, handling errors effectively requires a focus on structured error responses. Since GraphQL always returns a 200 status code - even when something goes wrong - errors are included in the errors array within the response. This array gives crucial details, such as the type of error and where it occurred in the query. This makes it easier to differentiate between syntax errors (like invalid queries) and execution errors (such as REST API call failures).

To enhance error handling:

  • Write clear and user-friendly error messages that don't reveal sensitive details.
  • Use custom error codes to provide precise feedback and allow for tailored error responses.
  • Rely on logging and monitoring tools to track, analyze, and debug errors effectively.

By adopting these strategies, developers can ensure a smoother user experience while maintaining strong error management practices.