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
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.
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?
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 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.
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
- Building a GraphQL server on the edge with Cloudflare Workers by Kristian Freeman
- Bringing GraphQL to the edge by Kristian Freeman
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
).
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). Usingworker-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));
});