 Hey folks, last of the day. So I'm happy to be here again after I think three, four years when we talk a little bit about the work that we were doing with Haskell long time ago when we first started using it. I'm going to talk a little bit about what we do at Haskell and kind of our experience using Haskell at Haskell and I hope that kind of this talk gives you a nice sense of the features of Haskell that are useful to us. Really simple ideas and simple topics but maybe hopefully should be an interesting case study for you. So before I get started I'll just like to quickly sample the audience so how many of you write Haskell regularly? Okay, how many of you have dabbled with Haskell a little bit but don't really regularly write it? Okay, cool. And how many of you have heard of GraphQL? How many of you use GraphQL? Alright, love it. Perfect. So I'm Tanmay. I'm one of the founders at Haskell. We have an open source project that we call the GraphQL engine and I'll kind of talk about what that does. I'll start with a demo to kind of motivate the idea behind what we do and then get into how we use Haskell. I'm going to cover a little bit of a little introduction to what Haskell does maybe a brief intro to what GraphQL itself is for those of you who are not familiar with it and then the architecture that we have and why we've built Haskell the way we built it. I'm going to talk a little bit about the compiler pipeline touch upon two key ideas. The ideas of how ADT makes it very easy to kind of represent syntax of a different language and how ASD transformations are particularly easy for us because of the compilation or transpilation step that we do and then I'm going to go into the real-time component that we have and how that was also spectacularly easy to build because of exploiting STM constructs in Haskell and then I'm going to do a quick review of the stuff that we use our tech stack, our libraries. I'll talk a little bit about the problems and the challenges that we have now going forward and then kind of wrap the whole thing up together. We'll do questions towards the end. I also have Bamshi here with me who is the lead architect. He honestly knows ten times more than me about everything that's happening here. Cool. Okay. So what we do at Hasura is that we want to give users, teams and application developers a GraphQL API instantly on top of an existing or a new Postgres database. And so the idea is that if you're building a mobile application or a web application and you want to use APIs from your database you don't have to write cruddy real-time logic. This entity kind of just sits in the middle in that little architecture diagram and that kind of replaces a chunk of the business logic that you would have written, especially if it's just cruddy or real-time. We launched this as an open-source project about four months ago. We started writing this four years ago but then we just open-sourced about four months ago and since then it's picked up really well. Our users include Fortune 500 healthcare companies, government transport authority, banks, AI startups, DevTools startups and recently crypto startups as well. So it's been fairly, the adoption's been very great for us and we've been able to move very quickly as the community wanted us to move. A lot of that is due to Haskell. So I'm going to start off quickly with a demo just to show you what this is. I'm going to deploy this on Heroku. How many of you are familiar with Heroku? All right, cool. So Heroku is a provider on AWS. It kind of helps you deploy Docker containers and sets up a database for you so that you don't have to set it up. So I'm going to create a domain called fncon and you can see that this is provisioning Postgres for me as well and I'm deploying this. So what's happening in the background right now is that I have a Postgres database and we're deploying a Docker container and the Docker container is essentially the Haskell GraphQL engine which gets deployed as a demo and I can afford to do this live because the Docker image is just 15 MB in size which is also one of the benefits of having a language that you can compile down and then have a really tiny Docker image that makes it faster shipped. If this was on the JVM, I wouldn't have been able to do a live demo. It also wouldn't run on Heroku's free tier. Not hitting on anything. All right, so this is kind of the GraphQL endpoint. If you can see, I'm just going to zoom that up a little bit. That's the GraphQL endpoint that your application will hit. You'll make a post query to that and you'll make GraphQL queries to this endpoint to fetch your data. So what I also have set up is I also have a sample database and I'm just going to import that sample database right now to show you what the GraphQL API on a sample database looks like. So this is the database that I'm going to use. This is the Chinook database. So that's the database that I'm going to import and it's just a standard database. There's Album, there's Artist. Every album has a bunch of tracks. Just a standard relational database. I'm going to connect directly to the database on Heroku and then I'm going to import an SQL file. So that's me connecting to the database very slowly. There's nothing in the database right now and so what I have is I have an SQL file and I'm just going to import that here. So you can see that that's creating your sequences and then eventually it'll start creating the tables. Let's give it a few seconds. So what happens is that on the UI, Hasura tells you that we introspect the database and so we connect to Postgres and we say, hey, what are the tables that you have? And then we show you those tables and we say, do you want to expose these tables over GraphQL? And you can expose them selectively or because we're in God mode, I'll say expose all of them. And then we say, okay, these are the foreign keys that we've seen in your schema. Do you want to convert these into GraphQL relationships? And so we'd say yes, convert all of them. And then as soon as I do that, what I can start doing from my application is I can start querying it with... I can do an auto-complete and I can say albums. Every album has an ID and a title and then there's an artist for every album and every artist has a name. And I can make that query. So you're seeing an empty response there which is because the data import is happening at its own leisurely pace. And so as that data import happens, we'll start seeing some data here. But that's the idea. To motivate the idea behind GraphQL a little bit, I hope you're worried about security. There is permission and access control layer so that you're not actually querying your database raw. But let's assume that that's set up so that I don't have to show that to you. But the idea behind GraphQL essentially is that instead of having REST endpoints to give you an album where you fetch the album or to give you the artist of a particular album or having to iterate with your backend developer and say, hey, can you give me a precise API that fetches exactly this data? Instead of doing that, I as a mobile application developer I can make the exact query that I need. Let's say, for example, in this query now, I also need the ID attribute of every artist. And so all I need to do is add that column here, make that query, and there will be some data that comes up. So that's kind of the crux of what Hasura does and hopefully that gives you a sense of what GraphQL looks like as well. So GraphQL, if you look at it, is a structured language. It kind of looks like the shape that you base on that you want. And we don't have any data yet inside our... Let me see if we have any artists here. ID and... All right, so we have some artists imported. So if you look at what the GraphQL query is, the GraphQL query is essentially the shape of the data that you want. It's like a language that is essentially removing the values and just having the keys and then removing the quotes. That has a particular grammar. And so queries to fetch data are called queries. If you want to write data, that's called a mutation. So mutations are equivalents of like post requests, patch request, put request, delete request. All of that gets called a mutation. So you can use a mutation to insert an album or to update an album and stuff like that. And then you also have subscriptions which give you a real-time interface to your API. And so what I can actually do is maybe show that to you. I have a number for albums to see how many albums have imported so far. I have 126 albums and that number is gradually increasing because I have the import running live here. But what I can do is I can convert this query to a subscription and you'll see that this is changing real-time. So you're seeing that like you have that count that is changing real-time for you as the data is changing in the database. So that's the kind of API that we generate. Socket API that allows you to take like 5,000 clients or 10,000 clients and run that on one database. So that's the real-time component of GraphQL and the idea is that this is called a GraphQL subscription. It's like having a watch for your rest. Imagine if rest had get, post, put and also watch. It's kind of like a watch on rest. So that's what Hasura does and that's what GraphQL looks like. And now let's kind of get into how we built this. So if you were traditionally building your own GraphQL back-end, has anybody built a GraphQL server before? I know Harindra has. He's on the edge. So the way you built a GraphQL back-end is that instead of having the way you would build a traditional rest back-end is that you would have URLs and every URL would map to a function. Every path or route would map to a function. That function would do something and so every time you hit that URL with a particular method the function does something and returns some data to you. With GraphQL it's a little different. You have a GraphQL query. There's some component of your code that uses the GraphQL query and calls out functions. It traverses the GraphQL query instead of looking at a route and then calls the right set of functions and then all of those functions run and give you a response that you send back to the client. So let's say for example you had a really simple query that was fetching author and the id and name of an author. So when you send this query to an endpoint which is a web server, the web server has a controller. The controller starts traversing the query. There's a node called author and the node called author is mapped to a function which is called a resolver function which is the author function. The author function knows how to talk to the database and fetches data from the database. Simple. And so the author function decides okay, you've asked for id and name. Let me give you id and name. If you'd only asked for id, author will only fetch id from the database. If you'd ask for id, name and age it will fetch id, name and age from the database. Let's say you had author which would be a controller. The controller would then kind of call the author function and the article's function. The author function will independently fetch data from the database. The article's function will also independently fetch data from the database. You will merge that data together and then you will respond back to the client. Imagine that every article had tags. So you would now call three functions. Each of those three functions would call the database individually and then you'd return the response. You can see where I'm going with this and the idea is that this is a problem in ad hoc queries which are very large. You'll essentially be hitting the database several times even if you make just one single GraphQL query. This is also like the N plus one query problem. I remember the first time that I was writing a web server with Django. Everything worked when I used the ORM and then I put it in production and then I had 10 users and then the whole thing crashed. I was like, what is happening? Everything was working fine and I have 10 users. That's because in the ORM when I was trying to fetch the article and the author for each article I had a 4A in author fetch articles. And that's iterating in the loop and that's hitting the database 10 times. So if I have N authors I'm hitting the database N times to fetch N articles. Now imagine if each of those N articles has N tags or each of those N articles has tags. So now you'll hit the database again N times for every article and N times for every author. So you'll very soon start basically killing your database and then you wanted to use GraphQL but then you died because your DBA killed you. So that's kind of one problem that you have with GraphQL with a typical approach to writing GraphQL. So we take a very different approach. We say that if you look at this GraphQL query what we really wanted was that SQL query. We don't want to resolve we don't want to traverse this query we don't want to make that SQL query. And that's what we want as the final output. Obviously this restricts what you can do with your GraphQL API but the performance benefit that you get is slightly serious. So this is what we want to do and obviously what this looks like is a transpiler where you're saying hey let's take that GraphQL query and let's compile that to that single SQL query and let's add the right access control in the middle let's add the right type information in the middle and let's create a SQL query. So that's what Hasura is doing in the background. This is super useful because for every GraphQL query now you have just one SQL query so it's very easy to debug or to see if there's a performance problem. Thank you. But let's say for example you make this query. What I can do now is look at the actual SQL query that is being generated and then I can give this SQL query or I can look at the cost of this GraphQL query by DBA and the DBA can figure out what are the right indexes to add and stuff like that. The performance is also super, super high. On nested queries we can serve about 2,000 requests per second on a nested GraphQL query that is hitting a join in 50 megs of RAM. This is ridiculous. This is insane for a lot of people. You can fetch about 1,500 records in just 4 milliseconds because it's just one great SQL query that Postgres knows how to optimize. This has been running for no good reason. GraphQL queries that fetch 400 MB of data joining several tables that have hundreds of millions of rows and that works within a 1-5% performance difference from if you just made a raw query to Postgres directly and not had an HTTP layer at all. So that is the advantage that you get with this approach. Let's talk a little bit about what that approach looks like and why Haskell was a good choice in hindsight to have done this. It would end up being useful. So what you're doing is you have the GraphQL query. We're going to parse that into a GraphQL syntax tree which is an internal representation of the GraphQL query. We'll then convert that to an internal data structure that we have so that we can add the right information to it. Like for example with author and ID and name we want to know that ID is an integer. Name is a string so that later on if you're adding any operators to name like you're saying I want author where name starts with a T. If you want to do that we want to validate that it is generated the right SQL. So for doing that we need the right type information for every column. So that internal AST is then converted into an internal AST that has extra information. Then we finally render that into a SQL AST we convert that to a SQL AST and then we render the SQL and we hit the database with it. There's a more optimization that we do after we've generated the SQL as well but that's okay. The first bit is obviously that you need to represent the GraphQL grammar in your language and Haskell makes this spectacularly beautiful to write. What you see on the left is a screenshot of the GraphQL spec. What you see on right is a screenshot of the code that we have which basically means that you can literally look at the GraphQL spec and map that one to one into an ADT that will represent your GraphQL AST internally. This is useful. This is trivial but this is useful because as the GraphQL spec itself undergoes changes it's very easy for us to kind of keep up the date. Of course you're representing everything in a type safe kind of language which is also a fun thing to do for those who have not done it before. What you now need to do is let's say you now know what the final structure you want to get to is right. So you want to convert the GraphQL query which is a string which the client sends you and you want to convert that into the GraphQL AST that you're representing with your ADT. If you want to do this if you want to write the lexer and parser by hand A you shouldn't do it but you can kind of imagine the work that goes into it. You kind of read character by character see that there is a here's a word, here's a space, here's a curly bracket after this I expect a node name the node name should be author and that's fairly tedious work usually with most languages that you use like if you're reading JSON you need to parse JSON as well but you don't do that because boss road is on but you don't but we don't have that with GraphQL we did have that with GraphQL but we needed to work at this layer because there was a lot of other stuff that we needed to do at the GraphQL layer as well so we kind of ended up writing our own parser to do this and writing the parser is also spectacularly easy this is for example the code that it takes to figure out whether you're seeing a query a mutation or a subscription and you don't really need to grok the syntax but kind of just look at the at a high level you're dealing with the concept of what the GraphQL query is you're saying I want to parse the operation type because the operation can be a query or a mutation or a subscription and so you're saying I want to parse the operation type and so I expect this to be a token called query or a mutation or a subscription this is really the code that you need to write again helping you kind of keep iterating at this layer fairly easily and the rest of the kind of heavy lifting is taken care of with the combinator libraries and with the tokenizing libraries underneath so that kind of makes it easy again I'm not going into too much detail but it's just one example of where writing that parser yourself has a lot of benefits so this part is going to be little involved but and don't worry too much about the syntax but let's look at a particular part of the syntax tree and let's look at a possible transformation that we have to do right so I'm going to go back to the GraphQL interface and I'm going to make a query to fetch artists where the ID is greater than 4 and less than 2 is a bad query let me change that to 50 right and let's say that's the GraphQL query that I want to make ID and name right and so if that's the GraphQL query that I'm making I unfortunately don't have any data that has that sorry sorry oh thank you let me change that to less than 4 alright cool and now let me make that greater than 2 cool so I have just one which is 3 and so that's the data that I'm catching right and so if you look at let's just look at this part that we ultimately need to compile down to SQL right and so what we have is a Boolean expression here this is a part of our overall tree and we need to convert that part to SQL right and let's zoom in on that particular task so what we have is the JSON syntax there which is basically you have a column and the column will take a list of operations and values right and if you want to do that the natural way to kind of model that with the radity is to say I'm going to have a column expression type and the column expression type will have a name and a list of operations right and the Boolean expression that I give to the where is going to essentially be this column expression which is great if this is all it was but actually what we also want to do is we want to have a list of operations and or a not so if I do and would be a bunch of columns I want column one to have these conditions and column two to have these conditions and column three to have these conditions or an or or a not right like a typical Boolean expression and so what you end up doing is you have a tree and you say that I have a Boolean expression which could be an and and a list of expressions or an a list of expressions not of an expression and ultimately that node that has to be there has to be a column expression right so so internally when you read that portion of your GraphQL query this is what is going to internally look like right you're going to have a tree which is going to be and this is one example of what a query might look like you'll have and of three expressions two of them are column expressions one is not of an expression right so now what we need to do if you if you remember one part of the compilation step we needed to add extra information about every column right so I have this tree but in this column expression I need to now take the information that I have from postgres and I need to add that extra information in every column expression along with a column name saying that this is the type or this is the actual postgres column name not the column name that is being exposed right so we need to add that extra information so if you think about it this is the transformation that we need to do we have a column expression that just has name and operation and we now want to convert that into something that also has the information that is the rest of the expression object right so that green column expression is going into a yellowish annotated column expression right that's what I want to do as just one tiny little part of this whole process right and so what I really want to do at a higher level is that once I know how to do that I want to transform my entire tree instead of having green nodes on the bottom I want to have yellow nodes on the bottom right and the simplest way of thinking about this oh cool let me just have one AST for this for the red green tree which you can't see so I'm going to call it the left tree and let me have another ADT for the right tree right and so on there we have the left tree which is just a simple Boolean expression and on the right you have the annotated version of the tree right and so what you want to do is you will write this function called add annotation which will traverse the first tree you know go through everything and then convert that into the annotated version of the tree this is this is yuck work right because I don't want to I don't want to write code that traverses through the entire tree I know I just want to do a tiny thing I want the green to go to a yellow that's the code I want to deal with I don't want to deal with the code that is dealing with this whole tree finding out the right like nodes and converting those nodes into the final type and so what you do is you can parameterize parameterize the ADT that you have right and so instead of having two different trees I would represent that as one tree but where the where the leaf node is a parameter right the type is parameterized and then what I do is you can derive and there's a typo that should be deriving and then I'll derive a traversable on that which for free gives me a traverse function and the traverse function will do is it will kind of go through the entire tree for me right if I give it a function it will apply that function through the tree right and this is useful because now what I can do is the mental model that I have is I know how to go from green to yellow and I have a function that I write that takes a column expression to an annotated column expression and then what I do is to apply that through the tree I get this ability to traverse the tree for free right and this is just one like super tiny example of a super tiny transformation that we need to do right but overall the transformation that we have are fairly significant you need to do these one to one transformations you need to reduce your tree into a different kind of tree right because this Boolean expression is not the Boolean expression that you have in SQL right so all of those transformations all of those modifications all of those reductions kind of become easy because of this ability to manipulate your trees without worrying about the implementation of the actual transformation at node level right so that's the second thing that kind of makes Haskell super easy it makes it a good choice one of the other things also at this point that I should point out is that back when we started writing this four years ago we didn't really have there was no GraphQL there was GraphQL but we were looking at GraphQL and saying right so that's literally the sound that we made and we regret making that sound and so what we used to have is we used to have a JSON AST so what we had back in the day like two years ago is we had everything after the internal AST right and then and we had our own JSON language and then GraphQL came along and we had to add support for GraphQL and adding that support was actually really easy because we looked at the internal AST we made that internal AST a little more generic and then we just added the front end which was accepting a different language and reducing it back to the internal AST right so whatever work we'd done on the back of that pipeline kind of continued working for us and we just changed the front end and it works really well because now we're adding support for something called relay and that's going to follow the same idea right and that makes life convenient alright so the last portion that I'm going to touch on is the real time portion so if you look at GraphQL subscriptions that's kind of what's happening underneath and the idea behind this diagram is to show you that it's a pain but I'll kind of walk you through it so let's say you have a client or you have a bunch of clients who want to get back to data so everybody wants to see what the latest score is so you say subscription score whatever team name team name and some value right and you're subscribing to this so what does the web server do the web server for every single connection creates a connection object and has a delivery thread that will take whatever data comes into the queue and deliver it to on that particular web socket but at the same time by doing Postgres Listen Notify or listening to the change stream or reading from the database whatever you do you don't want every single application to start its own event sourcing thread in the database because then you'll have 5,000 clients and all 5,000 of them are hitting the database simultaneously to fetch the live score so when you know that you have you have thousands of people requesting for the same piece of data you want the event sourcing thread to be the same so you separate that out and you say I'm going to have a bunch of event sourcing threads independent portions of data that will then pass that data along to the relevant connection objects and then send that to the application GraphQL also allows you to have over one GraphQL subscription subscribed to multiple things and that makes the connection object a little harder to deal with so one connection object can accept data from many different event sources right when you're creating a connection object you need to make sure that the connection object is created and is validated against the GraphQL schema that you have so I'm querying or subscribing to a live score the live score model needs to exist if something happened and your GraphQL schema went through an update any connection object that is using the score object should get invalidated or if you're creating a new connection object live score should not exist so there's a lot of shared state in completely functionally independent portion of the code something that is managing your GraphQL schema that is doing event sourcing and that is managing your connections right they're all concurrent within themselves and now there's shared state where these kind of multiple systems need to kind of talk to each other so it's a pain in the ass pardon my French and sorry if anybody speaks French for real but concurrent programming is a pain it's a gigantic pain this GIF is very dark but it is painful it is very very painful it's an example of how you would try to solve this problem and why solving that problem becomes painful very quickly so let's say I have that event sourcing thread and I have the connection thread and what I want to do is I want to pop from the event queue and I want to push into the connection objects queue pop is a safe operation inside the event sourcing threads push is a safe operation inside the connection thread and I want to compose them I want to say hey pop and then push I want that operation together to also be to also happen atomically this is a slightly contrived example you don't actually need to do this in our case but let's just say you need to so I need to pop and I need to push pop is safe, push is safe but pop plus push should also be atomic so what you would do naively is you would say oh cool let me create like a lock for the thread event sourcing threads a lock for the connection threads acquire lock 1 and lock 2 and the push and then release the locks and you might think this is a great idea but this is not because somebody else is using lock 2 and lock 1 so the ordering of lock starts mattering let's say one portion of your code is doing lock 1 and lock 2 and doing something and the other portion of your code is doing lock 2 and lock 1 and then doing something and then you will deadlock very quickly and all of those nice users who loved you will now hate you what you then do is then you would have like a global sequence of your locks and then this will also be painful because you then need to find out what order your lock has right this is not fun what is fun is doing this I want to write code and I will write code by doing atomically pop and push right this is nice I don't have to deal with any of what I showed you before and this is what STM gives you and this is why STM makes it easy STM is the idea of having database transaction working on persistent data but doing that to in-memory objects across threads right that's it and that's all you need to do and so you can imagine that all of the shared state that we have here you know where the scheme object is there that getting updated the connection getting created the connection getting validated connection subscribing to a same thread if a connection needs an event source thread that does not exist creating that new event source thread or telling an existing event thread that hey I'm a new connection I also want your data all of that synchronization becomes super easy right so STM is a huge boost and allows us to handle a vast number of real-time connections fairly easily alright so this is kind of a quick overview of what our stack looks like we build we build with stack and with Docker we ship with Docker we run our tests on a CI CD on CircleCI libraries that we use are fairly standard for the web stuff there is one web software library we use that we do not use a Postgres library to connect to Postgres directly we have our own hand-rolled library that uses liptiq we do this because most of the existing libraries the idea was that you will define your SQL query statically but in our case the SQL query is dynamic right the GraphQL query that you make will create a new SQL query and so we kind of needed to do this ourselves especially to leverage like prepared statements and stuff from the database for events and MTL for for monotransformers we have our tests written in Python we have a few in Haskell but mostly in Python because we have a part of our team that validates whether the GraphQL query that you're generating is valid whether queries work as they should it's easy for them to write those tests if they're just written in Python and then we use HPC for code coverage and try to max that we do performance analysis with Profiter, ThreadScope and a really fun tool in Lua called REC WRK2 for benchmarking HPP performance and we're going to release a tool for benchmarking subscriptions and WebSocket performance as well because I think the one tool called Sung in Erlang is there but it's not super easy to use or customize so our big challenges that we're facing at the moment are in some cases we need to start fighting laziness and this is because there was a point of time where we wanted to see how much time each step in our compiler takes and measuring that is hard because if you look at the various stages in the compiler let's say for each stage I want to see how much time everything takes the problem is that because the evaluation of the whole thing is actually done when you render the SQL and not at each stage it's been a little painful for us to when you add instrumentation for timing it ends up telling you how much time it took to build the thunk not really evaluate the thunk so that's some work that we need to do kind of refactor the code to take care of that but that has a few associated problems with it and that problem is around and that problem is that for example we wanted to evaluate moving from MTL to FRIAR for composing effects better but the idea but one of the things is that it's apparently the compiler can optimize MTL code better but we don't know whether we need to bother about this performance or not and performance is very important for the use case that we solve for our users and so without having an accurate way to measure performance it's very hard for us to now decide whether to kind of move to more modern ways of writing code another problem is now around kind of deciding to move to generic lens and this is not really a problem but this is more of a generic lens is a new thing but evaluating whether or not we should switch to that what the cost of actually moving to that is going to be is a little hard for us because it requires a certain amount of maturity with all of these various tools to be able to say which tool we should use and if something is working should you really change it? No, we shouldn't change it so we're not changing it but at a quick glance the high level business benefits that we've had because of using Haskell over the last few years one is that as a really tiny team our feature velocity is extremely high and that's because of like the three simple examples that I showed you we're doing a bunch of hard things that you need to iterate on very frequently are made really easy with Haskell especially in our context of creating a compiler like thing and something that has a high amount of concurrency that becomes easy. The other thing has been that we've been able to reduce debt-debt fairly quickly and that's because you kind of start using REC and then you're like hey I don't like this HTTP client I need to do some fancier stuff I need to have a better API internally it turns out it's just a few hundred lines of code you can just write it yourself, roll your own library you don't have to stick with old code you can refactor faster and that has another added benefit which is that there's this thing where people say that it's hard to get people who are new to Haskell to work with Haskell code base but actually that's not been a problem because if somebody is learning Haskell even if their code is quote-unquote immature it doesn't matter because as long as it works it's fairly safe because the compiler will ensure that the code works, that the code is safe and what you can do is you can refactor that later when everybody is more mature and that works because refactoring is fast anyway and all of this kind of adds up together in a lot of community love for us there has to have seen a lot of love from the GraphQL community that has nothing to do with Haskell because we're building things we're fixing things and we're shipping them super super fast and that's created a lot of love for us in the community and that's been a huge that's been super important for us what I'm going to conclude this and open this up for questions but a few ways that you can get involved with us are one, we're hiring Haskellers and FB programmers please head to haskell.org slash careers remote roles and roles in Bangalore we're also, please do try Haskell out join our discord, give us feedback use it in a side project, tell us if you like it tell us what you don't like, be nice though and then of course Stars on GitHub makes us feel like we're in kindergarten at all, like stars of course those contributions are welcome especially if you're doing Haskell stuff, we're very beginner friendly we have a lot of issues tagged for beginners so if you're doing React JavaScript, Go, or Haskell head to the repo try to build some things and help us fix a few things questions and I'd like to invite Bamshi who's here who's the lead architect who's been dealing with this over the last few years if you guys have any questions for us we'd love to take your time we have a CLI in Go because the CLI needs to work on our platforms cross compiling CLI not Node.js and not Go will not work anywhere it is open source but it's very specific to how we connect to Postgres it's open source you can try it out, it's optimized for performance but again without specific use case got it so this is not really a, it's more of a Haskell question it's not really a GraphQL question because so whenever you think if something is possible with GraphQL or not the right way to ask that question is is this possible with REST if it is possible with REST it's possible with GraphQL because REST, GraphQL, SOAP are all at the same layer but with Haskell that's a good question to ask and that's something that I didn't show you but what we allow you to do is create this access control layer which is kind of like Postgres low level security that we've added at the application layer so what you can do is you can say organization, right? So every organization has a manager and this organization manager can select data if the org ID let's assume that I had a org ID column if the org ID is equal to the org ID that comes in from their SSO and so that's the access control rule that we allow you to specify and so now whenever somebody queries you know a table and they make a GraphQL query in our compilation step when we create a GraphQL AST what we'll do is we'll look at who is this user what is their token? Let's convert their token into the role so this guy is a user manager so let's take user manager, let's take this access control rule, let's inject that into the AST so instead of doing select star from table it will become select star from table where org ID equal to session org ID does that make sense? I mean so you specify permissions individually on each table, right? each table would have its own access control rule so if you do each table you get to specify relationships and you can use relationships in permissions so let's say you have a team I get your point, so you're basically saying I'm allowed to access this if I belong to the team is that what you're saying? Absolutely you can, correct but the permissions on table are coming from table A implicitly that's what you're saying, right? so what you can do is you can create a permission here for example I can create a permission saying that for this particular album album.artist.name you get what I mean? the access control rule that you create can itself traverse your foreign key constraints but that's a Hasura specific thing it's not a GraphQL thing if you were building your own GraphQL API and you didn't build it as a compiler you would write your own business logic that would do whatever you wanted it to do in our case because we're implementing it as a compiler we give you these rules that you can add and these rules can traverse relationships so JWT token so what we do is when you make this query to Hasura what you do is your token will come in from R0 so you'll say ignore the spelling bearer and some token here so this token will come to Hasura you'll configure Hasura with environment variables that has the Jot signing key or the Jot public key Hasura will take this token will try to extract user ID and role information from that so we don't do authentication we do authorize it that authentication information comes in from R0 your own R system, password whatever you want to do that makes sense you had another question I was implementing one of the implementation from your end and one other thing which I wanted to know was how do you do multi-tenant kind of stuff like let's assume that I'm working on a source platform where each same table is multiple tennis we can take this offline it's a Hasura specific thing we can take this offline we can handle those intermediate and that's great but it becomes a problem when we need to find out how much time it takes let's say you're instrumenting an query and you want to know where the compiler is spending most of the time you want to know how long it took to parse validate how long it took to convert to SQL and then how long it took to actually execute it but now if you measure these things you just see that the final execution phase takes the largest chunk but it's not true because all of that is being lazily evaluated in the end so we are the fastest grab in the world, yes but do we know where it's faster, where it's slow, no you have shown the connectivity from GraphQL to PostgreSQL so do you have connectivity to other databases eventually we'll get the money from the sky to follow us and then we'll have all the databases okay but one of the things that helps out is if you think of it as a compiler it's like the SQL target that we render that language has to change so it's like instead of rendering the Postgres dialect of SQL we now have to render mySQL dialect of SQL which is the most god-awful SQL dialect or Oracle SQL dialect which is better but things like that or Google Spanner SQL dialect or whatever so that's what we need to do why don't you take some time so how are you handling the subscription piece in a sense because I saw those being inserted to tables and you know so I mean are you using the Postgres notification or things like that so so it's kind of a what we do is we end up using something like that but we also debounce so it's kind of like a glorified polling so what we end up doing is like if you use something like Postgres Listen Notify just by itself right the problem is that what if a million events happen will you send a million events to a mobile client no right suppose you're subscribing to something that's super active you don't want every single change on a mobile client you might want every single change for an event sourcing like if you're syncing that data to a different database you might want every single event but on a mobile client you want the latest score every single score change that has happened right and so we debounce to a second and so it ends up being like a polling thing so what we do is we take like thousands of subscription connections we convert that to one query on the database and then we watch for changes on that query if something changes on that query for any of those relevant clients then we send data down but within a maximum interval of one second which you can configure so you keep polling the query to the database correct correct correct and so we we we did start off by using a listen notify we were actually starting off with a um by reading the postgres write a head log but that that becomes impractical very very fast like it becomes useless very quickly it sounds fancy but you can't use it for example postgres does have you heard of postgres's materialize views so there's a reason postgres materialize views are not auto refresh right because it's complicated it's almost hard an impossible problem to solve so the way we expose subscriptions is that you can query across views and views connected to other views so it kind of becomes hard for us to just use the write a head log and compute what has changed underlying so this works out like across all possible use cases of our subscription system but if you're a write a head log kind of guy um we have a really nice library which streams postgres write log changes on web sockets uh it's a fun library it's useless but it's fun if you feel like I want to do write a head log stuff it won't work for real world use cases but it's nice and do you support stitching? yes that's that's how you add like custom business logic so if you see remote schemas here um I can just add I can just add a GraphQL schema like pokemon I don't know what the pokemon url is pokemon.now.sh and then what will happen is that your GraphQL query it's not a valid url but the GraphQL schema that you get will be merged the Hasura GraphQL schema and your GraphQL schema will merge together because obviously you can't do everything with the database right like payment APIs you can't handle that or a very custom transaction you can't handle that so those are your code however you write thank you I'm afraid that's all the time we had maybe Tanmay and Babshi can answer this later alright thank you thank you folks for having us