Bright ideas and techniques for building with Convex.
Profile image
Jamie Turner
a year ago

Edge to Butt: Wrappers as "Middleware"

Exciting news! There is an easier way to customize your queries, mutations, and actions. Check out this post to see how to use customFunction helpers from the convex-helpers npm package. It's it great living on the butt?

The Edge

I loved reading the informative and useful Wrappers as Middleware: Authentication from Ian Macartney. But it’s time to get serious and Enterprise in here. Let’s focus on a piece of Convex middleware real projects need.

Years ago, an inspired soul created a “Cloud to Butt” movement wherein millions of Internet denizens installed a cheeky Chrome extension. Within Chrome, this extension transformed all web content uses of the word “cloud”, or “the cloud”, to "butt", “my butt”, and so on. Resulting in childishness like this:


This whole phenomenon was clearly a cheap attempt to capitalize on our collective cynical, weary leeriness at the growing ubiquity of the word “cloud”. Everything we used to just call the Internet, or servers, or whatever, started being labeled “the cloud” one day by breathless digital marketers. So we responded by channeling our inner seven-year-olds and using potty humor.

But it’s not 2014, it’s 2023. We’ve all matured, and we’ve even come to embrace the term cloud as a warm friend–of course the cloud is here, and it generously runs everything for us for an oh-so reasonable fee, and we embrace it! We wouldn’t dream of offending the cloud!

Now the new intruder we’re rolling our eyes at is “the edge”. The edge is nothing like the cloud. It’s mysterious and coming for us. Everyone is starting to say it and we’re starting to feel inadequate that we’re not using it enough or talking about it the right way at parties!

Problem solved.

Introducing the newest piece of Convex middleware, Edge-to-Butt. Just drop this little beauty into your Convex queries and mutations, and your website will thumb its nose at this snot-nosed newcomer they're calling “the edge”.

First, let’s write a function that traverses any object or array recursively and looks for strings:

// Recursively explore objects/arrays and scalar values, looking for strings
// to transform from 'edge' into 'butt'.
function buttify(value: any): any {
  const isArr = Array.isArray(value);
  if (isArr) {
		// recurse for all items in the array
    value.forEach((v, i) => {
      value[i] = buttify(v);
    return value;
  const valueType = typeof value;
  if (valueType === "object") {
    for (var key of Object.keys(value)) {
			// recurse for all fields on the object
      value[key] = buttify(value[key]);
    return value;
  if (valueType === "string") {
    // String! replace "edge" with "butt", as one does.
    return value.replace(/(^|\W)(edge)(\W|$)/gim, caseStableButtification);
  return value;

Now, the key to this whole system is this caseStableButtification function. This is Enterprise software, so we need to get the details right. We want to make sure we only change the word ‘edge’, not ‘ledge’. And we want to preserve the case, so if someone says “Edge computing is the future”, we’ll want a capital B on that baby.

Here’s what that function looks like, operating on the groups of our matched regex:

// Convert 'edge'  to 'butt' while preserving case and surrounding syntax.
function caseStableButtification(
  _: string,
  prefix: string,
  edge: string,
  suffix: string
): string {
  const fixEdge = (s: string): string => {
    var buttLetters = [...s].map((l, i) => {
      if (l.toLocaleUpperCase() === l) {
        return BUTT.charAt(i).toLocaleUpperCase();
      } else {
        return BUTT.charAt(i);
    return buttLetters.join("");
  return prefix + fixEdge(edge) + suffix;

Finally, we need our Convex wrapper, and we'll use it on the standard listMessages query from the Convex tutorial:

 * Wrapper for Convex query or mutation functions turns all use of "edge" to
 * butt.
 * @param - func: your Convex query function.
 * @returns A return value with all strings having "edge" transformed
 * into "butt".
export const withEdgeToButt = (func: any) => {
  return async (...args: any[]) => {
    let result = await func(...args);
    return result;

// Retrieve all chat messages from the database with a sprinkle of 7-year old humor.
export default query(
  withEdgeToButt(async ({ db }: any) => {
    console.log("getting messages again");
    return await db.query("messages").collect();

On the edge of your seat?

Are we ready for production? You tell me:

Screenshot of the final app

I’m sure for many of you, this was the final Convex capability you were waiting for before taking the leap and using Convex on your next project. Please be patient with the team while we manage the influx of interest. Your call is important to us.

Build in minutes, scale forever.

Convex is the backend application platform with everything you need to build your project. Cloud functions, a database, file storage, scheduling, search, and realtime updates fit together seamlessly.

Get started