Running RedwoodJS on Cloudflare Workers

A “Cloudflare Worker” is JavaScript you write that runs on Cloudflare’s edge. A “Cloudflare Service Worker” is specifically a worker which handles HTTP traffic and is written against the Service Worker API.

Cloudflare Workers derive their name from Web Workers, and more specifically Service Workers, the W3C standard API for scripts that run in the background in a web browser and intercept HTTP requests. Cloudflare Workers are written against the same standard API, but run on Cloudflare’s servers, not in a browser.

Prisma is currently out of the question

@thedavid

The potential gotchas that keep me from trying:

  • Prisma binary hates v8 runtime
  • Some random dependency in @redwoodjs/api hates v8 runtime
  • Auth provider packages hate v8 runtime
  • Misc API package dependencies used by random Redwood Project X hate v8 runtime

In general, I foresee it being frustrating if my local dev environment spins up the API in a Node runtime so it’s not until I get to deployment that things break, which is then difficult to diagnose.

So I’d suggest starting with local dev for experimentation → I have ZERO knowledge here:

  • Is it possible to spin up a local v8 runtime for dev?
  • Does babel/esbuild support it?

If we can’t do something like this, then I’m not optimistic about creating a workable, um, workflow [Editor’s note: lol] regardless of being able to spin up a test app on Cloudflare using Workers.

Workers do NOT support Node.js.

I believe we can transpile code to the JavaScript version we need. But we can’t control the other dependencies (and their dependencies), which may or may not have Node.js specific requirements + dependencies.

I’ll be cheering you on. But here’s your first roadblock.

@msutkowski

You can specify a web worker output target. But you’re not wrong about various dependencies using non-web worker-friendly methods. An immediate example I can give you there is validation libraries on workers.

Wrangler won’t currently support a WASM build target, and workers don’t currently support TCP - sooooooooo, this is a hard blocker for anything Prisma related.

Deploy just Redwood-Worker-Functions to Cloudflare?

@thedavid

So maybe we have limitations. BUT it would still be rad to know what Workers are good at and what we could use them for. Imagine :rainbow: a Redwood App that takes advantage of a hybrid cloud infrastructure:

  • Cloudflare CDN + Networking (DNS)
  • Specific Redwood-Worker-Functions deployed to Cloudflare
  • Other parts of API deployed to AWS Fargate container for persistence

I don’t know why we’d do this. But it’s possible. ¯_(ツ)_/¯

I do think there’s a lot of value we could unlock in Cloudflare Key/Value and Durable Objects when it comes to performance + caching. Just don’t know what the ROI would be on trying to figure out how to piece it all together.

@peterp

I too very much doubt that Prisma+Cloudflare will happen. Those two products just seem so out of sync, why globally distribute your workers, but then connect to a single database.

I think caching is probably the immediate answer, probably on the API-level. Maybe you have an API that’s exposed via GraphQL, but that’s cached globally. Then… you don’t really need global workers, just global varnish.

Prior Art??

workers-graphql-server

It’s like they read my mind.

An Apollo GraphQL server, built with Cloudflare Workers. Try a demo by looking at a deployed GraphQL playground.

Why this rules: Cloudflare Workers is a serverless application platform for deploying your projects across Cloudflare’s massive distributed network. Deploying your GraphQL application to the edge is a huge opportunity to build consistent low-latency API servers, with the added benefits of “serverless” (I know, the project has server in it):

  • usage-based pricing
  • no cold starts
  • instant, easy-to-use deployment software, using Wrangler

By the way - as a full-stack developer who loves GraphQL, and the developer advocate for Cloudflare Workers, I would love to see what you build with this! Let me know on Twitter!

Usage

You can begin building your own Workers GraphQL server by installing Wrangler, the Workers command-line tool, and generating a new project:

wrangler generate my-graphql-server https://github.com/signalnerve/workers-graphql-server

You’ll need to configure your project’s wrangler.toml file to prepare your project for deployment. See the docs (“Configuring and Publishing”) for a guide on how to do this. Note that you’ll need to find your Cloudflare API keys to set up your config file. The source for this project includes an example external REST data source, and defined types for the PokeAPI, as an example of how to integrate external APIs.

To start using the project, configure your graphQLOptions object in src/index.js:

const graphQLOptions = {
  baseEndpoint: '/', // String
  playgroundEndpoint: '/___graphql', // ?String
  forwardUnmatchedRequestsToOrigin: false, // Boolean
  debug: false, // Boolean
  cors: true, // Boolean or Object to further configure
  kvCache: false // Boolean
}

Endpoints

Make requests to your GraphQL server at the baseEndpoint (e.g. graphql-on-workers.signalnerve.com/) and, if configured, try GraphQL queries at the playgroundEndpoint (e.g. graphql-on-workers.signalnerve.com/___graphql).

Origin forwarding

If you run your GraphQL server on a domain already registered with Cloudflare, you may want to pass any unmatched requests from inside your Workers script to your origin: in that case, set forwardUnmatchedRequestToOrigin to true (if you’re running a GraphQL server on a Workers.dev subdomain, the default of false is fine).

CORS

By default, the cors option allows cross-origin requests to the server from any origin. You may wish to configure it to whitelist specific origins, methods, or headers. To do this, change the cors option to an object:

const graphQLOptions = {
  // ... other options ...

  cors: {
    allowCredentials: 'true',
    allowHeaders: 'Content-type',
    allowOrigin: '*',
    allowMethods: 'GET, POST, PUT',
  }
}

Worker GraphQL

GraphQL on Workers was inspired by this blog post, but using Apollo Server has a massive bundle size or can’t bundle due to dependency on graphql-upload (a node.js dependency). Using worker-graphql resulted in a build of < 50KB.

Installation

npm install @borderless/worker-graphql --save

Usage

import { processGraphQL } from "@borderless/worker-graphql";
import { makeExecutableSchema } from "graphql-tools";

const schema = makeExecutableSchema({
  typeDefs: `
    type Query {
      hello: String
    }
  `,
  resolvers: {
    Query: {
      hello: () => "Hello world!",
    },
  },
});

// Wrap `processGraphQL` with CORS support.
const handler = async (req: Request) => {
  if (req.method.toUpperCase() === "OPTIONS") {
    return new Response(null, {
      status: 204,
      headers: {
        "Access-Control-Allow-Methods": "GET,POST",
        "Access-Control-Allow-Headers":
          req.headers.get("Access-Control-Request-Headers") || "Content-Type",
        "Access-Control-Allow-Origin": "*",
      },
    });
  }

  const res = await processGraphQL(req, { schema });
  res.headers.set("Access-Control-Allow-Origin", "*");
  return res;
};

addEventListener("fetch", (event) => {
  event.respondWith(handler(event.request));
});
4 Likes