<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hasura</title>
    <description>The latest articles on DEV Community by Hasura (@hasurahq).</description>
    <link>https://dev.to/hasurahq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F397%2F0f80fcfc-b6d9-4725-b97a-58fc08bbe0e6.png</url>
      <title>DEV Community: Hasura</title>
      <link>https://dev.to/hasurahq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hasurahq"/>
    <language>en</language>
    <item>
      <title>The complexity of building a GraphQL API permissions layer and how Hasura solves this</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Wed, 05 Jul 2023 09:17:26 +0000</pubDate>
      <link>https://dev.to/hasurahq/the-complexity-of-building-a-graphql-api-permissions-layer-and-how-hasura-solves-this-2j3l</link>
      <guid>https://dev.to/hasurahq/the-complexity-of-building-a-graphql-api-permissions-layer-and-how-hasura-solves-this-2j3l</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1ToPDh1z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://hasura.io/blog/content/images/2023/07/authz-permissions.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1ToPDh1z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://hasura.io/blog/content/images/2023/07/authz-permissions.png" alt="The complexity of building a GraphQL API permissions layer and how Hasura solves this" width="800" height="946"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;API security breaches are on the rise. &lt;a href="https://www.gartner.com/en/documents/4009103"&gt;Gartner&lt;/a&gt; predicts that by 2025, insecure APIs will account for more than 50% of data theft incidents. As enterprises continue to embrace an API-driven approach to software development (for all the benefits it brings), arming their developers with the tools to build secure APIs with proper data access and authorization logic needs to be a priority.&lt;/p&gt;

&lt;p&gt;Building an authorization layer involves many factors. In GraphQL, authorization belongs in the business logic layer and not typically inside resolvers. It is more complex to write an authorization layer for GraphQL APIs than REST APIs. In this blog, we will see what it takes to build authorization logic for a DIY GraphQL server to understand how it is more complex. We will also compare it with Hasura and how it solves this complexity by being declarative and leveraging predicate pushdown.&lt;/p&gt;

&lt;p&gt;Here's a quick TL'DR and summary before diving deep into this post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iX2uwwFj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4psljrxrpxedl2ggtmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iX2uwwFj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4psljrxrpxedl2ggtmo.png" alt="Image description" width="626" height="645"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key items to consider
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data modeling
&lt;/h3&gt;

&lt;p&gt;Data models contain key information about the types of data, relationships between data. This is used to determine the kind of authorization system that needs to be built.&lt;/p&gt;

&lt;p&gt;For example, you have users of the application that can view public data of all users and certain private data of them. The data model now contains certain table columns relevant to this and also relationships that are relevant to this private data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Roles and attributes
&lt;/h3&gt;

&lt;p&gt;The common ways to model your authorization system is through RBAC (role-based access control and ABAC (attribute-based access control).&lt;/p&gt;

&lt;p&gt;You could start defining roles for the application and list out use cases and privileges for each. RBAC could be flat, hierarchical, constrained and symmetrical. Authorization can also be modeled based on attributes where the user who logs in to the application will have certain attributes (type of user, type of resources, and the environment in which they are accessing) that can be checked for allowing access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested rules
&lt;/h3&gt;

&lt;p&gt;The GraphQL schema may or may not be a direct map to the data models. In some cases, the data model extends to multiple data sources. Even within the same data source, the GraphQL query could be nested with multiple fields spanning relationships. Applying authorization logic contextually to the nested query is vital.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
  users {
    id
    name
    orders { // apply permission rule here too
      id
      order_total
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the &lt;code&gt;orders&lt;/code&gt; is part of the users relationship and is nested. But the authorization logic needs to apply for both users, for orders, and for any level of nesting of the query, contextually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;Ideally, authorization checks shouldn’t add a lot of overhead to the response latency. In reality, when you are writing a custom GraphQL server yourself, checks are done after the data fetching operation and there’s a lot of unnecessary data fetched, which slows down the database if you consider millions of requests which slows down API performance as a result.&lt;/p&gt;

&lt;h4&gt;
  
  
  Predicate pushdown
&lt;/h4&gt;

&lt;p&gt;If the authorization logic is heavily dependent on the data being fetched, it is important to do a predicate pushdown of the Authorization rules.&lt;/p&gt;

&lt;p&gt;For example: If you are fetching orders placed by a user on an e-commerce app, you need to be able to apply the rule in the query that goes to the database to fetch only the orders placed by the user requesting them. This is not only faster, but also the most secure way to apply authorization logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a DIY authorization layer for a GraphQL API
&lt;/h2&gt;

&lt;p&gt;You can build an AuthZ layer using middleware libraries. The maturity of AuthZ libraries in GraphQL depends on the language or framework you are in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is building an authorization layer complex?
&lt;/h3&gt;

&lt;p&gt;When you are writing your own GraphQL server with custom authorization rules, there are a number of methods to write this logic, depending on the use case. You have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API-wide authorization&lt;/li&gt;
&lt;li&gt;Resolver-based authorization&lt;/li&gt;
&lt;li&gt;Schema / model-based authorization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resolver-based authorization quickly balloons into a lot of boilerplate code if authorization rules are applied to every field. Even with repeated rules and patterns that can be applied, it can easily expand to thousands of lines of code to secure your application. And this code becomes difficult to maintain.&lt;/p&gt;

&lt;p&gt;In the schema-based authorization, the authorization logic is dependent on the GraphQL schema and becomes independent of the underlying database or data fetching libraries and ORMs.&lt;/p&gt;

&lt;p&gt;Authorization in GraphQL is typically built using _ &lt;strong&gt;Context&lt;/strong&gt; _ object that is available on every request. The context is a value that is provided to every resolver and is created at the start of a GraphQL server request. This means you can add authentication and authorization details to the context, such as user data.&lt;/p&gt;

&lt;p&gt;Here’s how a typical AuthZ context gets passed in a DIY GraphQL Server written in Node.js:&lt;/p&gt;

&lt;h3&gt;
  
  
  Parsing and validating JWT token
&lt;/h3&gt;

&lt;p&gt;The first step is to parse and validate the incoming JWT token. Once the JWT token is verified, you can pass it to the context. We are using JWT tokens as an example here, because it is universal and works across platforms.&lt;/p&gt;

&lt;p&gt;An example request:&lt;/p&gt;

&lt;p&gt;Endpoint: myapp.com/v1/graphql&lt;/p&gt;

&lt;p&gt;Headers: Authorization: &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Authorization&lt;/code&gt; header is parsed, validated, and verified. The underlying authentication service used could be any solution that issues JWT. Here’s some example code that verifies a JWT token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try {
    if (token) {
        return jwt.verify(token, YOUR_SECRET_KEY);
    }
    return null;
} catch (err) {
    return null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s an example taken from a DIY GraphQL Server (Apollo) to parse the context:&lt;/p&gt;

&lt;p&gt;Create a new instance of Apollo Server by passing in the type definitions and resolvers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const server = new ApolloServer &amp;lt; MyContext &amp;gt; ({
    typeDefs,
    resolvers,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a standalone server that retrieves the token from headers and returns the &lt;code&gt;user&lt;/code&gt; information as context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {
    url
} = await startStandaloneServer(server, {
    // Note: This example uses the `req` argument to access headers,
    // but the arguments received by `context` vary by integration.
    context: async ({
        req,
        res
    }) =&amp;gt; {
        // Get the user token from the headers.
        const token = req.headers.authorization || '';
        // Try to retrieve a user with the token
        const user = await getUser(token);
        // Add the user to the context
        return {
            user
        };
    },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Passing context
&lt;/h3&gt;

&lt;p&gt;Once the token is extracted, you need to pass the context object to every resolver that will get executed. Now all of your resolver code gets access to the context.&lt;/p&gt;

&lt;p&gt;In the above example, we can see that the &lt;code&gt;user&lt;/code&gt; data is passed to the context.&lt;/p&gt;

&lt;h3&gt;
  
  
  API-wide authorization
&lt;/h3&gt;

&lt;p&gt;There are a few rules that might need to be enforced at the request level, even before passing it on to the GraphQL resolvers.&lt;/p&gt;

&lt;p&gt;For example, you could block a user from performing any queries and return a 401, unauthorized error. Again, this involves code logic and the logic could become a lot of boilerplate if there are many rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resolver-level authorization
&lt;/h3&gt;

&lt;p&gt;As the context gets attached to each resolver, it is now possible to authorize the request inside the resolver. This method is only suitable for basic authorization logic when there are only few resolvers and few rules to check and authorize users.&lt;/p&gt;

&lt;p&gt;For example: You have a users field that returns a list of user names. You will end up writing code, which looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users: (parent, args, contextValue) =&amp;gt; {
    // In this case, we'll pretend there is no data when
    // we're not logged in. Another option would be to
    // throw an error.
    if (!contextValue.user) return null;
    return ['bob', 'jake'];
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contextValue is now available for parsing and authorizing the user.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This is resolver logic for one field. Imagine repeating logic code in every single resolver and field. It is very challenging to scale this. It is also very challenging to update any logic quickly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  GraphQL schema-based authorization
&lt;/h3&gt;

&lt;p&gt;In a large schema with plenty of data based authorization, there are patterns of rules that are applicable for multiple queries. For example: Allow the user to fetch their own data and not any one else.&lt;/p&gt;

&lt;p&gt;Here’s an example from GraphQL AuthZ library using schema as a data source.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// using schema as a data source inside pre-execution rule
const CanPublishPost = preExecRule()(async (context, fieldArgs) =&amp;gt; {
    const graphQLResult = await graphql({
        schema: context.schema,
        source: `query post($postId: ID!) { post(id: $postId) { author { id } } }`,
        variableValues: {
            postId: fieldArgs.postId
        }
    })
    const post = graphQLResult.data?.post
    return post &amp;amp;&amp;amp; post.author.id === context.user?.id
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look at the return statement at the end in the above code snippet, that’s where the logic of checking user ID is written.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Hasura’s authorization layer work?
&lt;/h2&gt;

&lt;p&gt;Hasura has a powerful authorization engine that allows developers to declaratively define fine-grained permissions and policies to restrict access to only particular elements of the data based on the session information in an API call.&lt;/p&gt;

&lt;p&gt;Implementing proper data access control rules into the handwritten APIs is painstaking work. By some estimates, access control and authorization code can make up to 80% of the business logic in an API layer. Securing GraphQL is even harder because of the flexible nature of the query language. Hasura radically simplifies the effort needed to build authorization logic into APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declarative
&lt;/h3&gt;

&lt;p&gt;With Hasura, you can transparently and declaratively define roles, and what each role is allowed to access in the metadata configuration. This can be done either through the Hasura Console or programmatically through the Hasura CLI. This declarative approach to authorization is simpler to create, maintain, evolve, and audit for developers and security teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fine-grained access control
&lt;/h3&gt;

&lt;p&gt;Hasura supports a role-based access control system. Access control rules can be applied to all the CRUD operations. You define permissions granularly on the schema, sessions, and data (table, row, and column).&lt;/p&gt;

&lt;p&gt;For every role you create, Hasura automatically publishes a different GraphQL schema that represents the right queries, fields, and mutations that are available to that role. Every operation will use the request context to further apply permissions rules on the data.&lt;/p&gt;

&lt;p&gt;Authorization rules are conditions that can span any property of the JSON data graph and its methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any property of the data spanning relationships. Eg: Allow access if “document.collaborators.editors” contains “current_user.id3⁄4&lt;/li&gt;
&lt;li&gt;Any property of the user accessing the data. Eg: Allow access if accounts.organization.id is equal to current_user.organization_io&lt;/li&gt;
&lt;li&gt;Rules can mask, tokenize, or encrypt portions of the data model or the data returned by a method and are labeled. These labels are called "roles."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V48jwCq6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://lh3.googleusercontent.com/MyG4i7A6dPyA3AmuPXaJCEaNfAmz0wz-BhnyTKbGJk8_4LDijDp_WvUlNQU4RgmggFHFPiAYulaQUOcQZ1qViKYgvjzn2Q-X7PBGwcgC5YRKXpwRVo-sFEcLryRWpxRyuPr4rzsM1xNfR8IXP4WtAaw" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V48jwCq6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://lh3.googleusercontent.com/MyG4i7A6dPyA3AmuPXaJCEaNfAmz0wz-BhnyTKbGJk8_4LDijDp_WvUlNQU4RgmggFHFPiAYulaQUOcQZ1qViKYgvjzn2Q-X7PBGwcgC5YRKXpwRVo-sFEcLryRWpxRyuPr4rzsM1xNfR8IXP4WtAaw" alt="The complexity of building a GraphQL API permissions layer and how Hasura solves this" width="800" height="946"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Easily integrate with your authentication system
&lt;/h3&gt;

&lt;p&gt;Authentication is handled outside of Hasura, and you can bring in your own authentication server or integrate any authentication provider that supports JSON Web Token (JWT). If your authentication provider does not support JWT, or you want to handle authentication manually, you can use webhooks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"By using Hasura, we cut the development time in half and built our product in three months. The built-in role-based authorization system made it easy to secure our data."&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Mark Erdmann, Software Engineer, Pulley&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Predicate pushdown
&lt;/h3&gt;

&lt;p&gt;Hasura automatically pushes down the authorization check to the data query itself, which provides a significant performance boost and cost savings by avoiding additional lookups and unnecessary data egress, especially at larger scale.&lt;/p&gt;

&lt;p&gt;Hasura automates predicate pushdown, since it is essentially a JIT compiler that can dynamically apply the filter in where the clause of a SQL query is based on the user running the query. Most GraphQL server frameworks require you to write plenty of boilerplate code to achieve the predicate pushdown authorization check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CqE0IGOJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://lh3.googleusercontent.com/kxwq2X1k_6dX0CHJ9JwnpjRImSgV73iRtaGEONBBfywlLXnXdleu-FuCFTittWkwqL6oJ5-D6tr-7e6oFJx0GTY9UDyF1bUHTiqNZZ9-QnFwrMJGvdbBNkhtIDSGXvE9o-3F75SsQIFeBt-N2F4Wri4" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CqE0IGOJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://lh3.googleusercontent.com/kxwq2X1k_6dX0CHJ9JwnpjRImSgV73iRtaGEONBBfywlLXnXdleu-FuCFTittWkwqL6oJ5-D6tr-7e6oFJx0GTY9UDyF1bUHTiqNZZ9-QnFwrMJGvdbBNkhtIDSGXvE9o-3F75SsQIFeBt-N2F4Wri4" alt="The complexity of building a GraphQL API permissions layer and how Hasura solves this" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cross-source authorization
&lt;/h3&gt;

&lt;p&gt;Hasura integrates authorization rules based on data and entitlements in different sources. Hasura forwards the resolved values as headers to your external services, which makes it easy to apply authorization rules in your external service. Again, this is made possible by Hasura’s declarative authorization system.&lt;/p&gt;

&lt;h2&gt;
  
  
  OWASP Top 10
&lt;/h2&gt;

&lt;p&gt;OWASP is most famous for the “Top Ten” framework for structuring secure applications. As the industry expands into a microservice-driven approach, it’s important for organizations to validate all of their dependencies according to the OWASP framework.&lt;/p&gt;

&lt;p&gt;Hasura’s security-first approach ensures that the Top 10 security features and criteria are fulfilled. Read more: &lt;a href="https://hasura.io/blog/owasp-samm-and-hasura/"&gt;How Hasura addresses the OWASP Top 10 concerns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are building your own GraphQL server and writing authorization logic, you will need to ensure that the Top 10 concerns are handled to be secure and compliant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try out Hasura
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cloud.hasura.io/signup"&gt;Sign up&lt;/a&gt; to start your journey with Hasura Cloud today. If you are an enterprise looking to learn more about how Hasura fits in your app modernization strategy, reach out to us through the &lt;a href="https://hasura.io/contact-us/"&gt;Contact Us form&lt;/a&gt; and our team will get back to you.&lt;/p&gt;

</description>
      <category>authorization</category>
    </item>
    <item>
      <title>Save Time and Stop Writing GraphQL Resolvers</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Wed, 01 Feb 2023 16:40:23 +0000</pubDate>
      <link>https://dev.to/hasurahq/save-time-and-stop-writing-graphql-resolvers-2fgl</link>
      <guid>https://dev.to/hasurahq/save-time-and-stop-writing-graphql-resolvers-2fgl</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhasura.io%2Fblog%2Fcontent%2Fimages%2F2023%2F02%2Fsave-time-stop-writing-graphql-resolvers.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhasura.io%2Fblog%2Fcontent%2Fimages%2F2023%2F02%2Fsave-time-stop-writing-graphql-resolvers.png" alt="Save Time and Stop Writing GraphQL Resolvers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The conventional way of building a Data API through GraphQL involves building everything from scratch. That includes tasks such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;writing boilerplate code for basic CRUD functionalities&lt;/li&gt;
&lt;li&gt;implementing and maintaining GraphQL resolvers&lt;/li&gt;
&lt;li&gt;optimizing the resolvers for performance&lt;/li&gt;
&lt;li&gt;adding authentication and authorization&lt;/li&gt;
&lt;li&gt;implementing data validation and error checking mechanisms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the most complex and time-consuming tasks is the implementation of GraphQL resolvers, though. Since the resolvers are the ones that generate a response for a GraphQL operation, it's critical to implement them properly.&lt;/p&gt;

&lt;p&gt;However, as the complexity of the API increases, so does the effort and difficulty of coding the resolvers. Manually implementing the resolvers can be difficult for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boilerplate code: It requires a significant amount of boilerplate code for the CRUD operations&lt;/li&gt;
&lt;li&gt;Complexity: The resolvers become complex once you add filtering, aggregations, authorization, and custom business logic&lt;/li&gt;
&lt;li&gt;Performance: Inefficient implementation of resolvers can lead to an underperforming API&lt;/li&gt;
&lt;li&gt;Maintenance: Maintaining the resolvers and keeping them up-to-date with changes in the schema and data sources is time-consuming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moreover, you must ensure that the API is secure and can scale appropriately. All this work requires lots of effort and resources. What if you could eliminate all these challenges and time-consuming work? This article presents an alternative way of building GraphQL Data APIs without the difficulties of manually-built APIs. &lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the GraphQL Resolvers manually
&lt;/h2&gt;

&lt;p&gt;In this section, we'll go over a custom-built GraphQL API that represents a digital media store. The API is connected to the Chinook database, which includes tables for artists, albums, and tracks.&lt;/p&gt;

&lt;p&gt;The figure below illustrates the GraphQL API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FIoRylGa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FIoRylGa.png" alt="Digital media store GraphQL API screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the GraphQL resolvers are the main focus of this article, we'll only focus on them. We'll skip the other details that are not relevant to this article. However, you can see the complete application code &lt;a href="https://github.com/hasura/graphql-engine/pull/9401/files#diff-8cbddd3e0f633afad7ff9f7ffa99437bfce18563428413c20c1b4f356a013b77" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The schema of the GraphQL API looks as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;schema {
  query: Query
}

type Query {
  Album(id: Int!): Album
  Albums: [Album!]!
  Artist(id: Int!): Artist
  Artists: [Artist!]!
  Track(id: Int!): Track
  Tracks: [Track!]!
}

type Album {
  AlbumId: Int!
  Artist: Artist
  ArtistId: Int!
  Title: String!
  Tracks: [Track!]
}

type Artist {
  ArtistId: Int!
  Name: String
  Albums: [Album!]
}

type Track {
  Album: Album
  AlbumId: Int
  Bytes: Int
  Composer: String
  TrackId: Int!
  MediaTypeId: Int!
  Milliseconds: Int!
  Name: String!
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at the schema, you can see that the API only allows querying the database for artists, albums, and tracks. It doesn't enable mutating data, such as inserting, updating, and deleting records.&lt;/p&gt;

&lt;p&gt;As a result, this GraphQL API has a couple of resolvers. A resolver for retrieving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a specific album&lt;/li&gt;
&lt;li&gt;all albums&lt;/li&gt;
&lt;li&gt;a specific artist&lt;/li&gt;
&lt;li&gt;all artists&lt;/li&gt;
&lt;li&gt;a specific track&lt;/li&gt;
&lt;li&gt;all tracks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "resolvers" code looks as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { readFileSync } from "node:fs";
import { createServer } from "node:http";
import { createSchema, createYoga } from "graphql-yoga";
import { GraphQLError } from "graphql";
import DataLoader from "dataloader";
import { useDataLoader } from "@envelop/dataloader";
import type { Album, Artist, Resolvers, Track } from "./generated/graphql";
import sql from "./db";
import { genericBatchFunction } from "./dataloader";
import { keyByArray } from "./utils";

const typeDefs = readFileSync("../schema.graphql", "utf8");

const resolvers: Resolvers = {
    Query: {
        Album: async (parent, args, context, info) =&amp;gt; {
            return (context.getAlbumsById as DataLoader&amp;lt;string, Album&amp;gt;).load(args.id.toString());
        },
        Albums: async (parent, args, context, info) =&amp;gt; {
            const albums = await (
                context.getAllAlbums as DataLoader&amp;lt;string, Album[]&amp;gt;
            ).load('1');
            for (const album of albums) {
                (context.getAlbumsById as DataLoader&amp;lt;string, Album&amp;gt;).prime(
                    album.AlbumId.toString(),
                    album
                );
            }
            const albumsByArtistId = keyByArray(albums, 'ArtistId');
            for (const [ArtistId, albums] of Object.entries(albumsByArtistId)) {
                (context.getAlbumsByArtistId as DataLoader&amp;lt;string, Album[]&amp;gt;).prime(
                    ArtistId,
                    albums
                );
            }
            return albums;
        },
        Artist: async (parent, args, context, info) =&amp;gt; {
            return (context.getArtistsById as DataLoader&amp;lt;string, Artist&amp;gt;).load(
                args.id.toString()
            );
        },
        Artists: async (parent, args, context, info) =&amp;gt; {
            const artists = await (
                context.getAllArtists as DataLoader&amp;lt;string, Artist[]&amp;gt;
            ).load('1');
            if (!artists) {
                throw new GraphQLError(`Albums not found.`);
            }
            for (const artist of artists) {
                (context.getArtistsById as DataLoader&amp;lt;string, Artist&amp;gt;).prime(
                    artist.ArtistId.toString(),
                    artist
                );
            }
            return artists;
        },
        Track: async (parent, args, context, info) =&amp;gt; {
            return (context.getTracksById as DataLoader&amp;lt;string, Track&amp;gt;).load(args.id.toString());
        },
        Tracks: async (parent, args, context, info) =&amp;gt; {
            const tracks = await (
                context.getAllTracks as DataLoader&amp;lt;string, Track[]&amp;gt;
            ).load('1');
            for (const track of tracks) {
                (context.getTracksById as DataLoader&amp;lt;string, Track&amp;gt;).prime(
                    track.TrackId.toString(),
                    track
                );
            }
            const tracksByAlbumId = keyByArray(tracks, 'AlbumId');
            for (const [AlbumId, tracks] of Object.entries(tracksByAlbumId)) {
                (context.getTracksByAlbumId as DataLoader&amp;lt;string, Track[]&amp;gt;).prime(
                    AlbumId,
                    tracks
                );
            }
            return tracks;
        },
    },
    Album: {
        async Artist(parent, args, context, info) {
            return (context.getArtistsById as DataLoader&amp;lt;string, Artist&amp;gt;).load(
                parent.ArtistId.toString()
            );
        },
        async Tracks(parent, args, context, info) {
            const tracks = await (context.getTracksByAlbumId as DataLoader&amp;lt;string, Track[]&amp;gt;).load(
                parent.AlbumId.toString()
            );
            for (const track of tracks) {
                (context.getTracksById as DataLoader&amp;lt;string, Track&amp;gt;).prime(
                    track.TrackId.toString(),
                    track
                );
            }
            return tracks
        },
    },
    Artist: {
        async Albums(parent, args, context, info) {
            const albums = await (context.getAlbumsByArtistId as DataLoader&amp;lt;string, Album[]&amp;gt;).load(
                parent.ArtistId.toString()
            );
            if (Array.isArray(albums)) {
                for (const album of albums) {
                    (context.getAlbumsById as DataLoader&amp;lt;string, Album&amp;gt;).prime(
                        album.AlbumId.toString(),
                        album
                    );
                }

            }
            return albums || [];
        },
    },
    Track: {
        async Album(parent, args, context, info) {
            return (context.getAlbumsById as DataLoader&amp;lt;string, Album&amp;gt;).load(
                parent.AlbumId!.toString()
            );
        },
    }
};

export const schema = createSchema({
    typeDefs,
    resolvers,
});

const server = createServer(
    createYoga({
        schema,
        plugins: [
            useDataLoader(
                "getAlbumsById",
                (context) =&amp;gt;
                    new DataLoader((keys: Readonly&amp;lt;string[]&amp;gt;) =&amp;gt;
                        genericBatchFunction(keys, { name: "Album", id: "AlbumId" })
                    )
            ),
            useDataLoader(
                "getAllAlbums",
                (context) =&amp;gt;
                    new DataLoader(async (keys: Readonly&amp;lt;string[]&amp;gt;) =&amp;gt; {
                        const albums = await sql`SELECT * FROM ${sql("Album")}`;
                        return keys.map((key) =&amp;gt; albums);
                    })
            ),
            useDataLoader(
                "getAlbumsByArtistId",
                (context) =&amp;gt;
                    new DataLoader((keys: Readonly&amp;lt;string[]&amp;gt;) =&amp;gt;
                        genericBatchFunction(keys, { name: "Album", id: "ArtistId" }, true)
                    )
            ),
            useDataLoader(
                "getArtistsById",
                (context) =&amp;gt;
                    new DataLoader((keys: Readonly&amp;lt;string[]&amp;gt;) =&amp;gt;
                        genericBatchFunction(keys, { name: "Artist", id: "ArtistId" })
                    )
            ),
            useDataLoader(
                "getAllArtists",
                (context) =&amp;gt;
                    new DataLoader(async (keys: Readonly&amp;lt;string[]&amp;gt;) =&amp;gt; {
                        const artists = await sql`SELECT * FROM ${sql("Artist")}`;
                        return keys.map((key) =&amp;gt; artists);
                    })
            ),
            useDataLoader(
                "getTracksById",
                (context) =&amp;gt;
                    new DataLoader((keys: Readonly&amp;lt;string[]&amp;gt;) =&amp;gt;
                        genericBatchFunction(keys, { name: "Track", id: "TrackId" })
                    )
            ),
            useDataLoader(
                "getTracksByAlbumId",
                (context) =&amp;gt;
                    new DataLoader((keys: Readonly&amp;lt;string[]&amp;gt;) =&amp;gt;
                        genericBatchFunction(keys, { name: "Track", id: "AlbumId" }, true)
                    )
            ),
            useDataLoader(
                "getAllTracks",
                (context) =&amp;gt;
                    new DataLoader(async (keys: Readonly&amp;lt;string[]&amp;gt;) =&amp;gt; {
                        const tracks = await sql`SELECT * FROM ${sql("Track")}`;
                        return keys.map((key) =&amp;gt; tracks);
                    })
            ),
        ],
    })
);

server.listen(4000, () =&amp;gt; {
    console.info("Server is running on http://localhost:4000/graphql");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This GraphQL API has a single purpose - to read the data from the database. Although the API is quite simple, it requires a considerable amount of code. Now imagine a complex enterprise application and the amount of code you would have to write.&lt;/p&gt;

&lt;p&gt;The amount of code you have to write is one of many concerns. There are other things you need to think about, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the N+1 problem&lt;/li&gt;
&lt;li&gt;extending security rules per field in the schema&lt;/li&gt;
&lt;li&gt;adding gateway features (caching, rate limiting, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the above is difficult and time-consuming to implement. Hasura solves issues such as the N+1 problem and provides features such as caching, rate limiting, and authorization by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  N+1 problem
&lt;/h3&gt;

&lt;p&gt;GraphQL query execution usually involves executing a resolver for each field. Let's say you are fetching artists and their tracks. Each "artist" record in the database would invoke a function to fetch their N "tracks". As a result, the total round trips become N+1, which could become a huge performance bottleneck. The depth of the query makes the number of queries grow exponentially.&lt;/p&gt;

&lt;p&gt;Hasura mitigates the N+1 problem because, at its core, the server is a compiler. That means it compiles all GraphQL queries to a single SQL query, thus reducing the number of hits to the database and improving the performance.&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://hasura.io/blog/architecture-of-a-high-performance-graphql-to-sql-server-58d9944b8a87/" rel="noopener noreferrer"&gt;how Hasura solves the GraphQL N+1 Problem&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Rules
&lt;/h3&gt;

&lt;p&gt;Most applications require an authentication system to answer questions such as "&lt;em&gt;Does this (valid) user have permission to access this resource or perform this action?&lt;/em&gt;". It specifies what data a particular user can access and what action the user can perform. Implementing such a critical system is a challenging task.&lt;/p&gt;

&lt;p&gt;Hasura allows you to define granular role-based access control rules for every field in your GraphQL schema. It's granular enough to control access to any row or column in your database.&lt;/p&gt;

&lt;p&gt;With row-level access control, users can access tables without having access to all rows on that table. This is particularly useful for protecting sensitive personal data, which is part of the table. This way, you can allow all users to access a table but only a specific number of rows in that table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-engine-cdn.hasura.io%2Flearn-hasura%2Fassets%2Fgraphql-hasura-auth%2Frow-level-access-control.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-engine-cdn.hasura.io%2Flearn-hasura%2Fassets%2Fgraphql-hasura-auth%2Frow-level-access-control.png" alt="Row level permissions"&gt;&lt;/a&gt;&lt;em&gt;Row Level Permissions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Column-level access control lets you restrict access to specific columns in the table. This is useful to hide data that are not relevant, sensitive, or used for internal purposes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-engine-cdn.hasura.io%2Flearn-hasura%2Fassets%2Fgraphql-hasura-auth%2Fcolumn-level-access-control.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgraphql-engine-cdn.hasura.io%2Flearn-hasura%2Fassets%2Fgraphql-hasura-auth%2Fcolumn-level-access-control.png" alt="Column level permissions"&gt;&lt;/a&gt;&lt;em&gt;Column Level Permissions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Combining both these rules gives a flexible and powerful way to control data access to different stakeholders involved.&lt;/p&gt;

&lt;p&gt;How does it work? Hasura uses the role/user information from the session variables and the actual request to validate the request against the rules defined by you. If the request/operation is allowed, it generates a SQL query, which includes the row/column-level constraints from the access control rules. It then sends it to the database to perform the required operation (fetch the necessary rows for queries, insert/edit rows for mutations, etc).&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://hasura.io/docs/latest/auth/authorization/index/" rel="noopener noreferrer"&gt;Authorization and Access Control&lt;/a&gt; in the documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding gateway features
&lt;/h3&gt;

&lt;p&gt;A production-ready API requires features such as caching, rate limiting, monitoring, and observability. When you build the API manually, you must consider and implement all these features.&lt;/p&gt;

&lt;p&gt;There can be latency issues and slow response times for GraphQL queries because of the response size, the location of the server, and the number of concurrent requests. Hasura has metadata about the data models across data sources and the authorization rules at the application level. This enables Hasura to provide end-to-end application caching. Cached responses are stored for a period of time in an LRU (least recently used) cache and removed from the cache as needed based on usage.&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://hasura.io/docs/latest/queries/response-caching/" rel="noopener noreferrer"&gt;caching&lt;/a&gt; in the documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Save time and resources
&lt;/h3&gt;

&lt;p&gt;For enterprise-level Data APIs, the above features are a must. The API must be secure and performant. That results in massive work to implement just the basic CRUD functionalities. Once you add authorization, caching, rate limiting, and other features, it requires even more effort and resources.&lt;/p&gt;

&lt;p&gt;What if you could skip all these tedious and time-consuming tasks? In the next section, you'll see how you can have a production-ready GraphQL API within minutes without writing code, plus all the above features (and more).&lt;/p&gt;

&lt;h2&gt;
  
  
  Building GraphQL APIs without writing resolvers
&lt;/h2&gt;

&lt;p&gt;Hasura is a GraphQL Engine that makes your data instantly accessible over a real-time GraphQL API. It connects to your data source and automatically generates the GraphQL schema, queries, mutations, subscriptions, CRUD operations, and authorization.&lt;/p&gt;

&lt;p&gt;It doesn't require writing any code unless you want to add custom business logic. You get everything out of the box.&lt;/p&gt;

&lt;p&gt;Let's continue by creating a Data API with Hasura to demonstrate the power of Hasura. There are two ways to use Hasura:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using &lt;a href="https://cloud.hasura.io/" rel="noopener noreferrer"&gt;Hasura Cloud&lt;/a&gt;, which is the easiest way to use and build Hasura applications&lt;/li&gt;
&lt;li&gt;using Docker to &lt;a href="https://hasura.io/docs/latest/getting-started/docker-simple/" rel="noopener noreferrer"&gt;self-host it&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article uses Hasura Cloud. Navigate to your Hasura Cloud dashboard and create a new project by clicking the "New Project" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FA2N4Kud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FA2N4Kud.png" alt="Hasura Cloud Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next step, click the "Create Free Project" button, and you should have a Hasura project up and running within seconds. You should see the console after launching the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FuxptkID.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FuxptkID.png" alt="Hasura Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step involves connecting the Hasura app to your database. Navigate to the "DATA" tab, then to "Create New Database", and click the "Create Neon Database" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FrENFML7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FrENFML7.png" alt="Hasura Connect Neon Database"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The database should be created &amp;amp; connected within seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FKciOQ1R.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FKciOQ1R.png" alt="Hasura Database page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click the "Create Table" button to create a new table. For this example, create a &lt;code&gt;users&lt;/code&gt; table with the following columns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; of type UUID (Primary Key)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;username&lt;/code&gt; of type Text&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;country&lt;/code&gt; of type Text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Select the &lt;code&gt;id&lt;/code&gt; as the Primary Key and save the table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FpWmXKR1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FpWmXKR1.png" alt="Create table in Hasura console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you navigate back to the "API" tab, you should see all the available GraphQL operations. You get all the operations needed to access and mutate data without writing code (or resolvers). On top of that, you also get real-time capabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FYquyQus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FYquyQus.png" alt="Hasura Cloud Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This simple example demonstrates how Hasura improves the process of building and shipping Data APIs to production. It unblocks and allows developers to move extremely fast since they automatically get all the critical features.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does that work
&lt;/h2&gt;

&lt;p&gt;A GraphQL Resolver is a function that specifies how to process a specific GraphQL operation and turn it into data. A conventional GraphQL API can't exist without resolvers because they are an essential part of it.&lt;/p&gt;

&lt;p&gt;Hasura takes a different approach, though. Instead of using the conventional GraphQL resolvers, it uses a GraphQL Engine. The engine, which is actually a compiler, compiles the GraphQL operation into an efficient SQL query.&lt;/p&gt;

&lt;p&gt;Let's take the Chinook database as an example again. The Chinook data model represents a digital media store, including tables for artists, albums, media tracks, invoices, and customers. The example below illustrates a couple of untracked tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FnhsQTEa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FnhsQTEa.png" alt="Track tables in Hasura"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the tables are untracked, they are not exposed over the GraphQL API. To expose them to the public, you need to track them. When you track the tables, the GraphQL Engine automatically generates a bunch of things, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a GraphQL type definition&lt;/li&gt;
&lt;li&gt;queries&lt;/li&gt;
&lt;li&gt;mutations&lt;/li&gt;
&lt;li&gt;subscriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the &lt;a href="https://hasura.io/docs/latest/getting-started/how-it-works/index/#tracking-tables--schema-generation" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for the complete list of things it generates.&lt;/p&gt;

&lt;p&gt;Once the engine tracks the tables, it knows how to respond when users request data from the database to which these tables belong. You can also see the available operations in the "Explorer" tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FPn4FiL3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FPn4FiL3.png" alt="Hasura Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As mentioned earlier, Hasura compiles the GraphQL operation into a SQL query. Consider the following query from the above image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
  Album {
    Title
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click on the &lt;code&gt;Analyze&lt;/code&gt; button to see what happens under the hood when you run the query. Clicking the button opens a new pop-up with the "Generated SQL" and the "Execution Plan", as shown in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FVCjT6dL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FVCjT6dL.png" alt="Query Analysis in Hasura"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The generated SQL statement for the query is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
  coalesce(json_agg("root"), '[]') AS "root"
FROM
  (
    SELECT
      row_to_json(
        (
          SELECT
            "_e"
          FROM
            (
              SELECT
                "_root.base"."Title" AS "Title"
            ) AS "_e"
        )
      ) AS "root"
    FROM
      (
        SELECT
          *
        FROM
          "public"."Album"
        WHERE
          ('true')
      ) AS "_root.base"
  ) AS "_root"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's how Hasura works. Instead of using GraphQL resolvers, it generates SQL statements at runtime and then runs them against your database.&lt;/p&gt;

&lt;p&gt;The video illustrates how Hasura doesn't need to use resolvers for data interop and what the key differences are with other systems.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0GfFu5shmO0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Additional resources to learn more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/architecture-of-a-high-performance-graphql-to-sql-server-58d9944b8a87/" rel="noopener noreferrer"&gt;Architecture of a high performance GraphQL to SQL engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/fast-graphql-execution-with-query-caching-prepared-statements/" rel="noopener noreferrer"&gt;Blazing fast GraphQL execution with query caching and Postgres prepared statements
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding custom business logic
&lt;/h2&gt;

&lt;p&gt;Even though Hasura doesn't use resolvers, you can join your existing GraphQL APIs with your Hasura application. That's possible with the help of Remote Schemas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remote Schemas
&lt;/h3&gt;

&lt;p&gt;Hasura can merge remote GraphQL schemas and provide a unified GraphQL API. Think of it like automated schema stitching. All you need to do is build your GraphQL service and provide the HTTP endpoint to Hasura. Your GraphQL service can be written in any language or framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhasura.io%2Fdocs%2Fassets%2Fimages%2Fremote-schema-arch-5bb135f3789ab646431fd2f60e85a59a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhasura.io%2Fdocs%2Fassets%2Fimages%2Fremote-schema-arch-5bb135f3789ab646431fd2f60e85a59a.png" alt="Hasura Remote Schemas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That enables users to connect their existing GraphQL API to their Hasura application. For instance, if you have a GraphQL payment API, you can join it to your Hasura application with the help of Remote Schemas.&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://hasura.io/docs/latest/remote-schemas/index/" rel="noopener noreferrer"&gt;Remote Schemas&lt;/a&gt; in the documentation.&lt;/p&gt;

&lt;p&gt;Additionally, there are other ways of adding custom business logic, such as Actions and Event Triggers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hasura Actions
&lt;/h3&gt;

&lt;p&gt;Actions are a way to extend Hasura's schema with custom business logic using custom queries and mutations. Actions can be added to Hasura to handle various use cases such as data validation, data enrichment from external sources, and any other complex business logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhasura.io%2Fdocs%2Fassets%2Fimages%2Factions-arch-98d3a4de3ecd8b4e4d622d4d7a6143d6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhasura.io%2Fdocs%2Fassets%2Fimages%2Factions-arch-98d3a4de3ecd8b4e4d622d4d7a6143d6.png" alt="Hasura Actions diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, Actions in Hasura allow for integrating new and existing REST APIs with your Hasura application. For example, if you have a Node.js REST API for sending emails, you can plug it into Hasura without making any changes to the existing API.&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://hasura.io/docs/latest/actions/index/" rel="noopener noreferrer"&gt;Actions&lt;/a&gt; in the documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event Triggers
&lt;/h3&gt;

&lt;p&gt;Hasura can be used to create Event Triggers on tables in the database. Event Triggers reliably capture events (such as insert, update, and delete) on specified tables and invoke HTTP webhooks to carry out any custom logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhasura.io%2Fdocs%2Fassets%2Fimages%2Fdata-triggers-arch-1f34cc10f2174a0649406af40c219f42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhasura.io%2Fdocs%2Fassets%2Fimages%2Fdata-triggers-arch-1f34cc10f2174a0649406af40c219f42.png" alt="Hasura Event Triggers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A simple example would be triggering an API endpoint to send a welcome email once a user is added to the database.&lt;/p&gt;

&lt;p&gt;Read more about &lt;a href="https://hasura.io/docs/latest/event-triggers/index/" rel="noopener noreferrer"&gt;Event Triggers&lt;/a&gt; in the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Exposing your data via a GraphQL API doesn't mean you need to spend resources and effort building it manually. You can let Hasura do all the heavy lifting for you. In the use cases where you require custom business logic, you can add it with the help of Actions, Remote Schemas, and Event Triggers.&lt;/p&gt;

&lt;p&gt;Additional material:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hasura.io/learn/graphql/backend-stack/introduction/" rel="noopener noreferrer"&gt;Develop backend applications in various languages and integrate them with Hasura&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/docs/latest/index/" rel="noopener noreferrer"&gt;Hasura GraphQL Engine Documentation
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/docs/latest/data-federation/index/" rel="noopener noreferrer"&gt;Data federation in Hasura&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/docs/latest/getting-started/how-it-works/index/" rel="noopener noreferrer"&gt;How Hasura GraphQL Engine Works&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>graphql</category>
      <category>api</category>
    </item>
    <item>
      <title>Announcing Pod42, the Hasura ChatGPT Bot</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Wed, 25 Jan 2023 13:30:38 +0000</pubDate>
      <link>https://dev.to/hasurahq/announcing-pod42-the-hasura-chatgpt-bot-4g04</link>
      <guid>https://dev.to/hasurahq/announcing-pod42-the-hasura-chatgpt-bot-4g04</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatu3baondmwdco04bfq8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatu3baondmwdco04bfq8.jpeg" alt="Announcing Pod42, the Hasura ChatGPT Bot" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are happy to announce the launch of &lt;strong&gt;Pod42&lt;/strong&gt;, a bot that answers questions related to Hasura. It uses the GPT AI model, trained on resources like Hasura’s docs, learn and blog portals.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: It’s in alpha, so expect some bugs or issues with answers and the sources it gives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting started with Pod42
&lt;/h2&gt;

&lt;p&gt;The bot has been integrated into our discord server. In case you haven’t joined yet, head over to &lt;a href="https://hasura.io/discord" rel="noopener noreferrer"&gt;https://hasura.io/discord&lt;/a&gt; to join HasuraHQ discord server.&lt;/p&gt;

&lt;p&gt;Once inside the server, head to &lt;a href="https://discord.com/channels/407792526867693568/1067425955217428487" rel="noopener noreferrer"&gt;#pod42-support&lt;/a&gt; channel and tag the &lt;code&gt;@Pod42&lt;/code&gt; bot to ask a question. The format for asking a question looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Pod42 &amp;lt;question&amp;gt;

// example
@Pod42 What is Hasura?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing for fun
&lt;/h2&gt;

&lt;p&gt;Here are some of the answers we have been getting from the bot after training it with our data sources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F147przzi8nwfsur08wng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F147przzi8nwfsur08wng.png" alt="Announcing Pod42, the Hasura ChatGPT Bot" width="800" height="434"&gt;&lt;/a&gt;&lt;em&gt;Why use Hasura?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A fun attempt by David below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qrku1ofrjvcghaovh11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qrku1ofrjvcghaovh11.png" alt="Announcing Pod42, the Hasura ChatGPT Bot" width="800" height="786"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another one about read replicas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6rhqt3mu38qn82jptvjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6rhqt3mu38qn82jptvjq.png" alt="Announcing Pod42, the Hasura ChatGPT Bot" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;The bot is in alpha, which means it might give inaccurate or incomplete answers sometimes. Use the source/s to verify the information, and when in doubt, reach out to help-forum. Also, we're constantly training Pod42 to improve it. If you have any feedback, please let us know. Any feedback is appreciated!&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>GraphQL Security in Production with Automated Allow Lists</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Thu, 19 Jan 2023 10:32:09 +0000</pubDate>
      <link>https://dev.to/hasurahq/graphql-security-in-production-with-automated-allow-lists-1ii0</link>
      <guid>https://dev.to/hasurahq/graphql-security-in-production-with-automated-allow-lists-1ii0</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8xfqQOoF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/graphql-security-automated-allow-lists-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8xfqQOoF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/graphql-security-automated-allow-lists-1.png" alt="GraphQL Security in Production with Automated Allow Lists" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In some cases, you might want to restrict your Hasura application to a limited number of GraphQL operations (queries/mutations/subscriptions). The reason for doing it might be to avoid excessive data exposure, API scraping and to protect your application &amp;amp; data.&lt;/p&gt;

&lt;p&gt;Hasura allows you to restrict the number and type of available operations with the "Allow List" feature. The "Allow List" represents a list of allowed GraphQL Operations that can be executed in your application. So the Hasura GraphQL Engine only runs the operations specified in the "Allow List".&lt;/p&gt;

&lt;p&gt;Moreover, there is a way to generate an Allow List based on your front-end code automatically. This way, the allow list and your front-end code are always in sync!&lt;/p&gt;

&lt;p&gt;The article presents 3 ways to generate allow lists in Hasura, with the last one being fully automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable "Allow List"
&lt;/h2&gt;

&lt;p&gt;The "Allow List" feature is disabled by default. You need to navigate to your project settings and enable it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MXrSC4_i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/W1RA75U.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MXrSC4_i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/W1RA75U.png" alt="GraphQL Security in Production with Automated Allow Lists" width="880" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the &lt;code&gt;HASURA_GRAPHQL_ENABLE_ALLOWLIST&lt;/code&gt; environment variable to &lt;code&gt;true&lt;/code&gt;, as shown in the figure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Allow List of operations in Hasura
&lt;/h2&gt;

&lt;p&gt;Let's use the following Hasura application as an example. It's a simple application that stores users. Each user has an id and a name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cmS6ujCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/KW5TU07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cmS6ujCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/KW5TU07.png" alt="GraphQL Security in Production with Automated Allow Lists" width="880" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;code&gt;API&lt;/code&gt; tab and click on the &lt;code&gt;Allow List&lt;/code&gt; at the top of the page (right-hand side).&lt;/p&gt;

&lt;p&gt;That opens the "Allow List" manager. This is where you can add, modify and remove operations in your "Allow List".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TgmY7PJ5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/6Hd7MIk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TgmY7PJ5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/6Hd7MIk.png" alt="GraphQL Security in Production with Automated Allow Lists" width="880" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on the &lt;code&gt;Add Operation&lt;/code&gt; button opens a new modal where you can add an allowed operation. There are 3 ways to add an operation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;write it manually&lt;/li&gt;
&lt;li&gt;upload a file with the operation/operations&lt;/li&gt;
&lt;li&gt;pick it from the &lt;code&gt;Quick Add&lt;/code&gt; dropdown (it displays the latest operation executed)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w3YYJNaI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/eSjfkdH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w3YYJNaI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/eSjfkdH.png" alt="GraphQL Security in Production with Automated Allow Lists" width="880" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you add the operation, you can see it on the "Allow List" page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KxtlZyYQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/67pW5Uc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KxtlZyYQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/67pW5Uc.png" alt="GraphQL Security in Production with Automated Allow Lists" width="880" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Another way to create an AllowList
&lt;/h2&gt;

&lt;p&gt;There's another way you can manage your "Allow List". Navigate to the &lt;code&gt;MONITORING&lt;/code&gt; tab and click on the &lt;code&gt;Allow Lists&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;If you navigate to the &lt;code&gt;NEW OPERATIONS&lt;/code&gt; page, you can see the operations you executed that are not in the "Allow List" yet. That means Hasura Cloud allows you to choose from a list of operations you executed previously and add the ones you want to the "Allow List".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HB8KfNNc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/puceR6B.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HB8KfNNc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/puceR6B.png" alt="GraphQL Security in Production with Automated Allow Lists" width="880" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of manually writing the operations, you can select the ones you want to add to the "Allow List" and click the &lt;code&gt;Add to Allowlist&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uvF6E1Nn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/92lrc05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uvF6E1Nn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/92lrc05.png" alt="GraphQL Security in Production with Automated Allow Lists" width="880" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, you should see them on the &lt;code&gt;ALLOWED OPERATIONS&lt;/code&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically Generate an AllowList
&lt;/h2&gt;

&lt;p&gt;Creating an AllowList manually is fine, especially when the application is not large. However, once the application grows, it can become time-consuming to manually add operations to the AllowList. Keeping the list in sync with your front-end code can also be tedious.&lt;/p&gt;

&lt;p&gt;Thankfully, there is a way to automate that with the GraphQL Code Generator library. The GraphQL Code Generator automatically generates code from your GraphQL schema and operations. It also has a plugin &lt;a href="https://npmjs.com/package/@graphql-codegen/hasura-allow-list"&gt;@graphql-codegen/hasura-allow-list&lt;/a&gt; that automates the creation of the "AllowList" based on the GraphQL Queries found in your front-end code.&lt;/p&gt;

&lt;p&gt;After you set up &lt;code&gt;@graphql-codegen&lt;/code&gt; in your project, add the following to your &lt;code&gt;codegen&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
 generates: {
  'path/to/metadata/allow_list.yaml': {
    plugins: ['hasura-allow-list']
  }
 }
}

export default config;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the process of creating the "AllowList" is fully automated, the "AllowList" and your front-end code are always in sync.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The plugin only works in the JavaScript ecosystem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's important to mention that this is a community plugin, and not the official Hasura solution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additional resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hasura.io/graphql/security/"&gt;Learn how to declaratively protect your GraphQL APIs with Hasura Cloud - best practices to configure authz rules, allow lists, rate limiting &amp;amp; schema restrictions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/learn/graphql/hasura-advanced/security/"&gt;Learn how to optimize your Hasura application for security&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>graphql</category>
      <category>security</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The rise of GraphQL APIs on data warehouses</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Tue, 17 Jan 2023 12:16:47 +0000</pubDate>
      <link>https://dev.to/hasurahq/the-rise-of-graphql-apis-on-data-warehouses-174j</link>
      <guid>https://dev.to/hasurahq/the-rise-of-graphql-apis-on-data-warehouses-174j</guid>
      <description>&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CP_4gMgV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/graphql-api-data-warehouses.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CP_4gMgV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/graphql-api-data-warehouses.png" alt="The rise of GraphQL APIs on data warehouses" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Data warehouses are used to store valuable enterprise data. Data APIs – both GraphQL and REST – are emerging as a tool to power frictionless and high-quality integrations between applications/services and data in enterprise data warehouses. In this article, we will talk about drivers behind the rise of data APIs on data warehouses, challenges with current approaches to building, operating, and scaling data APIs, and how Hasura is addressing those challenges for our customers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nu0rJB7E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/wL1CZDFuFkF3YtD4GV5F8RMcJC1JhDN4k1gM_N0St-qKBT_4SthjmBSmHdVZsiqbdWFoaK1j4tjh8-SFetYBwqKOaAPJ5rbzEXVD6Fy6Q57BlkRj4CC4oPcSV6FpO5mb0ozxFHYx1VdMW-b6hwsbLIqAB5e3N175OnNCcSZ1pdayaTBZnYl0p3EZI04iNQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nu0rJB7E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/wL1CZDFuFkF3YtD4GV5F8RMcJC1JhDN4k1gM_N0St-qKBT_4SthjmBSmHdVZsiqbdWFoaK1j4tjh8-SFetYBwqKOaAPJ5rbzEXVD6Fy6Q57BlkRj4CC4oPcSV6FpO5mb0ozxFHYx1VdMW-b6hwsbLIqAB5e3N175OnNCcSZ1pdayaTBZnYl0p3EZI04iNQ" alt="The rise of GraphQL APIs on data warehouses" width="880" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The rise of data warehouse integrations&lt;/li&gt;
&lt;li&gt;Top use cases for data warehouse integration&lt;/li&gt;
&lt;li&gt;Data APIs are the solution to data warehouse integration&lt;/li&gt;
&lt;li&gt;Building APIs the traditional way can be slow and expensive&lt;/li&gt;
&lt;li&gt;Building and scaling GraphQL and REST APIs faster with Hasura&lt;/li&gt;
&lt;li&gt;Get started with instant API on your data warehouse&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The rise of data warehouse integrations
&lt;/h2&gt;

&lt;p&gt;Cloud data warehouses such as Snowflake, Google BigQuery, Amazon Redshift, etc. have seen a dramatic increase in adoption over the past few years. The maturity of the solutions themselves and the tooling (ETL) around them has improved a lot as well, making their adoption and usage a lot more tractable.&lt;/p&gt;

&lt;p&gt;With the broader movement to become more data-driven as well as to get more from their data investments, enterprises are constantly looking for ways to bring data into more initiatives, decisions, and experiences. How can the data be shared more widely (but in a secure and compliant way) to enable more internal and external stakeholders to make better decisions? How can data be incorporated into applications to build more delightful user experiences – for customers, partners, and employees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top use cases for data warehouse integration
&lt;/h2&gt;

&lt;p&gt;Let's take a look at the top 3 &lt;code&gt;data-first&lt;/code&gt; use cases&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data-as-a-product to other teams in the org&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Teams that own data want to publish it to other teams in their organization or externally to other partners. Increasingly they want to think of their data as a product.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; ​​Buyers with very high returns because of fraudulent reasons&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Embedded data features in existing products (apps or services)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;End users of digital products are increasingly expecting their product experience to be "smarter". For offline products, users benefit from a digital management and analytics experience. At the same time, product and business teams know that intelligent features can drive increased stickiness, retention and usage of their products.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; Show "predicted" end of month bills on a consumption-based billing service. Or, surface a customer 360 type of view for internal marketing and sales teams by aggregating data across multiple tables and data sources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Integrations to enrich data in other business tools&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enterprise tools and SaaS products (like Marketo, Salesforce, etc.) that are the operational foundation for the business can become a lot more valuable when they are integrated with more data and context, which usually resides in a data warehouse.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; Visualising data using Google's data studio.&lt;/p&gt;

&lt;p&gt;Data warehouses themselves are evolving to keep up with the demand for extracting more value from data. For example, Google Cloud has been investing heavily in BigQuery ML that allows teams to leverage their existing SQL knowledge to easily create and use ML models.&lt;/p&gt;

&lt;p&gt;The desire for integrating transactional and analytical use-cases has led to Snowflake announcing &lt;a href="https://www.snowflake.com/en/data-cloud/workloads/unistore/"&gt;Snowflake Unistore&lt;/a&gt; – their unified platform that brings product use-cases to the core CDW value-proposition that they are known for. However, customers are still looking for better ways to integrate their warehouse data into these use cases.&lt;/p&gt;

&lt;p&gt;Many are coming to the conclusion that APIs (and more specifically GraphQL APIs) are the most flexible, secure, and performant way to enable these use cases on their data warehouses.&lt;/p&gt;

&lt;p&gt;Let’s look at why data APIs are well-positioned to address these use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Data APIs are the solution to data warehouse integration
&lt;/h2&gt;

&lt;p&gt;Before we dive into why APIs are the best way to power this new breed of integration needs, let's look at how organizations primarily access data in their warehouses today, and why/how those current approaches fall short for these newer use cases.&lt;/p&gt;

&lt;p&gt;The 2 primary ways of connecting to data warehouses is either via JDBC / ODBC connectors in 3rd party applications or by writing SQL (or derivative) queries directly against the data. Both of these approaches are great for traditional analytics and visualization needs. For example visualizing data in a BI or visualization tools like Tableau or Looker; or querying and analyzing data for analytics and reporting use cases.&lt;/p&gt;

&lt;p&gt;But this type of &lt;code&gt;direct&lt;/code&gt; access to data doesn’t work for these new integration use cases because it tightly couples the consumption layer with the data layer in a way that is hard to maintain, operate, and secure. A data API access layer becomes necessary. Let’s look a few scenarios&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multiple applications/services need access to the same data&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiple tenants connected to the same database makes it hard to evolve the data and schema of the database, or to provide performance QoS or tiers to different consumers. Building an API layer between the producer and consumer allows decoupling, and provides an intermediate layer that can absorb those changes in a more elegant way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Enforcing authentication &amp;amp; authorization without an API is painful&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IAM in data warehouses are geared towards providing secure access to individuals and analysts, but are not ideal for application/service integrations.Sharing database credentials with multiple applications becomes a management headache and a potential security nightmare. Applications probably have a centralized authentication mechanism that should be leveraged, but databases typically don't integrate with that directly.&lt;/p&gt;

&lt;p&gt;Furthermore, entitlements (especially fine-grained policies) that govern access to certain rows or columns of data based on properties of the end-user or the application are hard to manage and enforce in the data warehouses because they don't provide those capabilities or because maintaining those policies in the data warehouse’s proprietary setup makes it hard to scale reviewing &amp;amp; auditing these security policies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GraphQL APIs allow data consumers to fetch just the data they need&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Direct access is fine when the consumer is trying to pull data that lives in a single table / view. But, can be inefficient because of multiple request roundtrips if you need to pull and join data from multiple tables/views/databases, etc. This inefficiency can have a non-trivial cost and performance impact depending on level of overfetching and query volume. REST APIs can also suffer from this limitation. GraphQL was created to solve exactly this problem for transactional workloads, and is gaining popularity for addressing this issue for analytical / OLAP data sources as well.&lt;/p&gt;

&lt;p&gt;All these reasons make APIs, and more specifically GraphQL APIs, a more elegant way to address the data integration use cases for data warehouses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building APIs the traditional way can be slow and expensive
&lt;/h2&gt;

&lt;p&gt;While GraphQL/REST APIs are great fit for these data integration use cases, building, scaling, and operating APIs can be a time-consuming task, which can in turn cause organizations to shy away from API-driven approach to data warehouse integrations.&lt;/p&gt;

&lt;p&gt;Here are just a few of the many things that add time and effort to build and operate APIs.&lt;/p&gt;

&lt;p&gt;Creating a single API end point requires developer to build and maintain these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boilerplate code for CRUD API&lt;/li&gt;
&lt;li&gt;Mapping client types and schema&lt;/li&gt;
&lt;li&gt;Baking authentication and authorization is tedious and risky&lt;/li&gt;
&lt;li&gt;Validation and error checking code in the API layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building GraphQL API brings additional costs. Developers need to write and maintain resolvers to map the GraphQL query to the underlying SQL queries. The effort to write resolvers increases significantly with the richness and complexity of the GraphQL queries, for e.g. if you have filtering, aggregations, complex and/or logic, etc. Furthermore, making sure that the resolvers are written in a way that the resulting GraphQL query is performant takes more planning and optimization.&lt;/p&gt;

&lt;p&gt;Operating APIs efficiently brings its own set of challenges. Making sure that the APIs are performant, reliable, and can scale smoothly with load can become a heavy lift, especially as this expertise is not typical in teams managing the central data warehouse. Updating the API to keep up with changes at the database level needs some discipline as well to make sure the consumers have a smooth experience.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hasura completely eliminates these challenges with traditional API creation and operation workflows. It allows organizations to become API-driven, but without all the effort that goes into API creation, scaling, security, and operations.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building and scaling GraphQL and REST APIs faster with Hasura
&lt;/h2&gt;

&lt;p&gt;Hasura provides a GraphQL API over transactional sources of data, and over the last year we've been seeing first-hand how developers and teams increasingly need to access data from a data warehouse.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hasura allows rapid creation of GraphQL APIs (or REST) by just configuring the data sources and the mapping of underlying data models into API models, and then Hasura provides an API that has joining, filtering, pagination &amp;amp; aggregation capabilities built-in. Hasura also has a session-aware caching layer that makes integrations with realtime applications easier with OLAP and CDW systems.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Imagine the following the eCommerce schema in a Data Warehouse&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6yauVsbE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/3qrXwEnTCtqTlP-xDU5O3_f_p9ZQz3JCN5SvOpqcQrMRL9yXIVNf2NfjU3hKgsOPb7-bPw3yWB1WBCSH9mBUAqeYeVBJyBC7MUPPVUMCNPItLLJ6ypIUvm5wZ5alPRHGFSrtCme2ourKKgMjNZ2uT5ZxvsPU_8LR3n1aJkj3ij_Yk-_CzM4biHXjXEdTAw" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6yauVsbE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/3qrXwEnTCtqTlP-xDU5O3_f_p9ZQz3JCN5SvOpqcQrMRL9yXIVNf2NfjU3hKgsOPb7-bPw3yWB1WBCSH9mBUAqeYeVBJyBC7MUPPVUMCNPItLLJ6ypIUvm5wZ5alPRHGFSrtCme2ourKKgMjNZ2uT5ZxvsPU_8LR3n1aJkj3ij_Yk-_CzM4biHXjXEdTAw" alt="The rise of GraphQL APIs on data warehouses" width="746" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An API call that fetches users based on number of orders they've made, is a normal query to make, but is challenging to represent as a flexible REST API call:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bseaXEVu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/GJzCkZPGuXE2NqdLtDqK5A7FcX-ESHI2qiuhilTVfzdlULBd6ZmODU4biz4Y8BRWJAWc_XA9gum8SiqysHooqmoYXCAZdqbT6aow_mmE3NrQ9gtqh_XGq7lXc_eutdmrYYHKqRpeSTPz-UUiZHgdugg6QHAB9I2eCwWJnx384fkkePwHXgITKI49dvRJ4w" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bseaXEVu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/GJzCkZPGuXE2NqdLtDqK5A7FcX-ESHI2qiuhilTVfzdlULBd6ZmODU4biz4Y8BRWJAWc_XA9gum8SiqysHooqmoYXCAZdqbT6aow_mmE3NrQ9gtqh_XGq7lXc_eutdmrYYHKqRpeSTPz-UUiZHgdugg6QHAB9I2eCwWJnx384fkkePwHXgITKI49dvRJ4w" alt="The rise of GraphQL APIs on data warehouses" width="880" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An API call that fetches orders based on what region the users are in, without fetching unnecessary user data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tmBZDLOv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/XJQ6ozIELl56jO3yOHQwQCt-vZBPkeEvhiZHlF_baiHVcVcozS2dgQO_Dy5TgN1ItnRoUcU3GPZBoxWEW6W7qeMlGYNhfK70Rowag4i4EwQtXdPDBnH9Uzl68GXcHBdNFlpF6zHvLi-StcpNH7mWEV0yBvCq31ZpOyWfqNxjAqdt7xDlWhike09mBiIGwQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tmBZDLOv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/XJQ6ozIELl56jO3yOHQwQCt-vZBPkeEvhiZHlF_baiHVcVcozS2dgQO_Dy5TgN1ItnRoUcU3GPZBoxWEW6W7qeMlGYNhfK70Rowag4i4EwQtXdPDBnH9Uzl68GXcHBdNFlpF6zHvLi-StcpNH7mWEV0yBvCq31ZpOyWfqNxjAqdt7xDlWhike09mBiIGwQ" alt="The rise of GraphQL APIs on data warehouses" width="798" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Streaming Subscriptions
&lt;/h3&gt;

&lt;p&gt;If you have a large amount of data, or "fast moving" data in Postgres, Hasura now allows you to instantly create an API for clients to fetch that data as a continuous stream. This API can be safely exposed to internal or external HTTP clients.&lt;/p&gt;

&lt;p&gt;For example, in the users placing orders scenario, during holiday season, there would be plenty of orders coming in every second. We can make use of streaming subscriptions to async load the data on the UI and not bombard the client to fetch orders which are still pending payments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;subscription getPendingOrders {
  orders_stream(cursor: {initial_value: {created_at: "2023-01-01"}, ordering: ASC}, batch_size: 2) {
    id
    amount
    created_at
    user_id
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;GraphQL Streaming Subscription with Hasura&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Hasura is highly optimized for performance, scalability and reliability. Hasura also provides caching and query optimization services, which further help improve performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;p&gt;Now, some queries are more frequently accessed than others. Typically, there could be latency and slow response times due to the size of the response, location of the server (in a distributed multi-region app), number of concurrent API calls.&lt;/p&gt;

&lt;p&gt;Hasura has metadata about the data models across data sources, and the authorization rules at the application level. This helps Hasura to provide end to end application caching.&lt;/p&gt;

&lt;h3&gt;
  
  
  High performance GraphQL to SQL Engine
&lt;/h3&gt;

&lt;p&gt;Internally Hasura parses a GraphQL query, gets an internal representation of the GraphQL AST. This is then converted to a SQL AST and with necessary transformations and variables the final SQL is formed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GraphQL Parser -&amp;gt; GraphQL AST -&amp;gt; SQL AST -&amp;gt; SQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This compiler based approach allows Hasura to form a single SQL query for a GraphQL query of any depth.&lt;/p&gt;

&lt;p&gt;Read more&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/architecture-of-a-high-performance-graphql-to-sql-server-58d9944b8a87/"&gt;Architecture of a high performance GraphQL to SQL engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/fast-graphql-execution-with-query-caching-prepared-statements/"&gt;Blazing fast GraphQL execution with query caching and Postgres prepared statements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GraphQL helps make one API call to fetch related data and a precise subset of data, instead of making multiple REST API calls that might return potentially redundant data.&lt;/p&gt;

&lt;p&gt;This has a cost implication because only the right subset of data is fetched and a single query is executed to fetch related pieces of data (as opposed to multiple REST API calls that fetch more data than is necessary)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FGqmW0Zh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/XrGRJI0vp8XH855sOIiS9NyTahuXZZgPYqmBr63EVp5gqJ23Q3cLM7TT3u2qqdTUkohmzDKBCkLARO8g9fLK6hzFrFYnb0zgG2ljJGjh5J29SCLz5RgTucZmP30b5rE9uM_LZxKqdikEFaZS2AavsVV_2riLyGdiG6o9zvg6fVz22k4vyAVfNM8OnHCyNg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FGqmW0Zh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/XrGRJI0vp8XH855sOIiS9NyTahuXZZgPYqmBr63EVp5gqJ23Q3cLM7TT3u2qqdTUkohmzDKBCkLARO8g9fLK6hzFrFYnb0zgG2ljJGjh5J29SCLz5RgTucZmP30b5rE9uM_LZxKqdikEFaZS2AavsVV_2riLyGdiG6o9zvg6fVz22k4vyAVfNM8OnHCyNg" alt="The rise of GraphQL APIs on data warehouses" width="880" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;Hasura has a fine-grained authorization engine that allows creating policies to restrict access to only particular elements of the data based on the session information in an API call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pgwZvuAO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/auth-high-level.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pgwZvuAO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/auth-high-level.png" alt="The rise of GraphQL APIs on data warehouses" width="880" height="608"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Hasura Authorization and Authentication&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Row level and column level permissions
&lt;/h4&gt;

&lt;p&gt;Hasura supports role-based access control system. Access control rules can be applied to all the CRUD operations (Create, Read, Update and Delete). Some operations can be completely restricted to not allow the user perform the operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yZlamqka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/row-level-access-control.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yZlamqka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/row-level-access-control.png" alt="The rise of GraphQL APIs on data warehouses" width="880" height="181"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Row Level Permissions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oaoapYtV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/column-level-access-control.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oaoapYtV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/column-level-access-control.png" alt="The rise of GraphQL APIs on data warehouses" width="880" height="181"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Column Level Permissions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The rules can be setup easily using Hasura Console, where you define a role and setup rules for access.&lt;/p&gt;

&lt;p&gt;For example, for row level permissions, we can set up filters like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"user_id": {"_eq": "X-Hasura-User-Id"}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above rule is equivalent to saying: allow this request to execute on the current row if the value of the &lt;code&gt;user_id&lt;/code&gt; column equals the value of the session variable &lt;code&gt;X-Hasura-User-Id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--goYKI_nF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/column-level-console.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--goYKI_nF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/column-level-console.png" alt="The rise of GraphQL APIs on data warehouses" width="880" height="312"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Column Level Access Rules&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Read more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/hasura-authorization-system-through-examples/"&gt;Hasura Authorization System through Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/implementing-authorization-for-organization-based-team-based-or-tenant-based-apps-in-hasura/"&gt;Implementing Authorization for Organization-based, Team-based, or Tenant-based Apps in Hasura&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get started with instant API on your data warehouse
&lt;/h2&gt;

&lt;p&gt;Hasura supports BigQuery, Snowflake, Amazon Athena, and Postgres flavors like AlloyDB and Hydra (geared for OLAP). If you and your team are looking at exposing data warehouse data over an API, Hasura can significantly reduce the time and effort to do so, in turn freeing up your developers to do other high-value work with direct ties to business outcomes.&lt;/p&gt;

&lt;p&gt;To get started follow our guides to get instant APIs on Snowflake or BigQuery.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hasura.io/docs/latest/databases/bigquery/index/"&gt;Get started with instant APIs on BigQuery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/docs/latest/databases/snowflake/index/"&gt;Get started with instant APIs on Snowflake&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see other guides &lt;a href="https://hasura.io/docs/latest/databases/index/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Or, reach out to the Hasura team for a custom demo to get all your questions answered about how Hasura can accelerate your path to APIs on data warehouses.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>database</category>
    </item>
    <item>
      <title>Best Practices Guide for GraphQL Observability with Hasura [Part 1]</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Fri, 06 Jan 2023 11:16:00 +0000</pubDate>
      <link>https://dev.to/hasurahq/best-practices-guide-for-graphql-observability-with-hasura-part-1-463j</link>
      <guid>https://dev.to/hasurahq/best-practices-guide-for-graphql-observability-with-hasura-part-1-463j</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HWL5q9gF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/best-practices-guide-graphql-observability.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HWL5q9gF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2023/01/best-practices-guide-graphql-observability.png" alt="Best Practices Guide for GraphQL Observability with Hasura [Part 1]" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hasura is an open-source product that accelerates API development by 10x by instantly giving you GraphQL or REST APIs on your data. Hasura plays a central role in your application stack as it can call out to your other services and databases while adding authorization and caching on top. Your development teams can quickly access critical data that powers their applications. Please &lt;a href="https://hasura.io/"&gt;see our website&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is a multi-part series. The current post delves into the various Observability metrics relevant for apps built with GraphQL / REST APIs. In the next parts, we will talk about the use cases and how Hasura specifically solves them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability
&lt;/h2&gt;

&lt;p&gt;As your app grows and becomes more complex, it becomes increasingly difficult to understand how it is used and performs. This is where observability comes in. Observability is the ability to gauge a system’s internal conditions by looking at its outputs, such as metrics or logs. Observability means you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gain insights into the functionality and health of your systems, collect data, visualize them, and set up alerts for potential problems.&lt;/li&gt;
&lt;li&gt;Have distributed tracing provide end-to-end visibility into actual requests and code, helping you improve your application’s performance.&lt;/li&gt;
&lt;li&gt;Audit, debug, and analyze logs from all your services, apps, and platforms at scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Hasura can act as a central gateway for your application stack, we take observability very seriously.&lt;/p&gt;

&lt;h2&gt;
  
  
  SLOs, SLIs, and the Three Pillars
&lt;/h2&gt;

&lt;p&gt;The path to thoroughly observing any app is to ensure you have your SLOs defined, SLIs identified, and telemetry data that enable the collection of those SLIs. In production, your SRE team would define service level objectives (SLO) that are intended to measure the reliability of your app. Each SLO is a target value or range of values for a service level measured by a service level indicator (SLI). For example, a service level indicator could be the number of requests per second, and an SLO could be that our app needs to be able to serve a minimum of ten thousand requests per second.&lt;/p&gt;

&lt;p&gt;Once our SLOs and SLIs are defined, we use the three pillars of observability to ensure we are meeting our objectives.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Three Pillars of Observability
&lt;/h3&gt;

&lt;p&gt;Observability relies on &lt;a href="https://hasura.io/docs/latest/deployment/best-practices/observability/#the-basics"&gt;three key pillars&lt;/a&gt;: logs, metrics, and traces. Access to these components does not automatically make systems more visible, but when used together, they can provide powerful tools for understanding a system’s internal state.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Metrics are used to determine if there’s a problem to begin with. They provide snapshots of a system’s metadata, such as server resources used or requests per second. Metrics can monitor performance, identify trends, and set alerts for potential troubles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Traces, used to find where the problem is, provide end-to-end visibility into a system’s operations by tracing requests through all the different services. Traces can show how a system is functioning, identify bottlenecks, and improve performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Logs, used to determine what the problem is, provide timestamped messages of events in a system. You can use them to debug issues.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hasura Cloud and Enterprise come out of the box with logs, tracing, and the following metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request rate&lt;/li&gt;
&lt;li&gt;Error rate&lt;/li&gt;
&lt;li&gt;Average request execution time&lt;/li&gt;
&lt;li&gt;Active subscriptions&lt;/li&gt;
&lt;li&gt;Number of WebSockets open&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All observability data is available via the &lt;a href="https://hasura.io/docs/latest/observability/overview/"&gt;Hasura Console&lt;/a&gt;, one of our APM integrations such as &lt;a href="https://hasura.io/docs/latest/observability/integrations/datadog/"&gt;Datadog&lt;/a&gt; and &lt;a href="https://hasura.io/docs/latest/observability/integrations/newrelic/"&gt;New Relic&lt;/a&gt;, or any APM receiver that supports the &lt;a href="https://hasura.io/docs/latest/observability/integrations/opentelemetry/"&gt;OpenTelemetry specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the traditional way of rolling your own GraphQL server, you have to manually set up all your resolver logic, observability, and authorization code.&lt;/p&gt;

&lt;p&gt;Hasura Cloud and Enterprise come with support out of the box.&lt;/p&gt;

&lt;h4&gt;
  
  
  Metrics
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tQGK4X56--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/docs/assets/images/monitoring-tab-overview-45b904df0b6c14e7902a990d8ba8aaad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tQGK4X56--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/docs/assets/images/monitoring-tab-overview-45b904df0b6c14e7902a990d8ba8aaad.png" alt="Best Practices Guide for GraphQL Observability with Hasura [Part 1]" width="880" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Request Rate
&lt;/h5&gt;

&lt;p&gt;The request rate represents how many requests a service receives per unit of time, such as 100 requests per minute (RPM). This is the most fundamental metric, as you must design your entire architecture around your expected request rate. As this metric grows, you must scale up your system with techniques such as database sharding or risk your system being overwhelmed and unreliable.&lt;/p&gt;

&lt;h6&gt;
  
  
  How to Use
&lt;/h6&gt;

&lt;p&gt;Request rate tracks the overall usage of a system. It becomes too performance-intensive at high request rates to monitor every request. Instead, you would sample a percentage. Some other best practices:&lt;/p&gt;

&lt;p&gt;Scale our services up and down dynamically based on the request rate. An example is &lt;a href="https://hasura.io/learn/graphql/hasura-advanced/performance/2-horizontal-scaling/"&gt;Hasura Cloud’s automatic horizontal scaling&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
If there are trends in RPM, such as more requests in the morning, you can pre-provision infrastructure.&lt;br&gt;&lt;br&gt;
When RPM spikes above expected usage, an alert should warn of a possible DOS attack.&lt;br&gt;&lt;br&gt;
High RPM from specific users may warrant setting up &lt;a href="https://hasura.io/docs/latest/security/api-limits/#rate-limits"&gt;IP or auth-based rate limiting&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Error Rate
&lt;/h5&gt;

&lt;p&gt;The error rate represents how many errors a service receives in a period of time. Most HTTP APIs signal errors via HTTP status codes, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses"&gt;400 for client errors&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#server_error_responses"&gt;500 for server errors&lt;/a&gt;. GraphQL differs because most errors are in the body’s &lt;code&gt;errors&lt;/code&gt; key. Hasura automatically parses both forms of errors for you.&lt;/p&gt;

&lt;p&gt;Errors are an inherent part of distributed systems. Network communication is complex, and a lot can go wrong.&lt;/p&gt;

&lt;h6&gt;
  
  
  How to Use
&lt;/h6&gt;

&lt;p&gt;Error rates are a good indicator of the overall health of your systems and are often the quickest way to spot problems. We can distinguish apparent disruptions over background noise by viewing the error rate over time and looking for patterns.&lt;/p&gt;

&lt;p&gt;A couple of ways you can use the error rate are monitoring to quickly roll back deployments when deploying service updates using techniques like &lt;a href="https://martinfowler.com/bliki/CanaryRelease.html"&gt;canary&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Blue-green_deployment"&gt;blue-green&lt;/a&gt; deployments or triggering an alert anytime the error rate is over a baseline amount.&lt;/p&gt;

&lt;h6&gt;
  
  
  Viewing Individual Errors
&lt;/h6&gt;

&lt;p&gt;For REST HTTP APIs, the HTTP status code tells us general information about the error. For example, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429"&gt;HTTP 429&lt;/a&gt; “indicates the user has sent too many requests in a given amount of time”. Based on the code, we can begin troubleshooting.&lt;/p&gt;

&lt;p&gt;GraphQL does not use HTTP status codes unless it’s a server error. Instead, the response object &lt;a href="https://spec.graphql.org/draft/#sec-Errors"&gt;has an &lt;code&gt;errors&lt;/code&gt; key&lt;/a&gt; that you need to check for errors.&lt;/p&gt;

&lt;h5&gt;
  
  
  Average Request Execution Time
&lt;/h5&gt;

&lt;p&gt;The average request execution time is calculated by the total time taken by all requests during time range / time range in minutes.&lt;/p&gt;

&lt;h6&gt;
  
  
  How to Use
&lt;/h6&gt;

&lt;p&gt;Latency is a huge factor in user experience, so as developers, we should try as hard as possible to reduce this metric. If your data allows it, &lt;a href="https://hasura.io/docs/latest/queries/response-caching/"&gt;caching built into Hasura&lt;/a&gt; is a significant first step in lowering execution time. Using tracing, which we learn about in an upcoming section, we can see remote APIs that need to be optimized or queries for which we may need to add an index.&lt;/p&gt;

&lt;h5&gt;
  
  
  Subscriptions
&lt;/h5&gt;

&lt;p&gt;With &lt;a href="https://hasura.io/docs/latest/subscriptions/index/"&gt;Hasura GraphQL subscriptions&lt;/a&gt;, there are two main components: &lt;a href="https://hasura.io/docs/latest/observability/subscription-workers/"&gt;database subscriptions&lt;/a&gt; and the &lt;a href="https://hasura.io/docs/latest/observability/websockets/"&gt;WebSocket connection to the client&lt;/a&gt;.&lt;/p&gt;

&lt;h6&gt;
  
  
  How to Use
&lt;/h6&gt;

&lt;p&gt;Primarily useful to monitor performance, the subscription metrics allow you to see the usage of GraphQL subscriptions. You can see a &lt;a href="https://hasura.io/docs/latest/observability/websockets/"&gt;list of clients connected via WebSocket&lt;/a&gt;. The &lt;a href="https://hasura.io/docs/latest/observability/subscription-workers/"&gt;database subscriptions feature&lt;/a&gt; helps you diagnose any database performance issue related to subscriptions. To understand the mapping between a WebSocket client and the underlying database subscription, please check out &lt;a href="https://github.com/hasura/graphql-engine/blob/master/architecture/live-queries.md"&gt;this article on how we scaled to one million GraphQL subscriptions&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Logs
&lt;/h4&gt;

&lt;p&gt;Once metrics have identified an incident, you can drill into individual logs to find the root cause. Hasura has many logging options, such as choosing what layers emit logs, so please read &lt;a href="https://hasura.io/docs/latest/deployment/logging/"&gt;the documentation&lt;/a&gt;. Here are a few ways we can use the Hasura logs to monitor our system:&lt;/p&gt;

&lt;h5&gt;
  
  
  Aggregating by Query Name
&lt;/h5&gt;

&lt;p&gt;With typical REST HTTP APIs, we aggregate our statistics by the endpoint. Since GraphQL uses one endpoint for all requests, we should combine the &lt;a href="https://hasura.io/docs/latest/security/allow-list/"&gt;security feature of allow lists&lt;/a&gt; with &lt;a href="https://graphql.org/learn/queries/#operation-name"&gt;aggregating by operation name&lt;/a&gt;. This reduces our monitoring surface drastically because all queries are determined ahead of time.&lt;/p&gt;

&lt;p&gt;This view is built-in to &lt;a href="https://hasura.io/docs/latest/observability/operations/"&gt;Hasura Console&lt;/a&gt; but can be done with any APM system using the data in the &lt;a href="https://hasura.io/docs/latest/deployment/logging/#query-log-structure"&gt;&lt;code&gt;query-log&lt;/code&gt; layer&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Diagnosing Errors
&lt;/h5&gt;

&lt;p&gt;Requests to Hasura record success or error in the &lt;a href="https://hasura.io/docs/latest/deployment/logging/#http-log-structure"&gt;&lt;code&gt;http-log&lt;/code&gt; layer&lt;/a&gt;. Once your metrics alert on an abnormal error rate, search your logs for the &lt;code&gt;level: error.&lt;/code&gt; The response objects have an &lt;code&gt;error&lt;/code&gt; key with the GraphQL error.&lt;/p&gt;

&lt;h4&gt;
  
  
  Distributed Tracing
&lt;/h4&gt;

&lt;p&gt;A request can go through many services; therefore, it can be difficult to see where it failed or slowed down. Distributed tracing via &lt;a href="https://github.com/openzipkin/b3-propagation"&gt;the B3 spec&lt;/a&gt; combined with observability tooling allows you to follow a request through its life and pinpoint issues.&lt;/p&gt;

&lt;p&gt;Hasura, acting as the API gateway, can trace external APIs and database queries.&lt;/p&gt;

&lt;h5&gt;
  
  
  External API Tracing
&lt;/h5&gt;

&lt;p&gt;When a request comes into Hasura, it generates a B3 header if one doesn’t already exist. Whenever an API encounters a B3 header, it will report its request metrics to an APM system. The APM system then aggregates all the information to &lt;a href="https://hasura.io/docs/latest/observability/tracing/"&gt;build a trace for each request&lt;/a&gt;, allowing you to &lt;a href="https://hasura.io/docs/latest/observability/tracing/#visualizing-traces"&gt;visualize them efficiently&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Database Tracing
&lt;/h5&gt;

&lt;p&gt;There is no standard method for trace propagation in Postgres, so &lt;a href="https://hasura.io/docs/latest/observability/tracing/#propagation-via-postgres"&gt;Hasura injects a transaction-local JSON variable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To get the trace information into database logs and native database monitoring tools, &lt;a href="https://hasura.io/docs/latest/observability/query-tags/"&gt;query tags&lt;/a&gt; are used. Query tags are SQL comments appended to the generated SQL statements with tracing metadata. Tools like &lt;code&gt;pganalyze&lt;/code&gt; can use them to help you optimize your queries.&lt;/p&gt;

&lt;p&gt;Quickly and easily set up a GraphQL API and explore Hasura’s observability features for free on Hasura Cloud.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cloud.hasura.io?utm_source=hasura&amp;amp;utm_medium=blog&amp;amp;utm_campaign=observability-best-practices"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5aaQ8O2C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://graphql-engine-cdn.hasura.io/assets/blog/deploy-to-hasura-cloud.png" alt="Best Practices Guide for GraphQL Observability with Hasura [Part 1]" width="188" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In conclusion, observability is a critical aspect of modern systems. By collecting and analyzing data from logs, metrics, and traces, you can gain valuable insights into the health and performance of your services. With the right tools and techniques offered out of the box with Hasura Cloud and Enterprise, you can improve the observability of your systems and ensure that they are functioning optimally.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>performance</category>
    </item>
    <item>
      <title>Top 3 Reasons why Enterprises Choose Hasura</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Tue, 20 Dec 2022 09:46:42 +0000</pubDate>
      <link>https://dev.to/hasurahq/top-3-reasons-why-enterprises-choose-hasura-595f</link>
      <guid>https://dev.to/hasurahq/top-3-reasons-why-enterprises-choose-hasura-595f</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojq37f752woqu7co4ubq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojq37f752woqu7co4ubq.png" alt="Top 3 Reasons why Enterprises Choose Hasura" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GraphQL is a popular choice for enterprises looking to improve the performance, efficiency, and flexibility of their APIs. Functionality like being able to fetch very specific data (no over or under-fetching), having a strongly typed system, and schema introspection all make GraphQL a great choice.&lt;/p&gt;

&lt;p&gt;However, building a GraphQL API manually requires considerable time, effort, and investment. Engineering organizations need to write a significant amount of code by hand just for the basic CRUD functionalities of your GraphQL API. Adding authorization, multiple data sources to your end-point, and custom business logic requires even more work and code. This work will likely take months or even years depending on complexity and a team of developers.&lt;/p&gt;

&lt;p&gt;We find more and more enterprises asking themselves whether their business is a GraphQL business or not. Why spend time and resources on tedious tasks that are not actually core to your IP when you can get the most heavily leveraged GraphQL functionality out of the box?&lt;/p&gt;

&lt;p&gt;This is where Hasura comes in.&lt;/p&gt;

&lt;p&gt;Hasura is an open-source product that auto-generates GraphQL or REST APIs with a built-in authorization layer for your data. Point Hasura to the databases, REST &amp;amp; GraphQL endpoints, and third-party APIs, and it provides a unified, connected, real-time, and secured GraphQL API for all of your data.&lt;/p&gt;

&lt;p&gt;Hasura speeds up the development process, and in this article, we will explore the top three reasons why enterprises choose Hasura for their GraphQL needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Access
&lt;/h2&gt;

&lt;p&gt;Data in the modern world most likely comes from multiple sources. In an enterprise environment, you might have data from sources such as your database(s), internal APIs, and third-party services.&lt;/p&gt;

&lt;p&gt;That means there are time-consuming and repetitive tasks, such as connecting those data sources to your applications. After you connect them, the work continues by ensuring they function properly on their own and with the other existing data sources.&lt;/p&gt;

&lt;p&gt;So each time a new data source needs to be integrated with an existing application, the process repeats:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Integrate the data source -&amp;gt; configure it -&amp;gt; test it -&amp;gt; maintain it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That can quickly become a bottleneck that slows down the development team and the application. So the question is - &lt;strong&gt;is there a better alternative?&lt;/strong&gt; The alternative is Hasura.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclu4udrfiqnh42z1443m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclu4udrfiqnh42z1443m.png" alt="Top 3 Reasons why Enterprises Choose Hasura" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the diagram shows, Hasura abstracts all the tedious, boring, and time-consuming work. You connect all the data sources (databases, REST and GraphQL APIs) to Hasura, and it automatically generates a unified, secure, and real-time GraphQL API.&lt;/p&gt;

&lt;p&gt;In the case of databases, the Hasura GraphQL Engine automatically generates the GraphQL Schema and operations such as queries, subscriptions, and mutations. It also enables you to connect existing REST and GraphQL APIs to your Hasura application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgtwmpaab0d6yh8x668q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgtwmpaab0d6yh8x668q.png" alt="Top 3 Reasons why Enterprises Choose Hasura" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hasura unifies all data sources and enables the client to access unified data through the GraphQL API provided by Hasura. Creating a unified data-access API layer also allows developers to access and manipulate data in their database without having to write complex SQL queries. To access the data, developers can use the GraphQL API endpoint provided by Hasura to send queries and mutations that specify the data that should be retrieved or updated.&lt;/p&gt;

&lt;p&gt;Hasura provides several tools and features to help developers work with data more effectively. For example, the Hasura Console provides an intuitive interface for exploring and testing the GraphQL API, and the Hasura CLI allows developers to manage their data from the command line.&lt;/p&gt;

&lt;p&gt;Overall, Hasura’s approach to data access makes it easy for developers to work with their data and build robust and scalable applications on top of enterprise data.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Production Readiness
&lt;/h2&gt;

&lt;p&gt;In addition to improving data access, another primary business value Hasura provides is reducing the time to market by offering you critical features out of the box such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;caching&lt;/li&gt;
&lt;li&gt;real-time capabilities&lt;/li&gt;
&lt;li&gt;authorization&lt;/li&gt;
&lt;li&gt;observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... and much more.&lt;/p&gt;

&lt;p&gt;As mentioned previously, building a GraphQL API manually requires effort just for the basic functionalities. Implementing complex operational features like caching, real-time capabilities, or authorization requires even more effort.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flumx4w6t1r9x4fhusv7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flumx4w6t1r9x4fhusv7e.png" alt="Top 3 Reasons why Enterprises Choose Hasura" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Traditionally, building an API manually takes weeks or months and a team of developers. In this process, the developers also need to optimize the API to be production ready. That includes security, authorization, availability, scalability, and observability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuuzxzdrc6i7c9gasktm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuuzxzdrc6i7c9gasktm.png" alt="Top 3 Reasons why Enterprises Choose Hasura" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hasura transforms the process of building the API into a service that comes with features such as authorization, real-time capabilities, observability, and availability out of the box. And everything highlighted in the above diagram.&lt;/p&gt;

&lt;p&gt;Hasura and the developer tooling around it are designed to help teams build products faster and more efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Increased Developer Productivity
&lt;/h2&gt;

&lt;p&gt;Hasura cuts down development time by 50% to 80%. You can find more from our case studies &lt;a href="https://hasura.io/user-stories/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Hasura does this by automating key GraphQL development tasks such as defining schemas, writing resolvers, and setting up a server. Secondly, it automates and abstracts tedious tasks such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defining the GraphQL schema&lt;/li&gt;
&lt;li&gt;defining and implementing the GraphQL resolvers&lt;/li&gt;
&lt;li&gt;creating and configuring the GraphQL server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... plus other mundane work.&lt;/p&gt;

&lt;p&gt;Such features are critical for an enterprise and tricky to implement. They require experienced API developers and a considerable amount of effort and time. Once they are implemented, someone needs to maintain them. Letting Hasura take care of them improves the developers’ productivity as they can focus on other critical tasks, like delivering core IP back to the business.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftj10fbmhsgkaewy4700x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftj10fbmhsgkaewy4700x.png" alt="Top 3 Reasons why Enterprises Choose Hasura" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another thing that increases developer productivity is Hasura’s metadata. The metadata is the brain of a Hasura application that captures vital information such as the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;connections to the data sources&lt;/li&gt;
&lt;li&gt;mapping the model from the data sources into an API&lt;/li&gt;
&lt;li&gt;relationships between the models&lt;/li&gt;
&lt;li&gt;authorization rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hasura uses this metadata information to generate a JSON API schema and exposes it through a GraphQL API. GraphQL is ideal for working with JSON data, as it is native to the JSON format. As an API specification, GraphQL allows developers to create flexible and powerful APIs that can operate on a wide range of data models and support a variety of methods. At the same time, it maintains the controlled boundaries that are necessary for a web service.&lt;/p&gt;

&lt;p&gt;Developers can then use the metadata to configure and manage their applications. As a result, it improves the developer experience and productivity since developers don’t have to write code to configure or enhance their applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Most of the work involved in building a Data API via GraphQL is either tedious and repetitive or difficult and time-consuming. It’ll require lots of effort and resources.&lt;/p&gt;

&lt;p&gt;But it doesn’t have to be that way. Hasura enables enterprises to build a production-ready Data API, with all the critical features, without having to write any code. That makes it easy for developers to quickly and easily build scalable and reliable applications without spending time on tedious and complex tasks.&lt;/p&gt;

&lt;p&gt;Overall, Hasura offers a comprehensive and robust platform for enterprise application development.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
    </item>
    <item>
      <title>Introducing Instant GraphQL APIs for Snowflake Cloud Data Platform</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Tue, 06 Dec 2022 18:14:33 +0000</pubDate>
      <link>https://dev.to/hasurahq/introducing-instant-graphql-apis-for-snowflake-cloud-data-platform-18kp</link>
      <guid>https://dev.to/hasurahq/introducing-instant-graphql-apis-for-snowflake-cloud-data-platform-18kp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xx1so2yuu2k71xnjlae.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xx1so2yuu2k71xnjlae.jpg" alt="Introducing Instant GraphQL APIs for Snowflake Cloud Data Platform" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Real-time access to data is essential for building modern applications. However, as the data footprint in an organization grows and spreads, it creates unique challenges for developers who need to aggregate data across multiple sources and develop the necessary APIs to securely and efficiently access and serve the data required to power their applications.&lt;/p&gt;

&lt;p&gt;To help development teams with these challenges, we're excited to announce the beta release of our new &lt;a href="https://hasura.io/graphql/database/snowflake/" rel="noopener noreferrer"&gt;GraphQL Data Connector for Snowflake&lt;/a&gt;. This powerful new database integration will allow Hasura users to instantly create a unified GraphQL API that runs read-only queries across all your Snowflake data. Hasura drastically reduces the time and effort required to build and secure data APIs, allowing teams to quickly power their analytical applications with data aggregated in Snowflake.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; - This beta release of the Hasura GraphQL Data Connector for Snowflake supports read-only queries. During the beta period, the connector is available for Hasura Enterprise Edition (EE) and all Hasura Cloud customers. Once this integration moves to General Availability (GA), it will only be available in the Hasura Enterprise plans. For more information, &lt;a href="https://hasura.io/docs/latest/databases/snowflake/index/" rel="noopener noreferrer"&gt;read our docs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Snowflake?
&lt;/h2&gt;

&lt;p&gt;Snowflake, often referred to as a Cloud Data Platform, provides a way to bring together and easily access data siloed across multiple sources. Snowflake enables elaborate application development, collaboration, and more across a wide spectrum of data sets, all in the cloud. This provides massive horizontal scalability along with other capabilities that meet a wide variety of application, reporting, and other data consumption needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Applications with Snowflake and Hasura
&lt;/h2&gt;

&lt;p&gt;Hasura customers across many industries, such as construction, energy, financial services, healthcare, and human resources, use Snowflake to house critical data needed to derive insights and make data-driven business decisions. Many of these customers also use or want to use Snowflake data to directly power analytics in internal and external data applications. Or they want to open their Snowflake data – in a safe and controlled way – to internal or external stakeholders to make data-driven decisions. However, this journey has some challenges.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The resource-intensive nature of building an API, especially if they intend to build multiple apps and services that use data from Snowflake.&lt;/li&gt;
&lt;li&gt;Customers have to maintain and manage secure connections to Snowflake so only the authorized individuals and applications have access to this business critical data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To address these challenges, our new integration with Snowflake helps by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Providing an instant GraphQL API on Snowflake to access data securely.&lt;/li&gt;
&lt;li&gt;Providing a unified GraphQL endpoint across Snowflake and other data sources such as PostgreSQL, SQL Server, BigQuery, Amazon Athena and CockroachDB. GraphQL is an extremely flexible query language for APIs and a runtime for fulfilling those queries with your existing data.&lt;/li&gt;
&lt;li&gt;Bringing Hasura's out-of-the-box capabilities to Snowflake, such as:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;a. &lt;a href="https://hasura.io/learn/graphql/hasura-advanced/performance/1-caching/" rel="noopener noreferrer"&gt;Query response caching&lt;/a&gt; allows you to cache responses in Hasura for frequently executed queries as opposed to retrieving the data from Snowflake every time. This greatly improves application performance and consequently your end-user experience.&lt;br&gt;
b. Declarative &lt;a href="https://hasura.io/docs/latest/auth/index/" rel="noopener noreferrer"&gt;authentication and authorization&lt;/a&gt;&lt;br&gt;
c. &lt;a href="https://hasura.io/docs/latest/security/index/" rel="noopener noreferrer"&gt;API security&lt;/a&gt;&lt;br&gt;
d. Integration with your custom REST API application workflows via &lt;a href="https://hasura.io/docs/latest/actions/index/" rel="noopener noreferrer"&gt;Actions&lt;/a&gt; and &lt;a href="https://hasura.io/docs/latest/event-triggers/index/" rel="noopener noreferrer"&gt;Event Triggers&lt;/a&gt;&lt;br&gt;
e. &lt;a href="https://hasura.io/docs/latest/observability/index/" rel="noopener noreferrer"&gt;Observability&lt;/a&gt; to ensure the reliability of your application stack&lt;/p&gt;

&lt;p&gt;The new data connector for Snowflake introduces new ways to query, correlate and aggregate data, enabling Snowflake customers to do more with their data and increase the ROI on their Snowflake investments across the board.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Can I Build with Hasura and Snowflake?
&lt;/h2&gt;

&lt;p&gt;We’ve seen some really creative use cases that are only possible by integrating Hasura with our new Snowflake database connector. Below are a few examples to explore with your team.&lt;/p&gt;
&lt;h3&gt;
  
  
  Surface Analytics Data in Customer-facing and Internal Apps
&lt;/h3&gt;

&lt;p&gt;Embedding analytics directly into applications is not only creating great user experiences, but it’s also helping teams build a competitive advantage. Quick and efficient data access is essential for building applications like an internal customer service dashboard that surfaces real-time analytics and helps deliver world-class customer support. With Hasura GraphQL, development teams can quickly build a unified API for Snowflake that efficiently serves up data from multiple tables, all within a single query, instead of making multiple fetches to individual tables. This saves developers countless hours of writing additional APIs and queries required to surface data from Snowflake.&lt;/p&gt;
&lt;h3&gt;
  
  
  Share Snowflake Data with Internal and External Consumers via APIs
&lt;/h3&gt;

&lt;p&gt;In an effort to be more data-driven, organizations want to open their Snowflake data to more internal and external stakeholders, giving them the data they need to make better decisions. But given the critical and sensitive nature of the data often stored in Snowflake, organizations are often hesitant to do this through typical built-in connectors (eg. JDBC) and prefer to share just the data they want via APIs. API services allow them to provide data access in a secure, controlled, and limited way. With the Hasura connector for Snowflake, customers can now generate those APIs in a fraction of the time it would have otherwise taken to build. With the built-in Hasura authorization engine, customers are also able to easily implement access privileges into the API layer.&lt;/p&gt;
&lt;h3&gt;
  
  
  Joining Data from Snowflake and Other Sources
&lt;/h3&gt;

&lt;p&gt;While Snowflake is often used as a central data warehouse, large organizations can still have critical data stored in other locations for a variety of reasons. Architecture or security designs, legacy databases that have yet to be migrated, or siloed business units can all lead to data being stored in locations outside of Snowflake. This poses a challenge when building applications that require data across those multiple sources. Hasura GraphQL creates an easy and efficient way to join Snowflake data with data from those additional sources. For example, maybe a company needs to map order data and shipment data during order fulfillment. Joining these tables across multiple data sources helps drive better product insights for things like returns, quality issues, and buyer profiling.&lt;/p&gt;
&lt;h2&gt;
  
  
  What’s Included in the Snowflake Connector Beta?
&lt;/h2&gt;

&lt;p&gt;The Hasura connector for Snowflake currently provides query support only, along with a host of other standard Hasura features called out above. For example, connecting two tables is a standard Hasura feature that really stands out when working with denormalized reporting data that is often used in Snowflake. For a full list of capabilities, visit the &lt;a href="https://hasura.io/docs/latest/databases/snowflake/index/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Can I Get Started?
&lt;/h2&gt;

&lt;p&gt;To get started, you will need&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An existing Snowflake account instance and data to connect Hasura to query against.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://cloud.hasura.io/login?pg=snowflake-launch-blog&amp;amp;plcmt=body&amp;amp;cta=hasura-cloud&amp;amp;tech=default" rel="noopener noreferrer"&gt;Hasura Cloud&lt;/a&gt; or Hasura Enterprise Edition self-hosted setup - version 2.16.0 or later.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;While the Snowflake connector is in beta, it is behind a feature flag. Please follow the steps outlined in the &lt;a href="https://hasura.io/docs/latest/databases/snowflake/getting-started/cloud/#step-2-add-your-snowflake-database-as-a-source-to-hasura" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to learn how to enable it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Navigate to the Data tab as shown and click on &lt;em&gt;Connect Database&lt;/em&gt;. The following dialog requires four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Turn on the &lt;a href="https://hasura.io/docs/latest/databases/snowflake/getting-started/cloud/#step-2-add-your-snowflake-database-as-a-source-to-hasura" rel="noopener noreferrer"&gt;experimental features&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Select Snowflake (beta) from the dropdown. This will then shape the rest of the connection form elements for the appropriate data needed for the Snowflake connection.&lt;/li&gt;
&lt;li&gt;Set the name you’d like the connection to be listed as.&lt;/li&gt;
&lt;li&gt;Add the JDBC connection string to your Snowflake instance. The JDBC connection string will need the URI path with credentials, warehouse, database, role, and schema added to the string. It would look something like this.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jdbc:snowflake://[URI instance to connect to]?user=[theUser]&amp;amp;password=[superSecretPassword]&amp;amp;warehouse=[THE_WAREHOUSE]&amp;amp;db=[the_db]&amp;amp;role=[role_like_PUBLIC]&amp;amp;schema=[schema_to_use]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Once those steps are done, click on &lt;em&gt;Connect Database&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FmE9PH7bnEBk-f4MmfNyTnWlgom6ulRhvZg-qdA_GztkaYx6UQBqzrNOvgkIKcH2qbprGNoocWHfyrPlHkChfzdhhTty_71Y77eqNVVTWK5HsX4FtXuqTGr51x72srY8rcGncIjCMYQKITS7qHQ3-NZx2KaIW8Up655zKVsZk6TVw4CGU-7LB9GbI2561Bg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FmE9PH7bnEBk-f4MmfNyTnWlgom6ulRhvZg-qdA_GztkaYx6UQBqzrNOvgkIKcH2qbprGNoocWHfyrPlHkChfzdhhTty_71Y77eqNVVTWK5HsX4FtXuqTGr51x72srY8rcGncIjCMYQKITS7qHQ3-NZx2KaIW8Up655zKVsZk6TVw4CGU-7LB9GbI2561Bg" alt="Introducing Instant GraphQL APIs for Snowflake Cloud Data Platform" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, you can select tables and query against those tables just like you would in any Hasura GraphQL API connected database. Let’s say, for example, you want to query a band along with the band’s albums. That query would look something like this, with the inferred nested object of albums returning as an array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query artistAlbums {
  CHINOOK_PUBLIC_ARTIST(where: {NAME: {_eq: "Metallica"}}) {
    NAME
    artistAlbums {
      TITLE
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result would then come back with the array of album titles for the particular artist &lt;code&gt;Metallica&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "data": {
    "CHINOOK_PUBLIC_ARTIST": [
      {
        "NAME": "Metallica",
        "artistAlbums": [
          {
            "TITLE": "Garage Inc. (Disc 1)"
          },
          {
            "TITLE": "Black Album"
          },
          {
            "TITLE": "Garage Inc. (Disc 2)"
          },
          {
            "TITLE": "Kill 'Em All"
          },
          {
            "TITLE": "Load"
          },
          {
            "TITLE": "Master Of Puppets"
          },
          {
            "TITLE": "ReLoad"
          },
          {
            "TITLE": "Ride The Lightning"
          },
          {
            "TITLE": "St. Anger"
          },
          {
            "TITLE": "...And Justice For All"
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correlative data across disparate tables within reporting tables are difficult requests to process. With Hasura connected to Snowflake, users can easily add relationships between tables to bring data together into a single GraphQL Query.&lt;/p&gt;

&lt;p&gt;A tactical example can be seen with the relationship shown in the console here. In this example, we have a music library database where we are trying to define a relationship between the Artist and Albums table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FGFt4pPHIThwspCSBgIGvZEMV92gWDKmC6ghnb5GesAy1-hCy9_aKfXNZXuz2cOupnr1ZqTca6deBUulJgqubVlFAkk68Bt-WB6ekPXlFJQ2fljbyMgY-oJCTS8O-MUiNDFiy1ECR9iwrpA8lD6PpxYY_B4Xukf596hD20OcaQ0Uz_2ilaq-x79ZiMG-IUg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FGFt4pPHIThwspCSBgIGvZEMV92gWDKmC6ghnb5GesAy1-hCy9_aKfXNZXuz2cOupnr1ZqTca6deBUulJgqubVlFAkk68Bt-WB6ekPXlFJQ2fljbyMgY-oJCTS8O-MUiNDFiy1ECR9iwrpA8lD6PpxYY_B4Xukf596hD20OcaQ0Uz_2ilaq-x79ZiMG-IUg" alt="Introducing Instant GraphQL APIs for Snowflake Cloud Data Platform" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hasura connects the Artist table to the Album table so that a relationship can traverse either direction when querying.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FGMvCTGidHn_xNYnj44BwOykfPoOL2YPcW6dkGIU0YvWL81v-XKcMl5IT5r_kiyn-6KRTkXy2y9kerrmsOZORfsfsHfvUXj8ZLQL8-ee-DqGS1YTMizxYkyxMW3UZveqH8qwZUA20vKMM8ZGQxdYNC_Ry-G6YnhUa6qbcRozyboCskLo2Dg0vQkKZ9dfBiw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FGMvCTGidHn_xNYnj44BwOykfPoOL2YPcW6dkGIU0YvWL81v-XKcMl5IT5r_kiyn-6KRTkXy2y9kerrmsOZORfsfsHfvUXj8ZLQL8-ee-DqGS1YTMizxYkyxMW3UZveqH8qwZUA20vKMM8ZGQxdYNC_Ry-G6YnhUa6qbcRozyboCskLo2Dg0vQkKZ9dfBiw" alt="Introducing Instant GraphQL APIs for Snowflake Cloud Data Platform" width="638" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following query shows how a nested query works based on that relationship, adding power to queries when working with Snowflake. Users now have multiple queries issued from a singular GraphQL query. Thereby reducing your query complexity, keeping the number of client calls minimal, and improving the readability of your code base.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FJnrggfgsBbOjGRr5qPSvJZQgFbW4XvHY2ui60H7-23XwzxOcNnXDKR3Cz5eXe-am03mbsGUTa_tmIrBXEFuNHL33m127TrrQK6jfS8rO6k5n4hrlScXKfNIzqlDFgDZYpIJC4b1cpoST2GL1goY93RZntvAsgrbdyagjYlcPpFbkZeW1kK7DbYIF4Rflqw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FJnrggfgsBbOjGRr5qPSvJZQgFbW4XvHY2ui60H7-23XwzxOcNnXDKR3Cz5eXe-am03mbsGUTa_tmIrBXEFuNHL33m127TrrQK6jfS8rO6k5n4hrlScXKfNIzqlDFgDZYpIJC4b1cpoST2GL1goY93RZntvAsgrbdyagjYlcPpFbkZeW1kK7DbYIF4Rflqw" alt="Introducing Instant GraphQL APIs for Snowflake Cloud Data Platform" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding caching is extremely easy. For example, using the query listed above, we can add the directive as shown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query artistAlbums @cached {
  CHINOOK_PUBLIC_ARTIST(where: {NAME: {_eq: "Metallica"}}) {
    NAME
    artistAlbums {
      TITLE
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Join our webinar on Jan 13
&lt;/h2&gt;

&lt;p&gt;We are hosting a live webinar on this topic on Jan 13, 2023, where we will do a technical deep dive on the connector, cover use cases, and more with a live demo. It’s a great opportunity to connect live with the technical team behind this capability. &lt;a href="https://hasura.io/events/webinar/instant-graphql-apis-on-snowflake-with-hasura/" rel="noopener noreferrer"&gt;Register here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to see a custom demo sooner? Get in touch with our team by &lt;a href="https://hasura.io/contact-us/" rel="noopener noreferrer"&gt;signing up here&lt;/a&gt;. Put in “Snowflake demo” in the notes, along with more context of your use case for Hasura+Snowflake.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Coming Next
&lt;/h2&gt;

&lt;p&gt;A reminder, this is the beta release, and we’re moving quickly to add new functionality for Snowflake as well as support for additional connectors. In the coming months, we’ll be building features like GraphQL mutation support for our API, and Snowflake will be one of the first connectors to receive that update.&lt;/p&gt;

&lt;p&gt;If you’d like to discuss Snowflake, request new features, or just ask questions about Hasura and Snowflake, &lt;a href="https://discord.com/invite/hasura" rel="noopener noreferrer"&gt;join our Discord&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Building a Digital Twin with MQTT and Hasura Streaming Subscriptions</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Tue, 06 Dec 2022 03:50:01 +0000</pubDate>
      <link>https://dev.to/hasurahq/building-a-digital-twin-with-mqtt-and-hasura-streaming-subscriptions-3ec8</link>
      <guid>https://dev.to/hasurahq/building-a-digital-twin-with-mqtt-and-hasura-streaming-subscriptions-3ec8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ig7d2po1bn6docd5rl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ig7d2po1bn6docd5rl.png" alt="Building a Digital Twin with MQTT and Hasura Streaming Subscriptions" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this blog we will be talking about how to build a digital twin of a toy car with an android phone (to leverage sensors),  &lt;a href="https://en.wikipedia.org/wiki/MQTT" rel="noopener noreferrer"&gt;MQTT broker&lt;/a&gt; and &lt;a href="http://hasura.io/" rel="noopener noreferrer"&gt;Hasura GraphQL Engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will go over,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building a digital twin of a toy car attached with a gyroscope sensor through an android device&lt;/li&gt;
&lt;li&gt;Setting up the device to stream real time sensor data to an MQTT Broker.&lt;/li&gt;
&lt;li&gt;Receiving data from the MQTT broker and sending it as a GraphQL mutation to Hasura GraphQL engine.&lt;/li&gt;
&lt;li&gt;Consume live data on the ReactJS application with Hasura GraphQL subscriptions&lt;/li&gt;
&lt;li&gt;Rendering the real time gyroscope sensor data as a 3D car model using ThreeJS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a Digital Twin?
&lt;/h2&gt;

&lt;p&gt;A digital twin is a virtual representation of a real-world physical system or product (a physical twin) that serves as the digital counterpart of it for practical purposes, such as system simulation, integration, testing, monitoring, and maintenance.&lt;/p&gt;

&lt;p&gt;Digital Twins are created in a way that it can receive sensor data in real time from the physical counterpart. This allows the twin to simulate the physical object in real time to learn about the characteristics of the physical twin. There are systems that also have controllers to remotely manage and maintain the system in real time.&lt;br&gt;&lt;br&gt;
It is also used to remotely debug large machinery, with the help of mixed reality gears- this ease out the need of skilled technicians to travel across the globe and save time and cost.&lt;/p&gt;

&lt;p&gt;Digital Twins are mainly used in&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manufacturing&lt;/li&gt;
&lt;li&gt;Automobiles&lt;/li&gt;
&lt;li&gt;Power Generation&lt;/li&gt;
&lt;li&gt;Healthcare&lt;/li&gt;
&lt;li&gt;Industry 4.0&lt;/li&gt;
&lt;li&gt;Town planning&lt;/li&gt;
&lt;li&gt;Remote Debugging&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Building a Digital Twin
&lt;/h2&gt;

&lt;p&gt;The basic requirements to build a digital twin are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physical object (a toy car in this case)&lt;/li&gt;
&lt;li&gt;Sensors to collect data (will be using an android device) &amp;amp; Controllers&lt;/li&gt;
&lt;li&gt;Connectivity to the central system (Internet)&lt;/li&gt;
&lt;li&gt;Server that manages the data flow and control events (Hasura GraphQL Engine &amp;amp; MQTT Broker)&lt;/li&gt;
&lt;li&gt;Virtual Representation (a static web application, using ThreeJS to render 3D in this case)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FDwle0TMUes2IbI8d6cuzZd-f4HJwttbzXXza0nbYokBJIY_u9CE_s0podsY_jiH2Ows6LhI2HDYksEC2IXwYGOIkmatJuZwjQg5SGKCHJt-15C-CtihF15zPhvl-VDtlwK_bmCg7wzYcoe74E8WTgyxCKKbl8nb_MHpOgGAgq6CrmMRbOSWj7YxQmaP0KQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FDwle0TMUes2IbI8d6cuzZd-f4HJwttbzXXza0nbYokBJIY_u9CE_s0podsY_jiH2Ows6LhI2HDYksEC2IXwYGOIkmatJuZwjQg5SGKCHJt-15C-CtihF15zPhvl-VDtlwK_bmCg7wzYcoe74E8WTgyxCKKbl8nb_MHpOgGAgq6CrmMRbOSWj7YxQmaP0KQ" alt="Building a Digital Twin with MQTT and Hasura Streaming Subscriptions" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note : The repository is available &lt;a href="https://github.com/soorajshankar/digital-twin-graphql" rel="noopener noreferrer"&gt;here&lt;/a&gt; and the commands mentioned in the steps are configured in the repository.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Setup a Physical Thing
&lt;/h2&gt;

&lt;p&gt;As shown above, we can have a toy car and an android phone attached to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FWdKUF62I8K8LBOA-KTGYaIANF9QrziuDMP63sQ0klAPigrxmeFDUG-eZS9dYuj6zNOTTMic4PGhSo1rDadH203WRm5gVNABg0MkOwxrZjxu_kgwZL89aO1boYHZvHLAinHhKXqzeOBJAEZ0D0W5Rrn1aSW1eEp0eM4YLeZDrtonbwpISn_j46mEcON9Ufw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FWdKUF62I8K8LBOA-KTGYaIANF9QrziuDMP63sQ0klAPigrxmeFDUG-eZS9dYuj6zNOTTMic4PGhSo1rDadH203WRm5gVNABg0MkOwxrZjxu_kgwZL89aO1boYHZvHLAinHhKXqzeOBJAEZ0D0W5Rrn1aSW1eEp0eM4YLeZDrtonbwpISn_j46mEcON9Ufw" alt="Building a Digital Twin with MQTT and Hasura Streaming Subscriptions" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2Fa8_f3cgbuyc_Qyh1BitASaibi8qGyDBnlCTVr8e6H_ueWjw1mp9lIQVpMpofP4CGNEP9po2bZ6Beu0f3y-9aklLVLP48TmYk0Tlrql28zporzE8t1YgmPCpakrsUOCcBa3GDg6brkAT7_qXRpuNBzpVPORgGIj54leOp3lNnH8saI9TO2e4shnvhOGennw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2Fa8_f3cgbuyc_Qyh1BitASaibi8qGyDBnlCTVr8e6H_ueWjw1mp9lIQVpMpofP4CGNEP9po2bZ6Beu0f3y-9aklLVLP48TmYk0Tlrql28zporzE8t1YgmPCpakrsUOCcBa3GDg6brkAT7_qXRpuNBzpVPORgGIj54leOp3lNnH8saI9TO2e4shnvhOGennw" alt="Building a Digital Twin with MQTT and Hasura Streaming Subscriptions" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The android device attached to the device will be able to collect real time sensor data (Gyroscope in this case) and send it to the cloud.&lt;/p&gt;
&lt;h2&gt;
  
  
  Streaming data to Cloud
&lt;/h2&gt;

&lt;p&gt;Considering most of the real world IoT systems use low footprint transfer protocols to send data from device to server communication, we will be using MQTT as the preferred protocol here.   &lt;/p&gt;

&lt;p&gt;We can also use GraphQL over HTTP from this point itself, but considering the current systems and integrations this demo will talk about consuming and ingesting data from end devices to MQTT Broker and then to a Postgres DB through a Hasura instance.&lt;/p&gt;

&lt;p&gt;To simplify the end device implementation, we will be using the android application  &lt;a href="https://hasurahq.slack.com/archives/C03N5SQC0MV/p1666953124176499" rel="noopener noreferrer"&gt;MQTT Simulator&lt;/a&gt; to collect and send rotational data to an MQTT broker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FCK8SIHqxrto48JdVvewTg4wjLm9V_F1LLjczwsSLm1NuD_Nl_mLj9oICztBpacVp6iNLoO8vczA4R9oRqSCNpbyU8B16Us7K4eG0Nrec5-3E3sHUAKxXWmd4YSAGRN1gYxF7odya8uv7G7V9W9V09-c-Z1Uqo0Qta8ezYFO0ol2MxRCBszgUJMCtBwN0NA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FCK8SIHqxrto48JdVvewTg4wjLm9V_F1LLjczwsSLm1NuD_Nl_mLj9oICztBpacVp6iNLoO8vczA4R9oRqSCNpbyU8B16Us7K4eG0Nrec5-3E3sHUAKxXWmd4YSAGRN1gYxF7odya8uv7G7V9W9V09-c-Z1Uqo0Qta8ezYFO0ol2MxRCBszgUJMCtBwN0NA" alt="Building a Digital Twin with MQTT and Hasura Streaming Subscriptions" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Receiving Data
&lt;/h2&gt;

&lt;p&gt;MQTT follows a topic based pub-sub pattern, and it requires a broker to handle this.&lt;/p&gt;

&lt;p&gt;For the demo purpose, let’s use &lt;a href="http://www.hivemq.com/demos/websocket-client/" rel="noopener noreferrer"&gt;HiveMQ&lt;/a&gt; public MQTT broker so that we don’t need any setup here.&lt;br&gt;&lt;br&gt;
If you use the above application, the data will be streaming to &lt;code&gt;digital_twin/android/&amp;lt;provided_device_id&amp;gt;&lt;/code&gt; by default and this can be listened easily by subscribing to the same topic or &lt;code&gt;digital_twin/android/#&lt;/code&gt;. # here will make sure any topic name that starts with the pattern will be subscribed.&lt;/p&gt;

&lt;p&gt;At this point, we have successfully received the data from the device to the internet/network, we now need a way to store this data and stream the data to the clients who require live data from the device.&lt;/p&gt;

&lt;p&gt;Now we need a place to store and access the data in a secure way, to do this we will be using Hasura GraphQL Engine backed with a postgres DB.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Setting up Hasura and Postgres DB&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Deploy to Hasura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwqbl80dn3bmg2rmilzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwqbl80dn3bmg2rmilzx.png" alt="Building a Digital Twin with MQTT and Hasura Streaming Subscriptions" width="188" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Hasura Console, head to Data tab and connect a Postgres DB if not done already.&lt;/li&gt;
&lt;li&gt;Head to Data-&amp;gt;SQL tab to create a table.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE "public"."device_data"("id" serial NOT NULL, "data" jsonb NOT NULL, "timestamp" timestamptz NOT NULL, "device_id" text NOT NULL, PRIMARY KEY ("id") );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Track the table &lt;code&gt;device_data&lt;/code&gt; so that it can be accessed through GraphQL APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Consuming MQTT data and sending to Hasura
&lt;/h2&gt;

&lt;p&gt;We need a NodeJS runtime to consume data from the MQTT runtime and send it to Hasura. I have a small NodeJS script that does the following,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subscribe to the MQTT broker with the same channel name used by the Android MQTT Simulator&lt;/li&gt;
&lt;li&gt;Parse the MQTT payload and transform the data
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client.on("message", function (topic, message) {
 // message is Buffer, convert this to generate GraphQL mutation from the message
 const decoded = getMutation(message.toString(), topic);
 sendToHasura(decoded, CONSTANTS.HASURA_HOST);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;GraphQL mutations (as shown below) on Hasura GraphQL Engine to store it on the DB.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mutation AddDeviceData($data: jsonb!, $device_id: String!, $timestamp: timestamptz!) {
 insert_device_data_one(object: {data: $data, device_id: $device_id, timestamp: $timestamp}) {
   id
 }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At this point we successfully managed to get the data to the postgres instance and the data flow will looks like the following&lt;/p&gt;

&lt;p&gt;To start this service,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;clone the project from &lt;a href="https://github.com/soorajshankar/digital-twin-graphql" rel="noopener noreferrer"&gt;https://github.com/soorajshankar/digital-twin-graphql&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Change .env file with your project configuration&lt;/li&gt;
&lt;li&gt;Run the command &lt;code&gt;yarn connect&lt;/code&gt;  or &lt;code&gt;npm run connect&lt;/code&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This command will connect to MQTT broker and start subscribing to the channel mentioned in the env file.&lt;/p&gt;

&lt;p&gt;You could also use the public MQTT broker (HiveMQ) used in the repository for testing purpose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FGl5qr7XMdTEBIN6Qh20xiuOhXvyO3mQcWYyKOLUHFxVGo2_mGDWHCcAEyk52MApsenywlM8YW19tSDDftQjOxRP2IWLc7gcT74LTdAYsEUa7FVKZwsOK5pbw_FqpaCDRDoIQGBmmCkTMJYsqiKFNQX1ksxArHigOm9cxgyrWHk9E6q59AhSnoYLn0Z_8KA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FGl5qr7XMdTEBIN6Qh20xiuOhXvyO3mQcWYyKOLUHFxVGo2_mGDWHCcAEyk52MApsenywlM8YW19tSDDftQjOxRP2IWLc7gcT74LTdAYsEUa7FVKZwsOK5pbw_FqpaCDRDoIQGBmmCkTMJYsqiKFNQX1ksxArHigOm9cxgyrWHk9E6q59AhSnoYLn0Z_8KA" alt="Building a Digital Twin with MQTT and Hasura Streaming Subscriptions" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Streaming Live Data to the Dashboard
&lt;/h2&gt;

&lt;p&gt;From this part we will be discussing the ReactJS application and the main functionalities.&lt;/p&gt;

&lt;p&gt;When you connect a postgres table on Hasura, you automatically get a GraphQL Subscription API to listen to the live changes. We will be using the latest &lt;a href="https://hasura.io/docs/latest/subscriptions/postgres/streaming/index/" rel="noopener noreferrer"&gt;streaming subscription&lt;/a&gt; feature from Hasura to stream the live data.&lt;/p&gt;

&lt;p&gt;In this case, the query looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;subscription GetDeviceData($timestamp: timestamptz) {
   device_data_stream(
     batch_size: 1
     cursor: { initial_value: { timestamp: $timestamp }, ordering: ASC }
     where: { device_id: { _eq: "android" } }
   ) {
     data
     device_id
     id
     timestamp
   }
 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how the streaming subscription work in general:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6z1b4q6rnpsc0he81f2u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6z1b4q6rnpsc0he81f2u.gif" alt="Building a Digital Twin with MQTT and Hasura Streaming Subscriptions" width="3498" height="2498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualization
&lt;/h2&gt;

&lt;p&gt;Visualizing data is very important in the connected world as it can save a lot of time to understand, make decisions and act on it. The concept of Digital Twin plays a great role in simplifying this task to an extent. Both human and simulated environments can process the data much faster when it is virtualized in a digital environment.&lt;br&gt;&lt;br&gt;
To do this we will be using ThreeJS model to visualize the toy car, the car model orientation will refresh frequently with the latest data stream.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/8408875/205365998-2c8a7e63-a906-4035-85ce-5a48796c4dc7.mov" rel="noopener noreferrer"&gt;Download demo video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/soorajshankar/digital-twin-graphql" rel="noopener noreferrer"&gt;https://github.com/soorajshankar/digital-twin-graphql&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Even though we built a digital twin of a toy car, this can be made useful for variety of use cases like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connected cars&lt;/li&gt;
&lt;li&gt;Remote Monitoring : Windmills, Smart Factories, Industry 4.0 and other Industrial IoT systems&lt;/li&gt;
&lt;li&gt;Remote Debugging with AR goggles and connected data. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also if you have an existing data stream/ queue, it can easily be connected to Hasura GraphQL to set up a secure and easy way to store and access realtime data until the end consumer.&lt;/p&gt;

</description>
      <category>mqtt</category>
      <category>graphql</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Next.js 13 Nested Layouts, Streaming SSR with Realtime GraphQL</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Tue, 22 Nov 2022 12:47:23 +0000</pubDate>
      <link>https://dev.to/hasurahq/nextjs-13-nested-layouts-streaming-ssr-with-realtime-graphql-4m5m</link>
      <guid>https://dev.to/hasurahq/nextjs-13-nested-layouts-streaming-ssr-with-realtime-graphql-4m5m</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsrkmdetzo5owu6hgcsm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsrkmdetzo5owu6hgcsm.jpg" alt="Next.js 13 Nested Layouts, Streaming SSR with Realtime GraphQL" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next.js, the open-source React framework by Vercel, has released version 13, laying the foundations to be dynamic without limits. Please read their &lt;a href="https://nextjs.org/blog/next-13" rel="noopener noreferrer"&gt;release blog post&lt;/a&gt; for an excellent overview.&lt;/p&gt;

&lt;p&gt;In this post, we will build a mini e-commerce store where you can view the products on sale, order one, and see a real-time feed of orders. We are covering new Next.js features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nested Layouts&lt;/li&gt;
&lt;li&gt;Data Fetching&lt;/li&gt;
&lt;li&gt;Streaming and Suspense&lt;/li&gt;
&lt;li&gt;Client and Server Components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also see how to integrate with Hasura GraphQL for realtime features such as &lt;a href="https://hasura.io/docs/latest/subscriptions/postgres/streaming/index/" rel="noopener noreferrer"&gt;Streaming Subscriptions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s dive in with the e-commerce schema from &lt;a href="https://hasura.io/reference-app/" rel="noopener noreferrer"&gt;the Hasura Super App&lt;/a&gt;. See our &lt;a href="https://hasura.io/data-hub/data-models-and-authorization/schema-share-data-model-ecommerce/" rel="noopener noreferrer"&gt;Data Hub&lt;/a&gt; for instructions on how to import the schema into Hasura.&lt;/p&gt;

&lt;p&gt;The source code is &lt;a href="https://github.com/hasura/graphql-engine/tree/master/community/sample-apps/nextjs-13" rel="noopener noreferrer"&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js Setup
&lt;/h2&gt;

&lt;p&gt;Create a Next.js TypeScript application. As of this blog post, we will use the experimental version because that allows us to use beta features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest &lt;span class="nt"&gt;--experimental-app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the dependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @graphql-codegen/cli @graphql-codegen/client-preset @graphql-typed-document-node/core dotenv graphql graphql-request graphql-ws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the contents of &lt;code&gt;next.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import('next').NextConfig} */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;appDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;experimental-edge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;remotePatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img6a.flixcart.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img5a.flixcart.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;experimental-edge&lt;/code&gt; runtime lets us use &lt;a href="https://edge-runtime.vercel.app/" rel="noopener noreferrer"&gt;Vercel's edge runtime&lt;/a&gt;. The term “Edge” refers to the orientation toward instant serverless compute environments and not a specific set of locations. The image section configures &lt;a href="https://beta.nextjs.org/docs/optimizing/images" rel="noopener noreferrer"&gt;Next.js's image component&lt;/a&gt; to use pictures from our e-commerce store.&lt;/p&gt;

&lt;p&gt;Our last step is adding two entries to &lt;code&gt;.env.local&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_HASURA_GRAPHQL_URL=&amp;lt;Hasura GraphQL endpoint&amp;gt;
NEXT_PUBLIC_HASURA_GRAPHQL_WS_URL=&amp;lt;Hasura websocket endpoint&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your Hasura instance has an admin secret, you can also set &lt;code&gt;HASURA_GRAPHQL_ADMIN_SECRET&lt;/code&gt; for use in the code generator.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Code Generator Setup
&lt;/h2&gt;

&lt;p&gt;When making TypeScript applications with GraphQL we highly recommend using &lt;a href="https://www.the-guild.dev/graphql/codegen" rel="noopener noreferrer"&gt;GraphQL Code Generator&lt;/a&gt;. Combined with the &lt;a href="https://github.com/prisma-labs/graphql-request" rel="noopener noreferrer"&gt;graphql-request&lt;/a&gt; library, we have fully typed queries and mutations by the magic of code generation!&lt;/p&gt;

&lt;p&gt;Create a file &lt;code&gt;codegen.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CodegenConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@graphql-codegen/cli&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CodegenConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;overwrite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_HASURA_GRAPHQL_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// If you have an admin secret set&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-hasura-admin-secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HASURA_GRAPHQL_ADMIN_SECRET&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;skipTypename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enumsAsTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scalars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lib/service/queries.graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lib/gql/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This config connects to our Hasura GraphQL instance and generates typings based on the client preset.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;package.json&lt;/code&gt; add a script to load environment variables and run the code generator &lt;code&gt;"codegen": "graphql-codegen --require dotenv/config --config codegen.ts dotenv_config_path=./.env.local"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, define our queries/mutations in &lt;code&gt;lib/service/queries.graphql&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GetProducts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GetProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;product_by_pk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;image_urls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$[0]"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order_product_arr_rel_insert_input&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;insert_order_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$products&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;billing_address_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;222&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;225&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;shipping_address_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;222&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, run the generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run codegen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  graphql-request Setup
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;graphql-request&lt;/code&gt; client in &lt;code&gt;lib/service/client.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GraphQLClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;graphql-request&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gqlClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphQLClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_HASURA_GRAPHQL_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pass in the environment’s global fetch, so our Next.js will work anywhere, regular Node.js or an edge runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested Layouts
&lt;/h2&gt;

&lt;p&gt;One of the new Next.js features is &lt;a href="https://beta.nextjs.org/docs/routing/pages-and-layouts#layouts" rel="noopener noreferrer"&gt;nested layouts&lt;/a&gt;. They allow us to have a shared parent component, and the router can render in child routes.&lt;/p&gt;

&lt;p&gt;To demonstrate this, we will display ten products on our home page. When you click on one, it will navigate the right-hand side to the product details page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetch Home Page Data
&lt;/h3&gt;

&lt;p&gt;Next.js 13 uses React’s new way of fetching data. Read more about the &lt;a href="https://github.com/reactjs/rfcs/pull/229" rel="noopener noreferrer"&gt;RFC on Github&lt;/a&gt;. We will use it to get a list of products.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;app/layout.tsx&lt;/code&gt;, we add an async function to get data from Hasura:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GetProductsDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../lib/gql/graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;gqlClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../lib/service/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./globals.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;gqlClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;GetProductsDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We call it in our component using async/await, then create an unordered list of product links:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;minWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;400px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;400px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;flexGrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With nested layouts and a &lt;code&gt;layout.tsx&lt;/code&gt; file, the URL will control the component output in &lt;code&gt;{children}&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product Details Page
&lt;/h3&gt;

&lt;p&gt;We will now add a &lt;a href="https://beta.nextjs.org/docs/routing/pages-and-layouts#pages" rel="noopener noreferrer"&gt;Next.js page&lt;/a&gt; that gets the product ID from the URL and fetches/displays the details.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;app/[id]/page.tsx&lt;/code&gt;. The &lt;code&gt;[id]&lt;/code&gt; means we will pass down data from the URL as a prop named &lt;code&gt;id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GetProductDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../lib/gql/graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;gqlClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../lib/service/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;product_by_pk&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;gqlClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GetProductDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;product_by_pk&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_urls&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Picture`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`$&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you click a link on the left, you should see the product details page appear on the right!&lt;/p&gt;

&lt;h3&gt;
  
  
  Streaming and Suspense
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://beta.nextjs.org/docs/data-fetching/fundamentals#streaming-and-suspense" rel="noopener noreferrer"&gt;Streaming and Suspense are new React features&lt;/a&gt; that allow us to instantly display a loading UI, then stream in UI once data fetching is done.&lt;/p&gt;

&lt;p&gt;As an example of this, create &lt;code&gt;app/loading.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// You can add any UI inside Loading, including a Skeleton.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;loading&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you try navigating between products you should see the loading UI before the details are loaded.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client Components
&lt;/h3&gt;

&lt;p&gt;All the stuff we've built so far uses &lt;a href="https://beta.nextjs.org/docs/rendering/server-and-client-components#server-components" rel="noopener noreferrer"&gt;Server Components&lt;/a&gt; which means they render on the server and don't need to ship JS to the browser.&lt;/p&gt;

&lt;p&gt;However, sometimes we need to create &lt;a href="https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components" rel="noopener noreferrer"&gt;Client Components&lt;/a&gt; that have logic on the browser. For our example, we will have a button that orders a product and a &lt;a href="https://hasura.io/docs/latest/subscriptions/postgres/streaming/index/" rel="noopener noreferrer"&gt;Hasura Streaming Subscription&lt;/a&gt; that shows us a real-time list of orders.&lt;/p&gt;

&lt;p&gt;For our real-time subscription, we'll use the &lt;a href="https://github.com/enisdenjo/graphql-ws" rel="noopener noreferrer"&gt;graphql-ws&lt;/a&gt; library. For the mutation we will continue to use &lt;code&gt;graphql-request&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We create a new client component in &lt;code&gt;app/components/orders.tsx&lt;/code&gt; using the syntax we are used to in React. The only new thing part is the &lt;code&gt;"use client";&lt;/code&gt; at the top which tells Next.js this is a client component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PlaceOrderDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../lib/gql/graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;graphql-ws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;gqlClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../lib/service/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wsClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_HASURA_GRAPHQL_WS_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setOrders&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
    &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
          subscription ProductOrders($id: Int!) {
            order_product_stream(
              batch_size: 10
              cursor: { initial_value: { created_at: "2021-02-22T18:16:12.95499+00:00" } }
              where: { product_id: { _eq: $id } }
            ) {
              id
              quantity
              created_at
            }
          }`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;setOrders&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;order_product_stream&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;gqlClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PlaceOrderDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Order
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`Order &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; created at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a new order comes from the subscription we update useState and render out the list of orders. When we click on the button, we trigger a GraphQL mutation to add a new order.&lt;/p&gt;

&lt;p&gt;Now back in &lt;code&gt;app/[id]/page.tsx&lt;/code&gt; we import the component and add it to the end of the page&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Orders&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../components/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      ...
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Orders&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we navigate to the product detail page you should now be able to order a product and see new orders pop up in real-time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Next.js is packed with new features and deep integration with cutting-edge React techniques. Combined with Hasura, we can easily make great applications for our users!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>graphql</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Hasura Actions using Netlify Functions for Custom Logic</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Wed, 16 Nov 2022 15:07:02 +0000</pubDate>
      <link>https://dev.to/hasurahq/hasura-actions-using-netlify-functions-for-custom-logic-4288</link>
      <guid>https://dev.to/hasurahq/hasura-actions-using-netlify-functions-for-custom-logic-4288</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WXo5tAT2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2022/11/hasura-actions-custom-logic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WXo5tAT2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2022/11/hasura-actions-custom-logic.png" alt="Hasura Actions using Netlify Functions for Custom Logic" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this guide, we will show how to write your own Hasura actions using Netlify functions (with your own custom business logic or a third-party API integration). But first, definitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are Hasura actions?
&lt;/h3&gt;

&lt;p&gt;Hasura provides support for integrating our own business logic or third-party APIs (usually REST APIs) with the help of “Hasura Actions”. &lt;a href="https://hasura.io/docs/latest/actions/index/"&gt;Learn more about Hasura Actions&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are Netlify functions?
&lt;/h3&gt;

&lt;p&gt;Netlify functions are AWS’s serverless lambda functions that allow you to define your business logic and functions as an API and expose them with an endpoint. &lt;a href="https://www.netlify.com/products/functions/"&gt;Click here&lt;/a&gt; to learn more about netlify functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to write your own Hasura Actions using Netlify functions?
&lt;/h3&gt;

&lt;p&gt;This guide will go through the process in three parts, feel free to skip to the part that is more interesting for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part I: Create a Function in an app&lt;/li&gt;
&lt;li&gt;Part II: Define the functions with logic&lt;/li&gt;
&lt;li&gt;Part III: Add the function to Hasura using Actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: Hasura Actions can also be used to&lt;/em&gt; &lt;a href="https://hasura.io/docs/latest/actions/rest-connectors"&gt;&lt;em&gt;add REST endpoints&lt;/em&gt;&lt;/a&gt; &lt;em&gt;to Hasura’s GraphQL schema.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Part I: Create a Function in an app&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To create a function in the app, install the &lt;a href="https://docs.netlify.com/cli/get-started"&gt;netlify-cli&lt;/a&gt; package from npm (preferably as global).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g netlify-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the netlify-cli is installed, go to the project’s root folder, and link your Netlify site to the folder. &lt;a href="https://docs.netlify.com/cli/get-started/#link-and-unlink-sites"&gt;Click here&lt;/a&gt; to learn more about linking sites in Netlify.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;netlify link
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once linked, type the following command to create a function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;netlify functions:create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a list of predefined templates. Choose the “hello-world” template.&lt;/p&gt;

&lt;p&gt;The function will be created in &lt;code&gt;&amp;lt;project-root&amp;gt;/netlify/functions/hello-world/hello-world.js&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Part II: Define the functions with logic&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is an example of a Netlify function for Hasura's Actions, which checks if an email is present in the DB without exposing all the users to the client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const axios = require("axios");

const hgeEndpoint = "https://example.hasura.app/v1/graphql";
const adminSecret = "adminSecret";

const handler = async (event) =&amp;gt; {
  let request;
  try {
    request = JSON.parse(event.body);
  } catch (error) {
    // Make sure you add code and message to errors. These will be shown in the hasura console errors.
    let response = {
      status: false,
      code: "api/parse-error",
      message: "error",
      error: { message: "cannot parse input" },
    };
    return { statusCode: 400, body: JSON.stringify(response) };
  }

  if (!request.input.email) {
    // Make sure you add code and message to errors. These will be shown in the hasura console errors.
    let response = {
      status: false,
      message: "Please send email",
      code: "input/undefined",
      error: { message: "Please send email" },
    };
    return { statusCode: 400, body: JSON.stringify(response) };
  }

  let query = {
    query: `
      query check_user($email: String) {
        users(where: {email: {_eq: $email}}) {
          id
          email_id
        }
      }
    `,
    variables: { email: request.input.email },
  };

  try {
    let result = await axios.post(hgeEndpoint, query, {
      headers: { "x-hasura-admin-secret": adminSecret },
    });
    if (result.data.users.length &amp;gt; 0) {
      let response = {
        status: true,
        message: "success",
        user: data.data.users,
      };
      return { statusCode: 200, body: JSON.stringify(response) };
    } else {
      // Make sure you add code and message to errors. These will be shown in the hasura console errors.
      let response = {
        status: false,
        code: "user/not-found",
        message: "No user found",
        error: { message: "No user found" },
      };
      return { statusCode: 400, body: JSON.stringify(response) };
    }
  } catch (error) {
    let response = {
      status: false,
      code: "api/fetch-error",
      message: error.message,
      error: { message: error.message, detail: error.toString() },
    };
    return { statusCode: 500, body: JSON.stringify(response) };
  }
};

module.exports = { handler };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE: Add the code and message to the response body.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are displayed in Hasura’s GraphQL errors (whenever the API is returning an error in Hasura, they follow the GraphQL error standard).&lt;/p&gt;

&lt;p&gt;The error in Hasura is displayed like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--79KGYetV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/ssXJTbpctJyttmAWDF0pDE2MNS2zlG1ORflEio3Dqpq8gW07sp50NQ_JqOTBjuXfkbEuX6oLTOvh1s0_TAei3p4MyoTJQiv_gkraZsrQzjVwXvwYgiucDWgZRInk1Oa6MsshQ1fswoK_6FomPpykucoEZOvJJBLxvOhi8An60I3CYdNjo2HhmEdaxE6YUg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--79KGYetV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/ssXJTbpctJyttmAWDF0pDE2MNS2zlG1ORflEio3Dqpq8gW07sp50NQ_JqOTBjuXfkbEuX6oLTOvh1s0_TAei3p4MyoTJQiv_gkraZsrQzjVwXvwYgiucDWgZRInk1Oa6MsshQ1fswoK_6FomPpykucoEZOvJJBLxvOhi8An60I3CYdNjo2HhmEdaxE6YUg" alt="Hasura Actions using Netlify Functions for Custom Logic" title="Error state in Hasura GraphQL" width="880" height="287"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Error state in Hasura GraphQL&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part III: Add the function to Hasura using Actions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the custom business logic is created as an API using Netlify functions, add it to Hasura using Actions as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login to your Hasura console and go to the “Actions” tab&lt;/li&gt;
&lt;li&gt;Click on the “Create” button&lt;/li&gt;
&lt;li&gt;Define the inputs and outputs of your API as shown below. (&lt;a href="https://hasura.io/docs/latest/actions/types/index"&gt;Learn more about the GraphQL inputs &amp;amp; outputs on Hasura&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add the endpoint of your Netlify function under the Handler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lJ3EnxRj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/c09_kYvMRI1vW06rWeGraLyH23lIsPNh1L1ohNKckY_dS5JRpADWCaZIUqR0ZG_w3MO0zmGzeLYSOF_2AZWgME7NpIV9S0NF9t1ZBJ8R98b9qncDdhAfNr3O2-JaJiQbM7pE2o64E508hLSabZTmKJiNsqlI37c2K6eRdVAWgKnfSxzXblWNA3cCOUZWzg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lJ3EnxRj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/c09_kYvMRI1vW06rWeGraLyH23lIsPNh1L1ohNKckY_dS5JRpADWCaZIUqR0ZG_w3MO0zmGzeLYSOF_2AZWgME7NpIV9S0NF9t1ZBJ8R98b9qncDdhAfNr3O2-JaJiQbM7pE2o64E508hLSabZTmKJiNsqlI37c2K6eRdVAWgKnfSxzXblWNA3cCOUZWzg" alt="Hasura Actions using Netlify Functions for Custom Logic" width="831" height="777"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Creating an Action&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can also modify the permission of the actions if you’re using role-based permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try out the Actions using Hasura’s GraphQL API Explorer
&lt;/h2&gt;

&lt;p&gt;The success state on Hasura’s API Explorer from the Netlify functions will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iJZXLHwO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/xLSQ1ElSCEmxqptt-Sk6_6oOi8K44nM7GbHW8qYsNo9vOCKAWfbnULzh99JDyjz1x0pe3h7Efa8e5u2l1ac2iySN5Rx2dJdSqVoGJbYQOYxYwWFH5dJKZ5WA3YQOJSpYwFjTiG_TKEzVm-H5ekU72mFyChVcX6MO0ErvbumUQTqTmAqrhLMnoRbN7C-nWA" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iJZXLHwO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/xLSQ1ElSCEmxqptt-Sk6_6oOi8K44nM7GbHW8qYsNo9vOCKAWfbnULzh99JDyjz1x0pe3h7Efa8e5u2l1ac2iySN5Rx2dJdSqVoGJbYQOYxYwWFH5dJKZ5WA3YQOJSpYwFjTiG_TKEzVm-H5ekU72mFyChVcX6MO0ErvbumUQTqTmAqrhLMnoRbN7C-nWA" alt="Hasura Actions using Netlify Functions for Custom Logic" width="880" height="293"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Success state of an action&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The error state on Hasura’s API Explorer from the Netlify functions will look like this:&lt;/p&gt;

&lt;p&gt;There are two error states&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the user is not found with the requested email, you’ll see the following (User not found in DB error)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4MVLoIA1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/nFGp3TPeun3YsHi7AgZ1pabqS8z9t4tTP0GJcsFSmMM0ul_-JFzBbg4Do1tkgA1Af0g_yCsJPzzaTSru7s6U-grHAxbMJykbXpesWhHkJgY2WGryBlgKMwbLkTJtWtm_pTV8jAmDvEpIV2L8O1EYkzQSBSmpB1xYA_bpt8z6GQQxynhFbFs3z1TJfD6Bmg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4MVLoIA1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/nFGp3TPeun3YsHi7AgZ1pabqS8z9t4tTP0GJcsFSmMM0ul_-JFzBbg4Do1tkgA1Af0g_yCsJPzzaTSru7s6U-grHAxbMJykbXpesWhHkJgY2WGryBlgKMwbLkTJtWtm_pTV8jAmDvEpIV2L8O1EYkzQSBSmpB1xYA_bpt8z6GQQxynhFbFs3z1TJfD6Bmg" alt="Hasura Actions using Netlify Functions for Custom Logic" width="880" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the email is not sent along with the request, you’ll see the following (Validation Error)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y8Z8Pvuk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/gYkdLQavolkRoIsC7SjgdXe0jodUmWF2PuK7mGAqQsiWPrtKV2S7930frRhZUzEkVaQYrU3LVdNGNAVNJTmLYN_cpV2gYRKo0arjShBj4ljiD1tm7ZNhCR75u0XXcYyzSKv4HvJAgYH7nQf7efpnFta40keKJggXtrP5rg4EoHHcjM6pyVM32saOqEulIA" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y8Z8Pvuk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/gYkdLQavolkRoIsC7SjgdXe0jodUmWF2PuK7mGAqQsiWPrtKV2S7930frRhZUzEkVaQYrU3LVdNGNAVNJTmLYN_cpV2gYRKo0arjShBj4ljiD1tm7ZNhCR75u0XXcYyzSKv4HvJAgYH7nQf7efpnFta40keKJggXtrP5rg4EoHHcjM6pyVM32saOqEulIA" alt="Hasura Actions using Netlify Functions for Custom Logic" width="880" height="371"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Input not given / undefined error&lt;/em&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
    </item>
    <item>
      <title>Top Serverless PostgreSQL Providers</title>
      <dc:creator>Hasura</dc:creator>
      <pubDate>Tue, 15 Nov 2022 12:26:45 +0000</pubDate>
      <link>https://dev.to/hasurahq/top-serverless-postgresql-providers-4l8e</link>
      <guid>https://dev.to/hasurahq/top-serverless-postgresql-providers-4l8e</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q3qXrMYZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2022/11/top-serverless-postgresql-providers.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q3qXrMYZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hasura.io/blog/content/images/2022/11/top-serverless-postgresql-providers.png" alt="Top Serverless PostgreSQL Providers" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Postgres (or PostgreSQL) is open-source, free, and one of the most popular relational databases. As a result, there's a lot of community tooling built on top of Postgres, or that's fully compatible with the Postgres spec, which allows for advanced features, unique use cases, and scaling.&lt;/p&gt;

&lt;p&gt;This post will show some of the top serverless Postgres database providers. The providers are in no particular order. If you know other providers that could be added to the article, feel free to mention &lt;a href="https://twitter.com/hasurahq"&gt;@HasuraHQ&lt;/a&gt; on Twitter, and we will try to add them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Neon
&lt;/h2&gt;

&lt;p&gt;Neon offers a fully-managed serverless Postgres service. Their aim is to separate storage from compute, which results in benefits such as auto-scaling, branching, and bottomless storage.&lt;/p&gt;

&lt;p&gt;That enables you to do things such as scaling CPU and I/O resources independently, running multiple compute Postgres instances without having multiple data copies, or quickly starting and shutting down your instance.&lt;/p&gt;

&lt;p&gt;Neon has both paid and free tiers. The free tier comes with the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10GB project size&lt;/li&gt;
&lt;li&gt;1 vCPU and 256MB of RAM&lt;/li&gt;
&lt;li&gt;3 Postgres databases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get started with Hasura and Neon &lt;a href="https://hasura.io/docs/latest/databases/connect-db/cloud-databases/neon/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We recently launched the native Neon Integration inside Hasura Cloud. &lt;a href="https://hasura.io/blog/introducing-a-native-postgres-integration-to-hasura-cloud-in-partnership-with-neon/"&gt;Read more&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon Aurora
&lt;/h2&gt;

&lt;p&gt;Amazon Aurora is another fully-managed serverless database service that is compatible with Postgres. Aurora claims to offer 3X more throughput than the base PostgreSQL.&lt;/p&gt;

&lt;p&gt;It comes with low latency read replicas, automated backups that enable point-in-time recovery, automatic software patching, and in-depth metrics.&lt;/p&gt;

&lt;p&gt;Amazon Aurora has both free and paid tiers, with the free tier offering the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20GB storage&lt;/li&gt;
&lt;li&gt;750 hours of compute time&lt;/li&gt;
&lt;li&gt;20GB of storage for backups and DB snapshots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get started with Hasura and Amazon Aurora &lt;a href="https://hasura.io/docs/latest/databases/connect-db/cloud-databases/aws-aurora/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crunchy
&lt;/h2&gt;

&lt;p&gt;Crunchy gives you a high-availability Postgres database with their Crunchy Postgres solution.&lt;/p&gt;

&lt;p&gt;Crunchy Postgres comes with disaster recovery, auto-scaling to tens of thousands of connections, comprehensive monitoring, high availability, and automatic failover.&lt;/p&gt;

&lt;p&gt;Get started with Hasura and CrunchyDB &lt;a href="https://hasura.io/docs/latest/databases/connect-db/cloud-databases/crunchy/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CitusDB
&lt;/h2&gt;

&lt;p&gt;Citus extends and transforms the base PostgreSQL into a distributed database. You get the power of distributed tables on top of the powerful Postgres capabilities.&lt;/p&gt;

&lt;p&gt;Citus comes with features such as parallelism, which allows you to speed up queries by 20x to 300x. You can also distribute data and queries by adding more nodes as you grow. Citus Postgres is an open-source project, which means that you can download and run it for free.&lt;/p&gt;

&lt;p&gt;Get started with Hasura and CitusDB &lt;a href="https://hasura.io/docs/latest/databases/postgres/citus-hyperscale-postgres/getting-started/cloud/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bit.io
&lt;/h2&gt;

&lt;p&gt;Bit.io claims to be the fastest way to get a serverless Postgres database. It allows you to get a database in 1 click.&lt;/p&gt;

&lt;p&gt;Bit.io Postgres offers automatic scaling to match your usage, high availability, and security features such as TLS/SSL and disk encryption.&lt;/p&gt;

&lt;p&gt;It has a paid and a free tier too. The free tier includes the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 Postgres databases with 3GB storage each and 1 billion rows queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get started with Bit.io &lt;a href="https://docs.bit.io/docs"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hasura.io/graphql/database/postgresql/"&gt;Hasura and Postgres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/docs/latest/guides/postgres/index/"&gt;PostgreSQL basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/postgresql-15-released-top-things-to-know/"&gt;Postgres 15 release: Top things to know&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/learn/database/postgresql/introduction/"&gt;Learn Postgres for free&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/top-postgresql-database-free-tier-solutions/"&gt;Top PostgreSQL database free tier solutions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/blog/top-psql-commands-and-flags-you-need-to-know-postgresql/"&gt;psql commands and flags you need to know&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
