warpgrapher 0.10.0

Automate web service creation with GraphQL and Graph Databases
Documentation
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Actix Web - Warpgrapher Book (0.10.0)</title>


        <!-- Custom HTML head -->
        
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../favicon.svg">
        <link rel="shortcut icon" href="../favicon.png">
        <link rel="stylesheet" href="../css/variables.css">
        <link rel="stylesheet" href="../css/general.css">
        <link rel="stylesheet" href="../css/chrome.css">
        <link rel="stylesheet" href="../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../highlight.css">
        <link rel="stylesheet" href="../tomorrow-night.css">
        <link rel="stylesheet" href="../ayu-highlight.css">

        <!-- Custom theme stylesheets -->

    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded "><a href="../warpgrapher/intro.html"><strong aria-hidden="true">1.</strong> Warpgrapher</a></li><li class="chapter-item expanded "><a href="../warpgrapher/quickstart.html"><strong aria-hidden="true">2.</strong> Quickstart</a></li><li class="chapter-item expanded "><a href="../integrations/intro.html"><strong aria-hidden="true">3.</strong> Server Integration</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../integrations/actix.html" class="active"><strong aria-hidden="true">3.1.</strong> Actix Web</a></li><li class="chapter-item expanded "><a href="../integrations/lambda.html"><strong aria-hidden="true">3.2.</strong> AWS Lambda</a></li></ol></li><li class="chapter-item expanded "><a href="../configuration/intro.html"><strong aria-hidden="true">4.</strong> Configuration</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../configuration/databases.html"><strong aria-hidden="true">4.1.</strong> Databases</a></li><li class="chapter-item expanded "><a href="../configuration/formats.html"><strong aria-hidden="true">4.2.</strong> Formats</a></li><li class="chapter-item expanded "><a href="../configuration/types.html"><strong aria-hidden="true">4.3.</strong> Types</a></li><li class="chapter-item expanded "><a href="../configuration/relationships.html"><strong aria-hidden="true">4.4.</strong> Relationships</a></li></ol></li><li class="chapter-item expanded "><a href="../api/intro.html"><strong aria-hidden="true">5.</strong> CRUD API Usage Examples</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../api/node_create.html"><strong aria-hidden="true">5.1.</strong> Node Create</a></li><li class="chapter-item expanded "><a href="../api/node_read.html"><strong aria-hidden="true">5.2.</strong> Node Read</a></li><li class="chapter-item expanded "><a href="../api/node_update.html"><strong aria-hidden="true">5.3.</strong> Node Update</a></li><li class="chapter-item expanded "><a href="../api/node_delete.html"><strong aria-hidden="true">5.4.</strong> Node Delete</a></li><li class="chapter-item expanded "><a href="../api/rel_create.html"><strong aria-hidden="true">5.5.</strong> Relationship Create</a></li><li class="chapter-item expanded "><a href="../api/rel_read.html"><strong aria-hidden="true">5.6.</strong> Relationship Read</a></li><li class="chapter-item expanded "><a href="../api/rel_update.html"><strong aria-hidden="true">5.7.</strong> Relationship Update</a></li><li class="chapter-item expanded "><a href="../api/rel_delete.html"><strong aria-hidden="true">5.8.</strong> Relationship Delete</a></li></ol></li><li class="chapter-item expanded "><a href="../engine/intro.html"><strong aria-hidden="true">6.</strong> Engine Features</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../engine/endpoints_static.html"><strong aria-hidden="true">6.1.</strong> Static Endpoints</a></li><li class="chapter-item expanded "><a href="../engine/endpoints_defined.html"><strong aria-hidden="true">6.2.</strong> Defined Endpoints</a></li><li class="chapter-item expanded "><a href="../engine/dynamic_props.html"><strong aria-hidden="true">6.3.</strong> Dynamic Props</a></li><li class="chapter-item expanded "><a href="../engine/dynamic_rels.html"><strong aria-hidden="true">6.4.</strong> Dynamic Relationships</a></li><li class="chapter-item expanded "><a href="../engine/context_request.html"><strong aria-hidden="true">6.5.</strong> Request Context</a></li><li class="chapter-item expanded "><a href="../engine/input_validation.html"><strong aria-hidden="true">6.6.</strong> Input Validation</a></li><li class="chapter-item expanded "><a href="../engine/event_handlers.html"><strong aria-hidden="true">6.7.</strong> Event Handlers</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Warpgrapher Book (0.10.0)</h1>

                    <div class="right-buttons">
                        <a href="../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="actix-web-integration"><a class="header" href="#actix-web-integration">Actix Web Integration</a></h1>
<p>A full example of integrating Warpgrapher with Actix Web is contained in the <a href="https://github.com/warpforge/warpgrapher-actixweb">warpgrapher-actixweb</a> respository on Github. A slightly simplified version of that project is reproduced with additional description below.</p>
<p>To integrate Warpgrapher with an Actix Web engine, include the following dependencies in the <code>Cargo.toml</code> file.</p>
<p><code>Cargo.toml</code></p>
<pre><code>[dependencies]
actix-http = &quot;3.0.0-beta.5&quot;
actix-web = &quot;4.0.0-beta.6&quot;
actix-cors = &quot;0.6.0-beta.2&quot;
serde = &quot;1.0.135&quot;
serde_json = &quot;1.0.78&quot;
warpgrapher = { version=&quot;0.10.0&quot;, features=[&quot;cypher&quot;]}
</code></pre>
<p>The rest of the code necessary to accomplish the integration is contained within the single source code file below. First, a number of structs and functions are imported from the Actix and Warpgrapher crates.</p>
<p><code>src/main.rs</code></p>
<pre><code>use actix_cors::Cors;
use actix_http::error::Error;
use actix_web::middleware::Logger;
use actix_web::web::{Data, Json};
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fs::File;

use warpgrapher::engine::config::Configuration;
use warpgrapher::engine::context::RequestContext;
use warpgrapher::engine::database::cypher::CypherEndpoint;
use warpgrapher::engine::database::DatabaseEndpoint;
use warpgrapher::juniper::http::playground::playground_source;
use warpgrapher::Engine;
</code></pre>
<p>The <code>AppData</code> struct, defined below, is used to pass application data created during setup into the web server. In the case of this integration, the application data that is passed into the web server is the Warpgrapher <code>Engine</code>.</p>
<pre><code>#[derive(Clone)]
struct AppData {
    engine: Engine&lt;Rctx&gt;,
}

impl AppData {
    fn new(engine: Engine&lt;Rctx&gt;) -&gt; AppData {
        AppData { engine }
    }
}
</code></pre>
<p>Just like the <a href="../warpgrapher/quickstart.html">Quickstart</a> tutorial, this integration creates a <code>RequestContext</code> that could be used to pass data into the Warpgrapher engine for custom resolvers or endpoints, but is left empty in this example. The <code>Rctx</code> struct does contain on associated type, which selects Cypher as the database type to be used for this Warpgrapher engine.</p>
<pre><code>#[derive(Clone, Debug)]
struct Rctx {}

impl RequestContext for Rctx {
    type DBEndpointType = CypherEndpoint;

    fn new() -&gt; Self {
        Rctx {}
    }
}
</code></pre>
<p>Next, the integration includes a <code>GraphqlRequest</code> that is used to deserialize queries coming from Actix Web and pass the query content to the Warpgrapher engine.</p>
<pre><code>#[derive(Clone, Debug, Deserialize)]
struct GraphqlRequest {
    pub query: String,
    pub variables: Option&lt;Value&gt;,
}
</code></pre>
<p>The following function is the handler that takes requests from the Actix Web framework and passes it into the Warpgrapher engine. In short, it pulls the query and query variables from the Actix Web query and passes those as arguments to the Warpgrapher engine's <code>execute</code> function. A successful response is passed back as an <code>Ok</code> result. Errors are returned within an InternalServerError.</p>
<pre><code>async fn graphql(data: Data&lt;AppData&gt;, req: Json&lt;GraphqlRequest&gt;) -&gt; Result&lt;HttpResponse, Error&gt; {
    let engine = &amp;data.engine;
    let metadata: HashMap&lt;String, String&gt; = HashMap::new();
    let resp = engine
        .execute(req.query.to_string(), req.variables.clone(), metadata)
        .await;
    match resp {
        Ok(body) =&gt; Ok(HttpResponse::Ok()
            .content_type(&quot;application/json&quot;)
            .body(body.to_string())),
        Err(e) =&gt; Ok(HttpResponse::InternalServerError()
            .content_type(&quot;application/json&quot;)
            .body(e.to_string())),
    }
}
</code></pre>
<p>To make it easier to explore the schema generated by Warpgrapher, the integration example also includes a handler function that returns a GraphQL playground, as the <code>/playground</code> path. The handler function is shown below.</p>
<pre><code>async fn playground(_data: Data&lt;AppData&gt;) -&gt; impl Responder {
    let html = playground_source(&quot;/graphql&quot;, None);
    HttpResponse::Ok()
        .content_type(&quot;text/html; charset=utf-8&quot;)
        .body(html)
}
</code></pre>
<p>The <code>create_engine</code> function pulls data from environment variables to determine how to connect to a Cypher-based database. These are the same environment variables described in the <a href="../warpgrapher/quickstart.html">Quickstart</a> and the Neo4J section of the <a href="../configuration/databases.html">Databases</a> book.</p>
<pre><code>async fn create_engine(config: Configuration) -&gt; Engine&lt;Rctx&gt; {
    let db = CypherEndpoint::from_env()
        .expect(&quot;Failed to parse endpoint from environment&quot;)
        .pool()
        .await
        .expect(&quot;Failed to create db endpoint&quot;);
    let engine: Engine&lt;Rctx&gt; = Engine::&lt;Rctx&gt;::new(config, db)
        .build()
        .expect(&quot;Failed to build engine&quot;);
    engine
}
</code></pre>
<p>Lastly, the main function itself pulls all of the above elements together. It reads a configuration from a <code>./config.yaml</code> file and passes that to the function defined above to create an Warpgrapher <code>Engine</code>. It packages the Warpgrapher engine into an <code>AppData</code> struct to pass off to Actix Web and creates an <code>HttpServer</code> to begin fielding requests. The GraphQL API is bound to the <code>/graphql</code> path, and the playground is bound to the <code>/playground</code> path.</p>
<pre><code>#[actix_web::main]
async fn main() -&gt; std::io::Result&lt;()&gt; {
    let config_file = File::open(&quot;./config.yaml&quot;.to_string()).expect(&quot;Could not read file&quot;);
    let config = Configuration::try_from(config_file).expect(&quot;Failed to parse config file&quot;);

    let engine = create_engine(config.clone()).await;

    let graphql_endpoint = &quot;/graphql&quot;;
    let playground_endpoint = &quot;/playground&quot;;
    let bind_addr = &quot;0.0.0.0&quot;.to_string();
    let bind_port = &quot;5000&quot;.to_string();
    let addr = format!(&quot;{}:{}&quot;, bind_addr, bind_port);

    let app_data = AppData::new(engine);

    println!(&quot;Starting server on {}&quot;, addr);
    HttpServer::new(move || {
        App::new()
            .app_data(actix_web::web::Data::new(app_data.clone()))
            .wrap(Logger::default())
            .wrap(Cors::permissive())
            .route(graphql_endpoint, web::post().to(graphql))
            .route(playground_endpoint, web::get().to(playground))
    })
    .bind(&amp;addr)
    .expect(&quot;Failed to start server&quot;)
    .run()
    .await
}
</code></pre>
<p>To view or clone a full repository project with an Actix Web integration, visit the <a href="https://github.com/warpforge/warpgrapher-actixweb">warpgrapher-actixweb</a> repository on GitHub.</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../integrations/intro.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next" href="../integrations/lambda.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../integrations/intro.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next" href="../integrations/lambda.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>




        <script type="text/javascript">
            window.playground_copyable = true;
        </script>


        <script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../searcher.js" type="text/javascript" charset="utf-8"></script>

        <script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->


    </body>
</html>