---
title: Expose REST APIs as GraphQL
summary: >-
  Your infrastructure consists of one or more REST APIs and you want to expose a
  unified GraphQL endpoint to fetch and cache data for your next-generation
  applications.
url: >-
  https://www.fastly.com/documentation/solutions/tutorials/compute/expose-rest-apids-graphql
---

[GraphQL](https://graphql.org/) is a typed query language for APIs that allows you to fetch data for your application with rich, descriptive queries. Your API defines a schema that can be used by clients to request exactly the data they need and nothing more, often in one single request to the API.

The [Compute](https://www.fastly.com/documentation/guides/compute) platform allows you to respond to HTTP requests at the edge using a variety of programming languages that compile to [WebAssembly](https://webassembly.org/). For the purposes of this solution, we will use [Rust](https://www.fastly.com/documentation/guides/compute/developer-guides/rust), as it has a rich ecosystem of libraries including the [juniper](https://docs.rs/juniper) GraphQL crate. This allows you to expose a GraphQL endpoint that could be fetching data from multiple backend systems, increasing the pace at which you can build new applications on top of your data stack.

On top of this, you can make use of the [cache override](https://www.fastly.com/documentation/guides/compute/developer-guides/migrate/#controlling-the-cache) interfaces in the [Fastly Rust SDK](https://docs.rs/fastly) to intelligently cache the responses from your backend APIs, reducing latency for your end-users and decreasing the load on your backend servers.

## Instructions

> **IMPORTANT:** This solution assumes that you already have the [Fastly CLI](https://www.fastly.com/documentation/reference/cli/) installed. If you are new to the platform, read our [Getting Started](https://www.fastly.com/documentation/guides/compute/getting-started-with-compute/) guide.

### Initialize a project

If you haven't already created a Rust-based Compute project, run <kbd>fastly compute init</kbd> in a new directory in your terminal and follow the prompts to provision a new service using the [default Rust starter kit](https://www.fastly.com/documentation/solutions/starters/compute-starter-kit-rust-default/):

```term
$ mkdir graphql && cd graphql
$ fastly compute init

Creating a new Compute project.

Press ^C at any time to quit.

Name: [graphql]
Description: A GraphQL processor at the edge
Author: My Name
Language:
[1] Rust
[2] JavaScript
[4] Other ('bring your own' Wasm binary)
Choose option: [1]
Starter kit:
[1] Default starter for Rust
    A basic starter kit that demonstrates routing, simple synthetic responses and
    overriding caching rules.
    https://github.com/fastly/compute-starter-kit-rust-default
[2] Beacon termination
    Capture beacon data from the browser, divert beacon request payloads to a log
    endpoint, and avoid putting load on your own infrastructure.
    https://github.com/fastly/compute-starter-kit-rust-beacon-termination
[3] Static content
    Apply performance, security and usability upgrades to static bucket services such as
    Google Cloud Storage or AWS S3.
    https://github.com/fastly/compute-starter-kit-rust-static-content
Choose option or paste git URL: [1]
```

### Install dependencies

The Rust ecosystem makes available several libraries to parse GraphQL queries, including the [juniper](https://docs.rs/juniper) crate which you will use for this solution.

Add this to your project's `Cargo.toml` file, optionally disabling the `default-features` as they are not required for this solution.

```toml title="Cargo.toml"
juniper = { version = "0.15.10", default-features = false }
```

Juniper will take care of parsing the inbound GraphQL queries, and Fastly can then make the necessary requests to your backend REST API.

When the responses come back, you'll need something to parse those so that they can be presented to the user as a GraphQL response. [`serde`](https://docs.rs/serde) is a Rust crate that provides (de)serialization APIs for common formats. To parse the backend responses' JSON bodies, you will use the [`serde_json`](https://docs.rs/serde_json) crate.

This solution also uses `serde_json` to encode the outgoing JSON responses to GraphQL requests.

> **HINT:** If your backends respond with XML, you could adapt the code in this solution to use the [`serde-xml-rs`](https://docs.rs/serde-xml-rs) crate.

Add these dependencies to your Cargo.toml file:

```toml title="Cargo.toml"
serde = { version = "^1", features = ["derive"] }
serde_json = "^1"
```

### Using the mock backend

To allow you to build this solution without creating your own REST API backend, we've made a mock REST API (using the Compute platform), hosted at `mock.edgecompute.app` that you're welcome to use. The mock server provides two endpoints:

- `GET /users/:id` - Retrieve a user
- `GET /products/:id` - Retrieve a product

Your new GraphQL endpoint can unify these calls so a client application can get both of these types with a single request.

Responses from the mock backend are always JSON objects with an `"ok"` boolean and a `"data"` payload. A request to the `GET /users/:id` endpoint would result in a response like this:

```http context="GET /users/123"
{
    "ok": true,
    "data": {
        "id": "123",
        "name": "Test Bot",
        "email": "me@example.com"
    }
}
```

Make sure to add this backend to your Fastly service, which you can do in either one of the following ways:

- On manage.fastly.com: [Connecting to origins](https://www.fastly.com/documentation/guides/getting-started/hosts/working-with-hosts)

- Using the [Fastly CLI](https://www.fastly.com/documentation/reference/cli/):
  ```term
  $ fastly backend create --name=api_backend --address=mock.edgecompute.app --service-id=<service> --version=<version>
  ```

### Define data types

So how can you model the APIs data types in Rust when the response follows a predictable format? You can build a `BackendResponse` type, which uses a [generic type parameter](https://doc.rust-lang.org/book/ch10-01-syntax.html) `<T>` to allow the encapsulation of both of the documents, removing the need to duplicate the `ok` and `data` parameters when adding more types.

You need to be able to deserialize these types from JSON. By adding the `Deserialize` implementation from `serde`, you will be able to build these types from the responses you get from the backend.

To define these response, user, and product types, add the following definitions to `src/main.rs`:

> **HINT:** If you're starting from scratch, feel free to un-collapse and copy the entire code sample as a replacement for the default main.rs file.

### Make requests to the backend

Now you can define an `ApiClient` type to handle making queries to the backend API:

> **HINT:** If you had multiple backends, you could adapt this code to use the correct backend for each query, and introduce new backend response types if needed.

This is great! You now have an API client that is aware of the shape of your backend data types, and you can invoke it like this:

```rust compile_fail
let backend = ApiClient::new();
let product = backend.get_product("abcdef".to_string())?;
```

### Build the GraphQL schema

Now we can introduce the `juniper` crate, which will build your GraphQL schema and call into your `ApiClient` to fulfil client requests.

First, you need a root query type for the queries to use. This contains the logic that will run to handle an incoming GraphQL query. Your implementation will pass the request on to the API client you built earlier.

You also need to annotate your types with `GraphQLObject`, and change the query response types to `FieldResult` from `juniper`. This allows the crate to derive a GraphQL schema from our Rust types:

### Expose the GraphQL endpoint

We now have a GraphQL schema that `juniper` can work with, so let's work on the main function and have it handle requests to the `POST /graphql` endpoint:

Congratulations! You now have a working GraphQL endpoint running at the edge. If you haven't yet, run the following command to build and deploy your service to the edge:

```term
$ fastly compute publish
✓ Initializing...
✓ Verifying package manifest...
✓ Verifying local rust toolchain...
✓ Building package using rust toolchain...
✓ Creating package archive...

SUCCESS: Built rust package graphql (pkg/graphql.tar.gz)

There is no Fastly service associated with this package. To connect to an existing service
add the Service ID to the fastly.toml file, otherwise follow the prompts to create a
service now.

Press ^C at any time to quit.

Create new service: [y/N] y

✓ Initializing...
✓ Creating service...

Domain: [random-funky-words.edgecompute.app]

Backend (hostname or IP address, or leave blank to stop adding backends): mock.edgecompute.app
Backend port number: [80] 443
Backend name: [backend_1] api_backend

Backend (hostname or IP address, or leave blank to stop adding backends):

✓ Initializing...
✓ Creating domain 'random-funky-words.edgecompute.app'...
✓ Creating backend 'api_backend' (host: mock.edgecompute.app, port: 443)...
✓ Uploading package...
✓ Activating version...

Manage this service at:
        https://manage.fastly.com/configure/services/PS1Z4isxPaoZGVKVdv0eY

View this service at:
        https://random-funky-words.edgecompute.app

SUCCESS: Deployed package (service PS1Z4isxPaoZGVKVdv0eY, version 1)
```

### Serve GraphQL Playground

Wouldn't it be great if there was some easy way to visualize your new graph API? Let's expose [GraphQL Playground](https://github.com/graphql/graphql-playground), which is an in-browser IDE for working with GraphQL services. Helpfully, the source for the playground is built into the `juniper` crate. Let's import this now, and add a route handler for the root path to serve the GraphQL playground source:

Build and deploy your service again, and you should be presented with GraphQL Playground. Explore the schema and run some queries to see data from the backend API served and cached at the edge.

### Next Steps

This solution shows how to use a singular backend for queries, but you could adapt this code to work with multiple backends. You could also perform validation of responses from your backends at the edge to improve the observability of your systems. See the [logging](https://www.fastly.com/documentation/guides/compute/developer-guides/rust/#logging) section of the Rust SDK guide.
