31

Recently I learned about GraphQL which claims to be superior to RESTful. However, I started wondering why don't we simply put SQL statements into an HTTP GET request.

For example, in GraphQL I would write

{
  Movie(id: "cixos5gtq0ogi0126tvekxo27") {
    id
    title
    actors {
      name
    }
  }
}

Which isn't much simpler than its SQL counterpart

SELECT id, title FROM movies 
  WHERE id = cixos5gtq0ogi0126tvekxo27;

SELECT actors.name FROM actors, actors_movies 
  WHERE actors.id == movies.actor_id 
  AND movie.id == cixos5gtq0ogi0126tvekxo27;

Maybe we can URL-encode the query and send to the server

GET endpoint?q=SELECT%20id%2C%20title%20FROM%20movies%20WHERE%20id%20%3D%20cixos5gtq0ogi0126tvekxo27%3B%0ASELECT%20actors.name%20FROM%20actors%2C%20actors_movies%20WHERE%20actors.id%20%3D%3D%20movies.actor_id%20AND%20movie.id%20%3D%3D%20cixos5gtq0ogi0126tvekxo27%3B HTTP/1.1

Yes, the query URL can be too long, but you can put it into the body of a POST request if you don't care about REST compliance. (By the way, I think the HTTP RFC need be revised for REST to make sense: capping the length of query strings mixes implementation with specification at the very beginning)

Directly issuing SQL from the client also has the advantage of

  1. No server-side code/library is required to parse GraphQL, reducing development time.
  2. No server-side overhead is needed to parse GraphQL, reducing runtime.
  3. SQL statements are much more flexible than GraphQL because (in most cases) the latter will reduce to SQL anyway.
  4. Everyone knows SQL.

So, what the advantages GraphQL have over SQL?

23
  • 47
    Little Bobby Tables. – Philip Kendall Apr 6 '19 at 7:05
  • 5
    1. I can still DoS you with arbitrarily complicated SQL queries. 2. There's no chance a malicious actor would ever obtain a valid key... – Philip Kendall Apr 6 '19 at 7:16
  • 4
    @PhilipKendall You are right, but using GraphQL (or REST or whatever) doesn't solve these problems either, right? – nalzok Apr 6 '19 at 7:18
  • 11
    @nalzok: SQL is Turing-complete, which means it is impossible to validate statically. – Jörg W Mittag Apr 6 '19 at 7:45
  • 3
    This is very simple to understand why it's a terrible idea. Implement it yourself. At some point, you will realise that your are investing the time mostly in 1 thing: security. Not too later you will feel somewhat upset because you are implementing a caped TOAD. Then you will realise how hard is mapping rows all over the system and you will try to reinvent the ORM wheel on both sides: client and server. By the time you give up, your PM will ask you for report: how is the users' service going? Is it done?"... – Laiv Apr 6 '19 at 12:24
41

Basically, abstraction.

SQL requires your clients to know your exact database structure, which is not good. On top of that, analysing the SQL in order to perform special operations based on the value sent as the input is a really difficult thing to do. There are entire softwares which are pretty much responsible only for that. Do you know what those are? If you have guessed the databases, you are right.

Thanks to not exposing the SQL directly, you are not limiting the consumer of the API to the internal representation of your database. You easily expose only what you want to expose.

And since clients of the API depend only on the abstraction, you are free to have as many layers as possible between the API input and the actual database (security, caching, loading data from multiple databases on a single request,...).

For public services, exposing a database directly is pretty much never the right approach. If you however have a few internal systems, sure, your approach might make sense but even then it might just be easier to connect to application A's database directly from Application B by giving the database credentials to the Application B, rather than trying to come up with a custom HTTP interface for the database SQL language.


Why can't I just compare the URL (or SQL query) against keys in Redis before performing the actual query on the RDBMS?

Because it's not easy. Even if someone uses a very simple query, such as:

SELECT st.id, jt.name
FROM some_table st
INNER JOIN join_table jt ON jt.some_table_id = st.id
WHERE st.name = 'hello
world' AND st.type = 'STANDARD'

how do you make sure the result is properly cached? This query includes newlines, but someone could just as well write the query in the following way:

SELECT st.id, jt.name FROM some_table st INNER JOIN join_table jt ON jt.some_table_id = st.id WHERE st.name = 'hello
world' AND st.type = 'STANDARD'

and it's still supposed to be cached in the same way as the one above. I have specifically included a where in which a string search contains a new line, so simply finding line endings and replacing them with a space is not going to work here, parsing the request correctly would be much more complicated.

And even if you do fix that, another query could switch the order of conditions and the query would look like this:

SELECT st.id, jt.name
FROM some_table st
INNER JOIN join_table jt ON jt.some_table_id = st.id
WHERE st.type = 'STANDARD' AND st.name = 'hello
world'

and another request could contain a redundant WHERE argument, like this:

SELECT st.id, jt.name
FROM some_table st
INNER JOIN join_table jt ON jt.some_table_id = st.id
WHERE st.type = 'STANDARD' AND st.name = 'hello
world' AND st.stype = 'STANDARD'

All of those queries are still supposed to return the same result, should be cached in the same way. But handling all of the possible options is pretty much impossible. That's why you cannot simply compare the URL against keys in Redis.

4
  • This is a nice answer, but please see the update. – nalzok Apr 7 '19 at 4:16
  • how do you make sure the result is properly cached? parse it into an AST and hash that. Not that you would want to do that which the DBMS is probably already doing. – StingyJack Dec 21 '20 at 18:52
  • and GraphQL doesn't require you to know your GraphQL structure? – user253751 Dec 22 '20 at 18:33
  • @user253751, what I meant by that sentence is the inversion: by exposing SQL directly, you're pretty much required to expose all your tables - which is detailed in the next paragraph. Having a control of your underlying layer without exposure is extremely important, because it makes refactoring much easier, since clients do not depend on the implementation (the DB structure), they rely on the abstraction you've provided. ACL is better implemented in the application layer, along with other mechanism which prevent unwanted access to the data. – Andy Dec 22 '20 at 19:08
25

In theory there is no reason you can't expose an SQL interface like this.

In practice SQL is far too powerful to be effectively limited to the security scope you want to expose.

Even if you allow only read access, a bad query can still hog resources.

Other languages such as graphQL are designed to be exposed. They are merely giving users a filter option on what they could already see.

The benefit of using these languages is that they have gone through all the things you would want to stop users doing in SQL and taken them off the table.

7
  • 5
    Thanks for the answer, but could you explain how GraphQL solve the problem of resource draining? A rogue GraphQL query can still say “tell me everything about each movie and their actors“, resulting in a huge graph, and exhausting my DBMS and network. – nalzok Apr 7 '19 at 3:14
  • But i can write a recursive SQL query that will lock your table and prevent other users from running any queries at all – Ewan Apr 7 '19 at 11:30
  • 5
    the problem is not so much restricting access to tables, or deleting, but the shear complexity of SQL. will you allow temp table creation? what about executing CLI? loops? transactions? sub selects? cursors? how will you distinguish when the use of these things is acceptable and when its 'bad' – Ewan Apr 7 '19 at 11:38
  • 1
    @Ewan, if there was a serious attempt to restrict the power of SQL, wouldn't that be done by implementing a parser for SQL and attaching additional security controls, rather than implementing a(nother) special query language whose only selling point is that it is less powerful? – Steve Dec 22 '20 at 8:00
  • tricky if you have single commands that do both allowed and non-allowed operations. It's easier to create a new language from the ground up – Ewan Dec 22 '20 at 8:38
5

As others have mentioned, exposing SQL directly in the api is a very bad option. GraphQL, despite it's name, is not an abstraction for SQL, but for any data store or even other services.

If you are looking for an abstraction that is closer to SQL, you might want to have a look at odata (if you happen to work in .NET backends, though maybe other implementations exist).

2

Because almost nobody understand that SQL != RDBMS != Relational model.

SQL is just a query language. It does not even need a relational database system. It is not fully relational either.

It also neither demand that is implemented any more fancy that simple CRUD, and not even need to implement CRUD. It happily is used as just read-only query data back pls.

Is just an (happy) accident that SQL + RDBMS is such a powerful combo that both become conflated. Also, that SQL is standardized across so many RDBMS that some of the capabilities of them surface, meaning that it can look more complex that something anemic like GraphQL or Rest can even dream.

Also, that is extended to be crud + schema manipulation + scripting + anything anybody wants or needs.

SQL is THAT FLEXIBLE.

So, is totally valid to put SQL on top of HTTP and that not means that MUST be directly feed to the database. Can be, totally, alike GraphQL and have a in-memory resolver that do whatever it need to be transformed, later, to the "real" SQL of the RDBMS or ANYTHING ELSE, because is part of the relational model foundational designs to be a abstraction over storage and query.

That is why, you use SQL (tables) on top of Trees (B-Tree) or arrays (Columnar) or Graphs, or files, or whatever on the sun.

0

if you want expose SQL like GraphQL, you will could need something like GraphQL, because you will need hide the important information and select what you want to show in the API, this for security.

GraphQl and SQL are different things, SQL is the language to query DataBase and GraphQL is only to manage the data from API,in API you will need make yours schemas to show and querys to manage it, etc.

in any API you will need make those things for simply security, but if you want something that is free data access maybe it would work, you know so many alternatives in the software world

Not the answer you're looking for? Browse other questions tagged or ask your own question.