npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@kall/ersa

v0.1.0

Published

A GraphQL server built for Cloudflare Workers

Downloads

4

Readme

☁️ Ersa - The quickest way to run GraphQL on Cloudflare Workers

In greek mythology, Ersa is the godess of dew, a lesser known half sister of Apollo. Ersa is also a GraphQL server that is purpose built for Cloudflare Workers. It has none of the cruft of solutions that weren't built for serverless and handles all cases described in the Serving over HTTP part of the GraphQL documentation. It is comparable to and heavily inspired by express-graphql.

  • Tiny: Because your app should get as much of the 1MB worker limit as possible. It only adds 34 KB.
  • Tested: Using a ported version of the express-graphql test suite as well as additional tests for added features.
  • Familiar: Ersa works with any executable graphql schema and supports all applicable configuration options offered by express-graphql.
  • Ready to go: It works in wrangler webpack projects out of the box. No dummy modules, no adapters, no webpack.config.js.
import { createRequestHandler } from "ersa";
import executableSchema from "./schema";

const gqlHandler = createRequestHandler(executableSchema);

addEventListener("fetch", (event) => {
  event.respondWith(gqlHandler(event.request));
});

Bring Your Own Schema

The only thing you need to provide to get started with Ersa is an executable GraphQL schema. The heavy lifting of executing queries is done by the widely used graphql.js library. This means it is compatible with the same type of schema used by other servers. There are a number of great tools to create a compatible schema. Here are a few example options:

Minimalist: graphql.js

The reference GraphQL implementation includes a solid way to create a schema. If you import the required tools directly from the ersa package, it ads no additional bundle size.

import { createRequestHandler } from "ersa";
import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "ersa/graphql";

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: "Query",
    fields: {
      hi: {
        type: GraphQLString,
        args: { name: { type: GraphQLString } },
        resolve: (_root, args) => "Hello " + (args.name ? args.name : "World"),
      },
    },
  }),
});

const gqlHandler = createRequestHandler(schema);

addEventListener("fetch", (event) => {
  event.respondWith(gqlHandler(event.request));
});

Familiar: graphql-tools

graphql-tools allows you to create a schema from a string in the GraphQL Schema Defintion language and an object containing the required resolvers. This is also the way apollo-server defines a schema, so you should be able to bring your schema right over.

import { createRequestHandler } from "ersa";
import { makeExecutableSchema } from "@graphql-tools/schema";

// a trick to let your editor provide language support
const gql = (strings) => strings[0];

const typeDefs = gql`
  type Query {
    hi(name: String): String
  }
`;

const resolvers = {
  Query: { hi: (name) => `Hello ${name ? name : "World"}` },
};

const gqlHandler = createRequestHandler(
  makeExecutableSchema({ typeDefs, resolvers })
);

addEventListener("fetch", (event) => {
  event.respondWith(gqlHandler(event.request));
});

Fancy: Nexus with TypeScript

Note: I wrote this example without trying it. Nexus does not work out of the box with Workers because it requires the fs module.

Nexus is a TypeScript powered schema builder from the prisma team. Like the first example with grapqhl.js, it is code-first (and only). It is more convenient and fully featured and especially powerful when using TypeScript.

import { createRequestHandler } from "ersa";
import { queryType, makeSchema } from "@nexus/schema";

const Query = queryType({
  definition(t) {
    t.string("hi", {
      args: { name: "String" },
      resolve: (_root, args) =>
        `Hello ${args.message ? args.message : "World"}`,
    });
  },
});

const gqlHandler = createRequestHandler(makeSchema({ types: [Query] }));

addEventListener("fetch", (event) => {
  event.respondWith(gqlHandler(event.request));
});

API

Setup

You can import createRequestHandler from ersa or ersa/lean. The function requires a schema as the first argument and takes optional config object as the second. It returns a handler that takes a Request object as the first argument, and an optional object that will be provided as context in resolvers as the second.

If no context is provided, the request is used as context. The choice goes:

context passed to handler > context provided in config > request passed to handler

import { createRequestHandler } from `ersa`
import { createRequestHandler } from `ersa/lean`

// ExecutableSchema is an executable graphql schema
// ConfigObject is documented in the next section
// Request and Response are the native fetch types used by Workers and the browser
const handler = createRequestHandler(schema: ExecutableSchema, config: ConfigObject)

const response: Response = handler(request: Request, context: object)

Options

Ersa supports the same options as express-graphql. Currently the only additional option is allowOrigins. graphiql (+ related) and formatErrors are not supported. schema is pulled out as it's own argument.

  • allowOrigins: Because clouflare workers has not yet established a common middleware pattern, CORS handling is included in Ersa. If you provide a string here, CORS requests will be allowed and the string will be the Access-Control-Allow-Origin header. Pass "*" to allow all, don't include in config to disable.

  • rootValue: A value to pass as the rootValue to the graphql() function from GraphQL.js/src/execute.js.

  • context: A value to pass as the context to the graphql() function from GraphQL.js/src/execute.js. If context is not provided, the request object is passed as the context.

  • pretty: If true, any JSON response will be pretty-printed.

  • extensions: An optional function for adding additional metadata to the GraphQL response as a key-value object. The result will be added to the "extensions" field in the resulting JSON. This is often a useful place to add development time metadata such as the runtime of a query or the amount of resources consumed. This may be an async function. The function is given one object as an argument: { document, variables, operationName, result, context }.

  • validationRules: Optional additional validation rules queries must satisfy in addition to those defined by the GraphQL spec.

  • customValidateFn: An optional function which will be used to validate instead of default validate from graphql-js.

  • customExecuteFn: An optional function which will be used to execute instead of default execute from graphql-js.

  • customFormatErrorFn: An optional function which will be used to format any errors produced by fulfilling a GraphQL operation. If no function is provided, GraphQL's default spec-compliant [formatError][] function will be used.

  • customParseFn: An optional function which will be used to create a document instead of the default parse from graphql-js.

Note: Some option descriptions where copied from express-graphql

Notes

Stay Lean

As an exercise in extreme efficiency, Ersa provides a lean version that is only 26 KB in size. This version skips on validating incoming queries, which is likely a bad choice, but with allow-listing on the roadmap, it may be worth it?

I would like to experiment more with timing parts of the request pipeline and may remove this version if it turns out to be a bad idea. With the low latency and distributed data sources offered by Workers, you can reach latency so low that every millisecond counts. If other features are introduced that increase the size significantly, the lean version will also stay around.

As a bonus, if you use graphql.js to construct your schema and read data from Worker-native sources, you can ship an entire GraphQL microservice in ~30KB.

import { createRequestHandler } from "ersa/lean";
import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "ersa/graphql";

// ... continue like examples above

❤️ express-graphql

It did not start out as one, but currently this project can almost be considered a fork of express-graphql. In particular, the settings API is the same and I have copied their http-tests test suite and adapted it to work against Ersa. In the process of working through the tests, the source code came ever closer to that of express-graphql. If you want to know in which ways Ersa is not yet as correct or fully featured as express-graphql you can check out which tests are currently skipped. The following features are not supported:

  • Solid handling of encoding and compression: will add
  • Configuration as function or promise: may add
  • Pretty printing config with request: eh
  • GraphiQL: will not add (except as extra module)

On Bundling

This library is not bundled in any way and uses the bundler-only flavor of es module imports. This means it will only work with a bundler that will also bundle it. The best way to build a Cloudflare Worker is with the official wrangler CLI and it's automatic webpack setup. This is currently the only tested configuration for Ersa.

Roadmap

The following features are planned or on the wishlist for Ersa

  • TypeScript Declarations
  • Automatic Persisted Queries with Workers KV
  • Naive full GET query caching with the Cache API
  • Allow-listing queries, possibly in combination with persisted queries
  • Batched queries

I would quite like Apollo Tracing, but it seems impossible with the spectre mitigating frozen clocks in Workers.