Radar 2.0 was built on the learnings of Radar 1.0 and was launched last month during Cloudflare's Birthday Week as a complete product revamp. We wanted to make it easier for our users to find insights and navigate our data, and overall provide a better and faster user experience.
We're building a Supercloud. Cloudflare's products now include hundreds of features in networking, security, access controls, computing, storage, and more.
This blog will explain how we built the new Radar from an engineering perspective. We wanted to do this to demonstrate that anyone could build a somewhat complex website that involves demanding requirements and multiple architectural layers, do it on top of our stack, and how easy it can be.
Hopefully, this will inspire other developers to switch from traditional software architectures and build their applications using modern, more efficient technologies.
High level architecture
The following diagram is a birds-eye view of the Radar 2.0 architecture. As you can see, it's divided into three main layers:
The Core layer is where we keep our data lake, data exploration tools, and backend API.
The Cloudflare network layer is where we host and run Radar and serve the public APIs.
The Client layer is essentially everything else that runs in your browser. We call it the Radar Web app.
As you can see, there are Cloudflare products everywhere. They provide the foundational resources to host and securely run our code at scale, but also other building blocks necessary to run the application end to end.
By having these features readily available and tightly integrated into our ecosystem and tools, at the distance of a click and a few lines of code, engineering teams don't have to reinvent the wheel constantly and can use their time on what is essential: their app logic.
Let's dig in.
Cloudflare Pages
Radar 2.0 is deployed using Cloudflare Pages, our developer-focused website hosting platform. In the early days, you could only host static assets on Pages, which was helpful for many use cases, including integrating with static site generators like Hugo, Jekyll, or Gatsby. Still, it wouldn't solve situations where your application needs some sort of server-side computing or advanced logic using a single deployment.
Luckily Pages recently added support to run custom Workers scripts. With Functions, you can now run server-side code and enable any kind of dynamic functionality you'd typically implement using a separate Worker.
Cloudflare Pages Functions also allow you to use Durable Objects, KV, R2, or D1, just like a regular Worker would. We provide excellent documentation on how to do this and more in our Developer Documentation. Furthermore, the team wrote a blog on how to build a full-stack application that describes all the steps in detail.
Radar 2.0 needs server-side functions for two reasons:
To render Radar and run the server side of Remix.
To implement and serve our frontend API.
Remix and Server-side Rendering
We use Remix with Cloudflare Pages on Radar 2.0.
Remix follows a server/client model and works under the premise that you can't control the user's network, so web apps must reduce the amount of Javascript, CSS, and JSON they send through the wire. To do this, they move some of the logic to the server.
In this case, the client browser will get pre-rendered DOM components and the result of pre-fetched API calls with just the right amount of JSON, Javascript, and CSS code, rightfully adjusted to the UI needs. Here’s the technical explanation with more detail.
Typically, Remix would need a Node.js server to do all of this, but guess what: It can also run on Cloudflare Workers and Pages.
Here’s the code to get the Remix server running on Workers, using Cloudflare Pages:
import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
import * as build from "@remix-run/dev/server-build";
const handleRequest = createPagesFunctionHandler({
build: {
...build,
publicPath: "/build/",
assetsBuildDirectory: "public/build",
},
mode: process.env.NODE_ENV,
getLoadContext: (context) => ({
...context.env,
CF: (context.request as any).cf as IncomingRequestCfProperties | undefined,
}),
});
const handler: ExportedHandler<Env> = {
fetch: async (req, env, ctx) => {
const r = new Request(req);
return handleRequest({
env,
params: {},
request: r,
waitUntil: ctx.waitUntil,
next: () => {
throw new Error("next() called in Worker");
},
functionPath: "",
data: undefined,
});
},
};
In Remix, routes handle changes when a user interacts with the app and changes it (clicking on a menu option, for example). A Remix route can have a loader, an action and a default export. The loader handles API calls for fetching data (GET method). The action handles submissions to the server (POST, PUT, PATCH, DELETE methods) and returns the response. The default export handles the UI code in React that’s returned for that route. A route without a default export returns only data.
Because Remix runs both on the server and the client, it can get smart and know what can be pre-fetched and computed server-side and what must go through the network connection, optimizing everything for performance and responsiveness.
Here’s an example of a Radar route, simplified for readability, for the Outage Center page.
import type { MetaFunction } from "@remix-run/cloudflare";
import { useLoaderData } from "@remix-run/react";
import { type LoaderArgs } from "@remix-run/server-runtime";
export async function loader(args: LoaderArgs) {
const ssr = await initialFetch(SSR_CHARTS, args);
return { ssr, };
}
export default function Outages() {
const { ssr } = useLoaderData<typeof loader>();
return (
<Page
filters={["timerange"]}
title={
<>
<Svg use="icon-outages" />
{t("nav.main.outage-center")}
</>
}
>
<Grid columns={[1, 1, 1, 1]}>
<Card.Article colspan={[1, 1, 1, 1]} rowspan={[1, 1, 1, 1]}>
<Card.Section>
<Components.InternetOutagesChoropleth ssr={ssr} />
</Card.Section>
<Divider />
<Card.Section>
<Components.InternetOutagesTable ssr={ssr} />
</Card.Section>
</Card.Article>
</Grid>
</Page>
);
}
And here’s what it produces:
Remix and SSR can also help you with your Lighthouse scores and SEO. It can drastically improve metrics like Cumulative Layout Shift, First Contentful Paint and Largest Contentful Paint by reducing the number of fetches and information traveling from the server to the browser and pre-rendering the DOM.
Another project porting their app to Remix is Cloudflare TV. This is how their metrics looked before and after the changes.
Radar’s Desktop Lighthouse score is now nearly 100% on Performance, Accessibility, Best Practices, and SEO.
Another Cloudflare product that we use extensively on Radar 2.0 is Speed. In particular, we want to mention the Early Hints feature. Early Hints is a new web standard that defines a new HTTP 103 header the server can use to inform the browser which assets will likely be needed to render the web page while it's still being requested, resulting in dramatic load times improvements.
You can use Cloudflare Pages with Early Hints.
APIs
Radar has two APIs. The backend which has direct access to our data sources, and the frontend, which is available on the Internet.
Backend API
The backend API was written using Python, Pandas and FastAPI and is protected by Cloudflare Access, JWT tokens and an authenticated origin pull (AOP) configuration. Using Python allows anyone on the team, engineers or data scientists, to collaborate easily and contribute to improving and expanding the API, which is great. Our data science team uses JupyterHub and Jupyter Notebooks as part of their data exploration workflows, which makes prototyping and reusing code, algorithms and models particularly easy and fast.
It then talks to the upstream frontend API via a Strawberry based GraphQL server. Using GraphQL makes it easy to create complex queries, giving internal users and analysts the flexibility they need when building reports from our vast collection of data.
Frontend API
We built Radar's frontend API on top of Cloudflare Workers. This worker has two main functions:
It fetches data from the backend API using GraphQL, and then transforms it.
It provides a public REST API that anyone can use, including Radar.
Using a worker in front of our core API allows us to easily add and separate microservices, and also adds notable features like:
Cloudflare's Cache API allows finer control over what to cache and for how long and supports POST requests and customizable cache control headers, which we use.
Stale responses using R2. When the backend API cannot serve a request for some reason, and there’s a stale response cached, it’ll be served directly from R2, giving end users a better experience.
CSV and JSON output formats. The CSV format is convenient and makes it easier for data scientists, analysts, and others to use the API and consume our API data directly from other tools.
Open sourcing our OpenAPI 3 schema generator and validator
One last feature on the frontend API is OpenAPI 3 support. We automatically generate an OpenAPI schema and validate user input with it. This is done through a custom library that we built on top of itty-router, which we also use. Today we’re open sourcing this work.
itty-router-openapi provides an easy and compact OpenAPI 3 schema generator and validator for Cloudflare Workers. Check our GitHub repository for more information and details on how to use it.
Developer’s Documentation
Today we’re also launching our developer’s documentation pages for the Radar API where you can find more information about our data license, basic concepts, how to get started and the available API methods. Cloudflare Radar's API is free, allowing academics, data sleuths and other web enthusiasts to investigate Internet usage across the globe, based on data from our global network.
To facilitate using our API, we also put together a Colab Notebook template that you can play with, copy and expand to your use case.
The Radar App
The Radar App is the code that runs in your browser. We've talked about Remix, but what else do we use?
Radar relies on a lot of data visualizations. Things like charts and maps are essential to us. We decided to build our reusable library of visualization components on top of two other frameworks: visx, a "collection of expressive, low-level visualization primitives for React," D3, a powerful JavaScript library for manipulating the DOM based on data, and MapLibre, an open-source map visualization stack.
Here’s one of our visualization components in action. We call it the “PewPew map”.
And here’s the Remix React code for it, whenever we need to use it in a page:
<Card.Section
title={t("card.attacks.title")}
description={t("card.attacks.description")}
>
<Flex gap={spacing.medium} align="center" justify="flex-end">
<SegmentedControl
label="Sort order:"
name="attacksDirection"
value={attacksDirection}
options={[
{ label: t("common.source"), value: "ORIGIN" },
{ label: t("common.target"), value: "TARGET" },
]}
onChange={({ target }: any) => setAttacksDirection(target.value)}
/>
</Flex>
<Components.AttacksCombinedChart
ssr={ssr}
height={400}
direction={attacksDirection}
/>
</Card.Section>
SVGs
Another change we made to Radar was switching our images and graphical assets to Scalable Vector Graphics. SVGs are great because they're essentially a declarative graphics language. They're XML text files with vectorial information. And so, they can be easily manipulated, transformed, stored, or indexed, and of course, they can be rendered at any size, producing beautiful, crisp results on any device and resolution.
SVGs are also extremely small and efficient in size compared to bitmap formats and support internationalization, making them easier to translate to other languages (localization), thus providing better accessibility.
Here’s an example of a Radar Bubble Chart, inspected, where you can see the SVG code and the strings embedded.
Cosmos
React Cosmos is a "sandbox for developing and testing UI components in isolation." We wanted to use Cosmos with Radar 2.0 because it's the perfect project for it:
It has a lot of visual components; some are complex and have many configuration options and features.
The components are highly reusable across multiple pages in different contexts with different data.
We have a multidisciplinary team; everyone can send a pull request and add or change code in the frontend.
Cosmos acts as a component library where you can see our palette of ready-to-use visualizations and widgets, from simple buttons to complex charts, and you play with their options in real-time and see what happens. Anyone can do it, not only designers or engineers but also other project stakeholders. This effectively improves team communications and makes contributing and iterating quickly.
Here’s a screenshot of our Cosmos in action:
Continuous integration and development
Continuous integration is important for any team doing modern software. Cloudflare Pages provides multiple options to work with CI tools using direct uploads, out of the box. The team has put up documentation and examples on how to do that with GitHub Actions, CircleCI, and Travis, but you can use others.
In our case, we use BitBucket and TeamCity internally to build and deploy our releases. Our workflow automatically builds, tests, and deploys Radar 2.0 within minutes on an approved PR and follow-up merge.
Unit tests are done with Vitest and E2E tests with Playwright. Visual Regression testing is planned and Playwright can also help with that.
Furthermore, we have multiple environments to stage and test our releases before they go live to production. Our CI/CD setup makes it easy to switch from one environment to the other or quickly roll back any undesired deployment.
Again Cloudflare Pages makes it easy to do this using Preview deployments, aliases, or Branch build controls. The same is true for regular Workers using Environments.
Fast previews and notifications
Radar 1.0 wasn't particularly fast doing CI/CD, we confess. We had a few episodes when a quick fix could take some good 30 minutes from committing the code to deployment, and we felt frustrated about it.
So we invested a lot in ensuring that the new CI would be fast, efficient, and furious.
One cool thing we ended up doing was fast preview links on any commit pushed to the code repository. Using a combination of intelligent caching during builds and doing asynchronous tests when the commit is outside the normal release branches, we were able to shorten the deployment time to seconds.
This is the notification we get in our chat when anyone pushes code to any branch:
Anyone can follow a thread for a specific branch in the chat and get notified on new changes when they happen.
Blazing-fast builds, preview links and notifications are game-changers. An engineer can go from an idea or a quick fix to showing the result on a link to a product manager or another team member. Anyone can quickly click the link to see the changes on a fully working end-to-end version of Radar.
Accessibility and localization
Cloudflare is committed to web accessibility. Recently we announced how we upgraded Cloudflare’s Dashboard to adhere to industry accessibility standards, but this premise is valid for all our properties. The same is true for localization. In 2020, we internationalized our Dashboard and added support for new languages and locales.
Accessibility and localization go hand in hand and are both important, but they are also different. The Web Content Accessibility Guidelines define many best practices around accessibility, including using color and contrast, tags, SVGs, shortcuts, gestures, and many others. The A11Y project page is an excellent resource for learning more.
Localization, though, also known as L10n, is more of a technical requirement when you start a new project. It's about making sure you choose the right set of libraries and frameworks that will make it easier to add new translations without engineering dependencies or code rewrites.
We wanted Radar to perform well on both fronts. Our design system takes Cloudflare's design and brand guidelines seriously and adds as many A11Y good practices as possible, and the app is fully aware of localization strings across its pages and UI components.
Adding a new language is as easy as translating a single JSON file. Here's a snippet of the en-US.json file with the default American English strings:
{
"abbr.asn": "Autonomous System Number",
"actions.chart.download.csv": "Download chart data in CSV",
"actions.chart.download.png": "Download chart in PNG Format",
"actions.chart.download.svg": "Download chart in SVG Format",
"actions.chart.download": "Download chart",
"actions.chart.maximize": "Maximize chart",
"actions.chart.minimize": "Minimize chart",
"actions.chart.share": "Share chart",
"actions.download.csv": "Download CSV",
"actions.download.png": "Download PNG",
"actions.download.svg": "Download SVG",
"actions.share": "Share",
"alert.beta.link": "Radar Classic",
"alert.beta.message": "Radar 2.0 is currently in Beta. You can still use {link} during the transition period.",
"card.about.cloudflare.p1": "Cloudflare, Inc. ({website} / {twitter}) is on a mission to help build a better Internet. Cloudflare's suite of products protects and accelerates any Internet application online without adding hardware, installing software, or changing a line of code. Internet properties powered by Cloudflare have all web traffic routed through its intelligent global network, which gets smarter with every request. As a result, they see significant improvement in performance and a decrease in spam and other attacks. Cloudflare was named to Entrepreneur Magazine's Top Company Cultures 2018 list and ranked among the World's Most Innovative Companies by Fast Company in 2019.",
"card.about.cloudflare.p2": "Headquartered in San Francisco, CA, Cloudflare has offices in Austin, TX, Champaign, IL, New York, NY, San Jose, CA, Seattle, WA, Washington, D.C., Toronto, Dubai, Lisbon, London, Munich, Paris, Beijing, Singapore, Sydney, and Tokyo.",
"card.about.cloudflare.title": "About Cloudflare",
...
You can expect us to release Radar in other languages soon.
Radar Reports and Jupyter notebooks
Radar Reports are documents that use data exploration and storytelling to analyze a particular theme in-depth. Some reports tend to get updates from time to time. Examples of Radar Reports are our quarterly DDoS Attack Trends, or the IPv6 adoption.
The source of these Reports is Jupyter Notebooks. Our Data Science team works on some use-case or themes with other stakeholders using our internal Jupyter Hub tool. After all the iteration and exploration are done, and the work is signed off, a notebook is produced.
A Jupyter Notebook is a JSON document containing text, source code, rich media such as images or charts, and other metadata. It is the de facto standard for presenting data science projects, and every data scientist uses it.
With Radar 1.0, converting a Jupyter Notebook to a Radar page was a lengthy and manual process implicating many engineering and design resources and causing much frustration to everyone involved. Even updating an already-published notebook would frequently cause trouble for us.
Radar 2.0 changed all of this. We now have a fully automated process that takes a Jupyter Notebook and, as long as it's designed using a list of simple rules and internal guidelines, converts it automatically, hosts the resulting HTML and assets in an R2 bucket, and publishes it on the Reports page.
The conversion to HTML takes into account our design system and UI components, and the result is a beautiful document, usually long-form, perfectly matching Radar's look and feel.
We will eventually open-source this tool so that anyone can use it.
More Cloudflare, less to worry about
We gave examples of using Cloudflare's products and features to build your next-gen app without worrying too much about things that aren't core to your business or logic. A few are missing, though.
Once the app is up and running, you must protect it from bad traffic and malicious actors. Cloudflare offers you DDoS, WAF, and Bot Management protection out of the box at a click's distance.
For example, here are some of our security rules. This is traffic we don't have to worry about in our app because Cloudflare detects it and acts on it according to our rules.
Another thing we don't need to worry about is redirects from the old site to the new one. Cloudflare has a feature called Bulk Redirects, where you can easily create redirect lists directly on the dashboard.
It's also important to mention that every time we talk about what you can do using our Dashboard, we're, in fact, also saying you can do precisely the same using Cloudflare's APIs. Our Dashboard is built entirely on top of them. And if you're the infrastructure as code kind of person, we have you covered, too; you can use the Cloudflare Terraform provider.
Deploying and managing Workers, R2 buckets, or Pages sites is obviously scriptable too. Wrangler is the command-line tool to do this and more, and it goes the extra mile to allow you to run your full app locally, emulating our stack, on your computer, before deploying.
Final words
We hope you enjoyed this Radar team write-up and were inspired to build your next app on top of our Supercloud. We will continue improving and innovating on Radar 2.0 with new features, share our findings and open-sourcing our tools with you.
In the meantime, we opened a Radar room on our Developers Discord Server. Feel free to join it and ask us questions; the team is eager to receive feedback and discuss web technology with you.
You can also follow us on Twitter for more Radar updates.