├── .gitignore ├── package.json ├── webpack.config.js ├── src └── helloStuff │ └── index.js ├── serverless.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .serverless/ 2 | node_modules/ 3 | *.code-workspace 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-graphql", 3 | "version": "0.0.1", 4 | "description": "Cloudflare Workers + KV + GraphQL", 5 | "main": "./src/helloStuff/index.js", 6 | "author": "Jon Patell", 7 | "license": "MIT", 8 | "scripts": { 9 | "test": "echo did you mean to run serverless deploy? :D" 10 | }, 11 | "dependencies": { 12 | "form-data": "^2.3.3", 13 | "graphql": "^14.0.2" 14 | }, 15 | "devDependencies": { 16 | "serverless-cloudflare-workers": "APIOpoly/serverless-cloudflare-workers", 17 | "serverless-webpack": "^5.2.0", 18 | "uglifyjs-webpack-plugin": "^2.0.1", 19 | "webpack": "^4.26.0", 20 | "webpack-cli": "^3.1.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | const path = require("path"); 3 | const slsw = require('serverless-webpack'); 4 | module.exports = { 5 | target: "webworker", 6 | mode: "production", 7 | node: { 8 | fs: "empty", 9 | tls: "empty", 10 | net: "empty", 11 | child_process: "empty" 12 | }, 13 | entry: slsw.lib.entries, 14 | output: { 15 | path: __dirname + "/dist", 16 | publicPath: "dist", 17 | filename: '[name].js', 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.mjs$/, 23 | type: "javascript/auto", 24 | use: [] 25 | }, 26 | { 27 | test: /\.js$/, 28 | type: "javascript/auto", 29 | use: [] 30 | } 31 | ] 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/helloStuff/index.js: -------------------------------------------------------------------------------- 1 | import { graphql, buildSchema } from "graphql"; 2 | 3 | addEventListener("fetch", event => { 4 | event.respondWith(handleGraphQLRequest(event)); 5 | }); 6 | 7 | // Stuff schema 8 | // "{ getStuffById(id: "1") { id blob } }" 9 | // "mutation: { stuffIt(id: "1", blob:"this is the right stuff") { id blob } }" 10 | var schema = buildSchema(` 11 | type Stuff { 12 | id: String! 13 | blob: String 14 | } 15 | 16 | type Mutation { 17 | putStuff(id: String!, blob: String): Stuff 18 | } 19 | 20 | type Query { 21 | getStuffById(id: String!): Stuff 22 | } 23 | `); 24 | 25 | var root = { 26 | putStuff: async (input) => { 27 | const { id, blob } = input 28 | const value = await NAMESPACE.put(id, blob) 29 | return { id: id, blob: value || blob } 30 | }, 31 | getStuffById: async (input) => { 32 | const { id } = input 33 | const value = await NAMESPACE.get(id) 34 | return { id: id, blob: value } 35 | } 36 | }; 37 | 38 | export default async function handleGraphQLRequest(event) { 39 | let gql = await event.request.text(); 40 | let response = await graphql(schema, gql, root); 41 | return new Response(JSON.stringify(response)); 42 | } 43 | -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | # serverless.yml 2 | service: 3 | name: hello-stuff 4 | custom: 5 | webpack: 6 | webpackConfig: 'webpack.config.js' # Name of webpack configuration file 7 | includeModules: true # Node modules configuration for packaging 8 | packager: 'yarn' # Packager that will be used to package your external modules 9 | 10 | provider: 11 | name: cloudflare 12 | config: 13 | accountId: ${env:CLOUDFLARE_ACCOUNT_ID} 14 | zoneId: ${env:CLOUDFLARE_ZONE_ID} 15 | namespaceId: ${env:CLOUDFLARE_NAMESPACE_ID} 16 | workers: 17 | hello: 18 | routes: 19 | - example.com/graphql 20 | 21 | plugins: 22 | - serverless-webpack 23 | - serverless-cloudflare-workers 24 | 25 | functions: 26 | helloStuff: 27 | handler: src/helloStuff/index.server 28 | # What the script will be called on Cloudflare 29 | worker: hello 30 | # The name of the script on your machine, omitting the .js file extension 31 | script: src/helloStuff/index 32 | # Events are only relevant to the `serverless invoke` command and don’t affect deployment in any way 33 | events: 34 | - http: 35 | url: example.com/graphql 36 | method: POST 37 | headers: 38 | someKey: someValue 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CloudFlare + Workers KV + GraphQL 2 | ### Because we can. 3 | 4 | This is a *very* simple GraphQL API built on top of Cloudflare Workers KV. 5 | 6 | You may query and mutate items in a single Workers KV namespace by sending application/graphql format POST requests. 7 | 8 | ```graphql 9 | 10 | query getStuff { 11 | getStuffById(id: "1") { 12 | id 13 | blob 14 | } 15 | } 16 | 17 | mutation putStuff { 18 | putStuff(id: "3", blob: "this is even more so the updated right stuff") { 19 | id 20 | blob 21 | } 22 | } 23 | ``` 24 | 25 | To get started: 26 | 27 | Make sure you have enabled Workers and created a KV Namespace. 28 | 29 | Install: 30 | 31 | - `yarn` or `npm i` 32 | 33 | Add your credentials and config: 34 | - modify serverless.yml to add your domain: 35 | 36 | ```yaml 37 | provider: 38 | name: cloudflare 39 | config: 40 | accountId: ${env:CLOUDFLARE_ACCOUNT_ID} 41 | zoneId: ${env:CLOUDFLARE_ZONE_ID} 42 | namespaceId: ${env:CLOUDFLARE_NAMESPACE_ID} 43 | workers: 44 | hello: 45 | routes: 46 | - **example.com**/graphql 47 | ``` 48 | - `export CLOUDFLARE_AUTH_KEY=MY_CF_GLOBAL_API_KEY` 49 | - `export CLOUDFLARE_AUTH_EMAIL=MY_CF_ACCT_EMAIL` 50 | - `export CLOUDFLARE_ACCOUNT_ID=MY_CF_ACT_ID` 51 | - `export CLOUDFLARE_ZONE_ID=A_CF_ZONE_ID` 52 | - `export CLOUDFLARE_NAMESPACE_ID=A_CF_WORKERS_KV_NAMESPACE_ID` 53 | 54 | Deploy it!: 55 | 56 | `serverless deploy` 57 | 58 | You may now launch the editor via the Cloudflare dash and play 59 | 60 | #### **Note:** this proof of concept uses a fork of a fork of the cloudflare-serverless-workers project in order to enable serverless-webpack and Workers KV NAMESPACE binding 61 | --------------------------------------------------------------------------------