1. 6
  1.  

  2. 3

    I don’t think this is a bad pattern overall, but it strikes me as an admission of API design failure. With an API built on something like GraphQL, Falcor, etc, you can get round trips down dramatically in a generic fashion for most operations. You can still have FE-specific BEs (or BFFs if you like), but they can probably be pretty thin templating systems or a bundle of custom “mutations” that accompany the generic query endpoint. If your queries become too large/complex, the query part of the BFF system becomes mostly just stored procedures / query cache, or a generic pre-render service, rather than a complete service of its own.

    1. 3

      Exposing an API as powerful as GraphQL to strangers on the internet seems scary to me.

      • What happens when a miscreant crafts a query that joins the biggest set in the DB against itself multiple times? e.g. user { friends { friends { friends { friends { friends { name } } } } } }.
      • What do I do when I find my iPhone app is accidentally emitting a really inefficient query but it will take days at minimum for the App Store to approve the update and every existing user to install it?
      • What’s the likelihood that any given full-blown GraphQL implementation has serious bugs in it just because the attack surface is so large?

      With BFF:

      • There’s no way to submit queries to the BE other than what the BFF decides to generate.
      • Inefficient queries made by the BFF can be fixed and rolled out immediately because they don’t run on anyone else’s devices. Inefficient requests made by the FE are still a problem but hopefully less likely since now you’ve got a pretty clear design guideline that each screen or user interaction should send a small fixed number of requests to the BFF.
      • The attack surface of the BFF can be as small as implementing the specific set of things that you want to do.
      1. 1

        What happens when a miscreant crafts a query that joins the biggest set in the DB against itself multiple times?

        This means you designed a bad API if you allowed joins in you biggest dataset, you can do the exact same mistake in REST too, it’s not a GraphQL problem. It’s not like GraphQL forces you to expose all possible relations between all entities that have them, you could create a schema where it’s only possible to to do user { friends {name}} and no deeper.

        What do I do when I find my iPhone app is accidentally emitting a really inefficient query

        Why are you finding this in production? The queries do not magically get generated by the app in production, you actually wrote them down in a string, just like you would write SQL. But even if this happens, you add special code to one of your resolvers to intercept this particular query (you can inspect the AST) and take a more optimal approach until your fix your app.

        What’s the likelihood that any given full-blown GraphQL implementation has serious bugs in it just because the attack surface is so large?

        Why is the attack surface larger then any other API? Are you talking here about something like graphql-js or sangria or are you talking about systems that use these libs?

        1. 1

          Why are you finding this in production?

          I honestly don’t know where to start with this.

          Perhaps you work on literally any kind of high-value dataset and can’t just have a copy of production to test again?

          Why is the attack surface larger then any other API?

          It’s designed to let you re-use disparate elements of your API. See the thread last week: ‘most security issues come from the intersection of two or more features’.

          1. 1

            I get the part about not having access to the actual production dataset, but the specific discussion there was more about the size of the dataset and queries against that size, and you can generate any size for testing. Also the comment was related to an iPhone app, and the app could have been tested against the live dataset before submitting to the app store. Maybe another way to put it is, yes, you can’t test everything before going to production, but the specific question/problem raised most certainly can be discovered before going to production.

            In relation to attack surface, if you are talking about the possibility of mounting DOS attacks by executing complex queries then yes, i agree with you, a developer will need to put in a lot more thought into defending the api against that, but it’s not impossible to implement. Flexibility comes at a cost. As for other types of attacks like sql injection and things like that, i would say GraphQL is safer because of it’s “types”

            I would also like to mention that the issue of a GraphQL api being more susceptible to dos attacks is not a real problem for most developers/projects, so dismissing the technology just on this would be a mistake. This is a issue only for relatively big players. On the small chance that they do get hit with a dos it’s going to be just some kind of network flood and not a sophisticated thing that researches the the way the api works and finds the week spots.

            1. 1

              Dataset size isn’t enough - you need a similar distribution of string prefixes / numbers for indexed fields (not a RNG output).

              I’m not dismissing GraphQL for being ‘more susceptible to DOS attacks’. I’m dismissing it for organizations where the team writing the server code can sit near the team writing the client code, because the added cost of flexibility adds too little in that situation.

              1. 1

                For the past 5 years i’ve worked on a product where the backend is an API, similar to graphql, that supports all the frontend code (spa and mobile). The team were all in the same office, and yet i strongly disagree with your statement that this type of flexible api has little value in this situation. In fact, i would say it was the only sane way of building an api that has to support nontrivial clients, otherwise every little change in the ui would require a change in the backend as well.

        2. 1

          What happens when a miscreant crafts a query that joins the biggest set in the DB against itself multiple times?

          Just throw an exception: “too many joins” or return a partial data set or something like that. Same thing you do if somebody supplies illegal parameters to some FE specific BFF query.

          What do I do when I find my iPhone app is accidentally emitting a really inefficient query

          What do you do when you find your iPhone app is accidentally in an HTTP request loop? Write some code to detect the situation and mitigate it.

          What’s the likelihood that any given full-blown GraphQL implementation has serious bugs in it just because the attack surface is so large?

          Lower than the odds that multiple ad-hoc BFF services have serious bugs in them just because they form such a large aggregate attack service.

          Overall, I think you’re worrying about problems you already have with any backend design. I’d much rather design, implement, and harden one such service, rather than one per frontend.

        3. 1

          If your queries become too large/complex, the query part of the BFF system becomes mostly just stored procedures / query cache, or a generic pre-render service, rather than a complete service of its own.

          In my experience the generic versions of these just aren’t quite good enough yet. An explicit BFF is sort of an admission of failure in the same way that e.g. a denormalized database schema is - but I just don’t think GraphQL, pre-rendering services etc. are mature enough to rely on yet.