How to Turn GraphQL into a High‑Performance Edge CDN Gateway
This article explains how to use GraphQL as a BFF gateway, migrate Apollo Server to Alibaba Cloud EdgeRoutine's serverless environment, implement CDN edge caching for query requests, and build a complete weather‑query demo with TypeScript, GraphQL SDL, resolvers, and Playground debugging.
Introduction
If you are not familiar with GraphQL, you can start with the team’s lectures and articles.
GraphQL official site: https://graphql.cn/
Apollo official site: https://apollographql.com/
Our CCO technology department has adopted GraphQL as the sole standard for describing, exposing, and invoking APIs internally and externally. Major companies like Facebook, Netflix, GitHub, PayPal, Microsoft, Volkswagen, and Walmart also use GraphQL at scale.
1. GraphQL as a Gateway Layer
We consider GraphQL most suitable as a BFF (Backend for Frontend) gateway, aggregating raw HSF interfaces and third‑party RESTful APIs into a Service Façade layer. Its characteristics make it easy to integrate with existing HTTP‑based gateways and to serve as a Serverless/FaaS gateway with a single HTTP trigger.
1.2 GraphQL Gateway and CDN Edge Computing
EdgeRoutine is Alibaba Cloud CDN’s new Serverless platform that provides a ServiceWorker‑like container, leveraging global CDN nodes for high‑availability, high‑performance distributed computing. It is currently free and in trial, with a public release planned for early September.
Query‑type requests dominate traffic, yet each request still hits the backend service even though the data rarely changes.
We propose using EdgeRoutine as a proxy for GraphQL query requests: the first query goes through the CDN to the GraphQL gateway, then to the backend service, and the response is cached at the CDN. Subsequent requests can be served from cache based on TTL rules, dramatically reducing QPS on the origin service.
2. Porting Apollo GraphQL Server to EdgeRoutine
Apollo Server is the most widely used open‑source GraphQL server, but its Node.js version cannot run directly in EdgeRoutine’s ServiceWorker environment. We created apollo-server-edge-routine to adapt it.
2.1 Build TypeScript Development Environment and Scaffold
We set up a TypeScript environment for EdgeRoutine, using a ServiceWorker TypeScript library, Webpack for local debugging, and hot‑reload via WebSocket.
2.2 Implement Custom ApolloServer for EdgeRoutine
import { ApolloServerBase } from 'apollo-server-core';
import { handleGraphQLRequest } from './handlers';
/** Apollo GraphQL Server implementation on EdgeRoutine */
export class ApolloServer extends ApolloServerBase {
/** Listen for GraphQL POST requests on the specified path */
async listen(path = '/graphql') {
this.assertStarted('listen');
addEventListener('fetch', async (event: FetchEvent) => {
const { request } = event;
if (request.method === 'POST') {
const url = new URL(request.url);
if (url.pathname === path) {
const options = await this.graphQLServerOptions();
event.respondWith(handleGraphQLRequest(this, request, options));
}
}
});
}
}2.3 Implement handleGraphQLRequest
import { GraphQLOptions, GraphQLRequest } from 'apollo-server-core';
import { ApolloServer } from './ApolloServer';
/** Parse HTTP request into GraphQL query, execute, and return response */
export async function handleGraphQLRequest(
server: ApolloServer,
request: Request,
options: GraphQLOptions
): Promise<Response> {
let gqlReq: GraphQLRequest;
try {
gqlReq = await request.json();
} catch (e) {
throw new Error('Error occurred when parsing request body to JSON.');
}
const gqlRes = await server.executeOperation(gqlReq);
const response = new Response(JSON.stringify({ data: gqlRes.data, errors: gqlRes.errors }), {
headers: { 'content-type': 'application/json' },
});
for (const [key, value] of Object.entries(gqlRes.http.headers)) {
response.headers.set(key, value);
}
return response;
}3. Simple Weather‑Query GraphQL CDN Proxy Demo
3.1 What We Build
We wrap the third‑party weather service (tianqiapi.com) with a GraphQL CDN gateway. The free tier limits QPS to 300 per day, so we cache the first successful query per city at the CDN edge.
3.2 Weather API Overview
Example request:
Request URL: https://www.tianqiapi.com/free/day?appid={APP_ID}&appsecret={APP_SECRET}&city=%E5%8D%97%E4%BA%AC
Request Method: GET
Status Code: 200 OKExample response (JSON):
{
"air": "94",
"city": "南京",
"cityid": "101190101",
"tem": "31",
"tem_day": "31",
"tem_night": "24",
"update_time": "14:12",
"wea": "多云",
"wea_img": "yun",
"win": "东南风",
"win_meter": "9km/h",
"win_speed": "2级"
}3.3 Define GraphQL SDL
type Query {
versions: Versions!
weatherOfCity(name: String!): Weather!
}
type City {
id: ID!
name: String!
}
type Versions {
api: String!
graphql: String!
}
type Weather {
city: City!
updateTime: String!
code: String!
localized: String!
tempOfDay: Float!
tempOfNight: Float!
}3.4 Implement Resolvers
import { version as graphqlVersion } from 'graphql';
import { apiVersion } from '../api-version';
import { fetchWeatherOfCity } from '../tianqi-api';
export function versions() {
return { api: apiVersion, graphql: graphqlVersion };
}
export async function weatherOfCity(parent, args: { name: string }) {
const raw = await fetchWeatherOfCity(args.name).then(res => res.json());
return {
city: { id: raw.cityid, name: raw.city },
updateTime: raw.update_time,
code: raw.wea_img,
localized: raw.wea,
tempOfDay: raw.tem_day,
tempOfNight: raw.tem_night,
};
}3.5 Create and Start Server
import { ApolloServer } from '@ali/apollo-server-edge-routine';
import typeDefs from '../graphql/schema.graphql';
import * as resolvers from '../resolvers';
const server = new ApolloServer({ typeDefs, resolvers });
server.start().then(() => server.listen());3.6 Build Configuration
tsconfig.json must include the EdgeRoutine lib and types:
{
"compilerOptions": {
"alwaysStrict": true,
"esModuleInterop": true,
"lib": ["esnext", "webworker"],
"module": "esnext",
"moduleResolution": "node",
"outDir": "./dist",
"preserveConstEnums": true,
"removeComments": true,
"sourceMap": true,
"strict": true,
"target": "esnext",
"types": ["@ali/edge-routine-types"]
},
"include": ["src"],
"exclude": ["node_modules"]
}Webpack must polyfill Node.js modules such as assert, buffer, crypto-browserify, etc.
3.7 Add CDN Cache
export async function fetchWeatherOfCity(city: string) {
const url = new URL('http://www.tianqiapi.com/free/day');
url.searchParams.set('appid', '23035354');
url.searchParams.set('appsecret', '8YvlPNrz');
url.searchParams.set('city', city);
const urlString = url.toString();
if (isCacheSupported()) {
const cached = await cache.get(urlString);
if (cached) return cached;
}
const response = await fetch(urlString);
if (isCacheSupported()) cache.put(urlString, response);
return response;
}3.8 Add Playground Debugger
addEventListener('fetch', (event) => {
const response = handleRequest(event.request);
if (response) event.respondWith(response);
});
function handleRequest(request: Request): Promise<Response> | void {
const url = new URL(request.url);
const path = url.pathname;
if (request.method === 'GET' && path === '/graphql') {
return Promise.resolve(new Response(rawPlaygroundHTML, { status: 200, headers: { 'content-type': 'text/html' } }));
}
}3.9 Full Project Code
All code will be open‑sourced on the author’s GitHub after EdgeRoutine’s official release.
Future Outlook
We plan to use EdgeRoutine’s KV database to cache query results and automatically invalidate caches on mutations by leveraging GraphQL’s type‑level IDs. More details can be found in the video at graphcdn.io.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
