<?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: Glenn Sonna</title>
    <description>The latest articles on DEV Community by Glenn Sonna (@theglenn).</description>
    <link>https://dev.to/theglenn</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%2Fuser%2Fprofile_image%2F76033%2F21a215d9-fc66-4846-bf7e-7db0641fd108.jpeg</url>
      <title>DEV Community: Glenn Sonna</title>
      <link>https://dev.to/theglenn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/theglenn"/>
    <language>en</language>
    <item>
      <title>Run AI Models On-Device — Zero Config, Five Minutes</title>
      <dc:creator>Glenn Sonna</dc:creator>
      <pubDate>Mon, 06 Apr 2026 13:55:40 +0000</pubDate>
      <link>https://dev.to/xybrid/run-ai-models-on-device-zero-config-five-minutes-21k1</link>
      <guid>https://dev.to/xybrid/run-ai-models-on-device-zero-config-five-minutes-21k1</guid>
      <description>&lt;p&gt;You already know why on-device AI matters. Privacy, latency, cost. You've read the guides.&lt;/p&gt;

&lt;p&gt;Now you want to actually do it. Here's what that looks like with &lt;a href="https://github.com/xybrid-ai/xybrid" rel="noopener noreferrer"&gt;Xybrid&lt;/a&gt; — no tensor shapes, no preprocessing scripts, no ML expertise.&lt;/p&gt;




&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS / Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://raw.githubusercontent.com/xybrid-ai/xybrid/master/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Windows (PowerShell)&lt;/span&gt;
irm https://raw.githubusercontent.com/xybrid-ai/xybrid/master/install.ps1 | iex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Text-to-Speech
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xybrid run &lt;span class="nt"&gt;--model&lt;/span&gt; kokoro-82m &lt;span class="nt"&gt;--input&lt;/span&gt; &lt;span class="s2"&gt;"Hello from the edge"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; hello.wav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Xybrid resolved the model from the registry, downloaded it, ran inference, and saved a WAV file. You configured nothing.&lt;/p&gt;

&lt;p&gt;Kokoro is an 82M parameter TTS model with 24 voices. First run downloads ~80MB and caches it locally. Subsequent runs are instant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speech Recognition
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xybrid run &lt;span class="nt"&gt;--model&lt;/span&gt; whisper-tiny &lt;span class="nt"&gt;--input&lt;/span&gt; recording.wav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whisper Tiny transcribes audio in real-time on any modern laptop. Outputs plain text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text Generation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xybrid run &lt;span class="nt"&gt;--model&lt;/span&gt; qwen3.5-0.8b &lt;span class="nt"&gt;--input&lt;/span&gt; &lt;span class="s2"&gt;"Explain quantum computing in one sentence"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Qwen 3.5 0.8B runs locally via llama.cpp. 201 languages, fits in 500MB quantized.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browse the Registry
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xybrid models list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;25+ models, all hosted on HuggingFace, downloaded on-demand, cached locally:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;kokoro-82m&lt;/td&gt;
&lt;td&gt;Text-to-Speech&lt;/td&gt;
&lt;td&gt;82M&lt;/td&gt;
&lt;td&gt;24 voices, high quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kitten-tts-nano-0.8&lt;/td&gt;
&lt;td&gt;Text-to-Speech&lt;/td&gt;
&lt;td&gt;15M&lt;/td&gt;
&lt;td&gt;Ultra-lightweight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3-tts-0.6b&lt;/td&gt;
&lt;td&gt;Text-to-Speech&lt;/td&gt;
&lt;td&gt;600M&lt;/td&gt;
&lt;td&gt;Multilingual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;whisper-tiny&lt;/td&gt;
&lt;td&gt;Speech Recognition&lt;/td&gt;
&lt;td&gt;39M&lt;/td&gt;
&lt;td&gt;Real-time, multilingual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wav2vec2-base-960h&lt;/td&gt;
&lt;td&gt;Speech Recognition&lt;/td&gt;
&lt;td&gt;95M&lt;/td&gt;
&lt;td&gt;CTC-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lfm2.5-350m&lt;/td&gt;
&lt;td&gt;Text Generation&lt;/td&gt;
&lt;td&gt;354M&lt;/td&gt;
&lt;td&gt;9 languages, edge-optimized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;smollm2-360m&lt;/td&gt;
&lt;td&gt;Text Generation&lt;/td&gt;
&lt;td&gt;360M&lt;/td&gt;
&lt;td&gt;Best tiny LLM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3.5-0.8b&lt;/td&gt;
&lt;td&gt;Text Generation&lt;/td&gt;
&lt;td&gt;800M&lt;/td&gt;
&lt;td&gt;201 languages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemma-4-e2b&lt;/td&gt;
&lt;td&gt;Text Generation&lt;/td&gt;
&lt;td&gt;5.1B&lt;/td&gt;
&lt;td&gt;Multimodal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mistral-7b&lt;/td&gt;
&lt;td&gt;Text Generation&lt;/td&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;Function calling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Beyond the CLI
&lt;/h2&gt;

&lt;p&gt;The CLI is the fastest way to evaluate. When you're ready to integrate into an app, Xybrid has SDKs for Flutter, Swift, Kotlin, Unity, and Rust — same models, same behavior, every platform.&lt;/p&gt;




&lt;p&gt;Xybrid is in beta (v0.1.0-beta9), open-source under Apache 2.0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/xybrid-ai/xybrid" rel="noopener noreferrer"&gt;github.com/xybrid-ai/xybrid&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Questions? Drop them in the comments — happy to help you get running.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rust</category>
      <category>machinelearning</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Handling and documenting GraphQL errors using Apollo-Prophecy</title>
      <dc:creator>Glenn Sonna</dc:creator>
      <pubDate>Tue, 17 Jul 2018 08:32:08 +0000</pubDate>
      <link>https://dev.to/theglenn/handling-and-documenting-graphql-errors-using-apollo-prophecy-5h27</link>
      <guid>https://dev.to/theglenn/handling-and-documenting-graphql-errors-using-apollo-prophecy-5h27</guid>
      <description>&lt;p&gt;&lt;em&gt;You shall fail… successfully&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Checkout-out the &lt;a href="https://hackernoon.com/handling-and-documenting-graphql-errors-using-apollo-prophecy-apollo-7a32de3547a1" rel="noopener noreferrer"&gt;Original Medium Post&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Errors are common to all computer programs; they might be hard to maintain, but properly dealing with them is without any doubt the most critical part of building applications.&lt;/p&gt;

&lt;p&gt;In the context of a Client/Server architecture we need the Server to output &lt;strong&gt;well-formatted&lt;/strong&gt; and &lt;strong&gt;easily identifiable errors&lt;/strong&gt; that the Client can &lt;strong&gt;seamlessly read, process&lt;/strong&gt; and &lt;strong&gt;handle&lt;/strong&gt; in order to &lt;strong&gt;fail successfully&lt;/strong&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72svorfes46n6bf3h2vc.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%2F72svorfes46n6bf3h2vc.jpeg" alt="**E.g. a successful failure**" width="200" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GraphQL powered APIs are no &lt;strong&gt;Exceptions&lt;/strong&gt; (pun intentional 😏) to this rule. Here is what the latest draft &lt;em&gt;(Sun, Jun 10, 2018)&lt;/em&gt; of the GraphQL specification says about how error outputs should be formatted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the operation encountered any errors, the response map must contain an entry with key &lt;code&gt;errors&lt;/code&gt;.&lt;br&gt;
Every error must contain an entry with the key &lt;code&gt;message&lt;/code&gt; with a string description of the error intended for the developer as a guide to understand and correct the error.&lt;br&gt;
GraphQL services may provide an additional entry to errors with key &lt;code&gt;extensions&lt;/code&gt;. This entry, if set, must have a map as its value. This entry is reserved for implementors to add additional information to errors however they see fit, and there are no additional restrictions on its contents.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With this in mind, a typical error object should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"errors"&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="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Only Prophets can do this"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"locations"&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="err"&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="nl"&gt;"path"&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="err"&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="nl"&gt;"extensions"&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="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NOT_A_PROPHET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Thu Jun 21 17:03:00 UTC 2018"&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Remember that we want the error output to be “&lt;strong&gt;well-formatted&lt;/strong&gt; and &lt;strong&gt;easily identifiable&lt;/strong&gt;” meaning it should contain at least one field than can be seamlessly processed by a computer.&lt;/p&gt;

&lt;p&gt;First candidate to consider is &lt;code&gt;message&lt;/code&gt;, a &lt;em&gt;“string description of the error intended for the developer[…]”.&lt;/em&gt; Since it is formatted to be read by a human, it could potentially be an expressive long string containing unwanted characters &lt;em&gt;(%, ç, à, $, €, @, white spaces, etc…)&lt;/em&gt; thus not ideal.&lt;/p&gt;

&lt;p&gt;According to the specification, &lt;code&gt;extensions&lt;/code&gt; should be the dedicated space for any additional entry to &lt;code&gt;errors&lt;/code&gt;. Here, it gives us the ability to attach a code key, providing a &lt;strong&gt;machine-readable&lt;/strong&gt; datum that can be “&lt;strong&gt;seamlessly read, processed&lt;/strong&gt; and &lt;strong&gt;handled&lt;/strong&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;if &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="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NOT_A_PROPHET&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="c1"&gt;// Do Something&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Moving forward 🏇
&lt;/h2&gt;

&lt;p&gt;We just saw that there are guidelines on how to output errors in the context of a GraphQL API. With that we should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Throw and output &lt;strong&gt;spec-compliant&lt;/strong&gt; and &lt;strong&gt;identifiable&lt;/strong&gt;errors — thanks to &lt;code&gt;extensions&lt;/code&gt; — within our resolvers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identify and handle errors client-side to &lt;strong&gt;fail successfully&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, the specification doesn’t specify guidelines for issues like APIs errors documentation, retry or failure handling, meaning that there are countless ways to properly arrange our code base for that purpose.&lt;/p&gt;

&lt;p&gt;The absence of explicit convention led me to build &lt;a href="https://github.com/theGlenn/apollo-prophecy" rel="noopener noreferrer"&gt;&lt;strong&gt;Apollo-Prophecy&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The way of the pagan
&lt;/h2&gt;

&lt;p&gt;First, let’s illustrate what maintaining errors can be like without &lt;a href="https://github.com/theGlenn/apollo-prophecy" rel="noopener noreferrer"&gt;&lt;strong&gt;Apollo-Prophecy&lt;/strong&gt;&lt;/a&gt;. To that end we’ll be using &lt;strong&gt;Apollo Server&lt;/strong&gt;, a prominent, spec-compliant, fully-featured and well-maintained GraphQL server implementation for nodeJS.&lt;/p&gt;

&lt;p&gt;Because we’re using &lt;a href="https://github.com/apollographql/apollo-server" rel="noopener noreferrer"&gt;Apollo Server&lt;/a&gt;, we can use the constructor &lt;code&gt;ApolloError(message, code)&lt;/code&gt;: errors thrown using this constructor produce a spec-compliant JSON output like the one above.&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Only Prophets can do this&lt;/span&gt;&lt;span class="dl"&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;NOT_A_PROPHET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In order to make it easier for us to store errors we could organize our server-side code the following way:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;And properly handle errors like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Done, right?&lt;/p&gt;

&lt;p&gt;No, we can do better. With this configuration, we end up doing the same work twice: since &lt;strong&gt;for every existing error entry&lt;/strong&gt; on the server we would have to write a &lt;strong&gt;corresponding key&lt;/strong&gt; client side.&lt;/p&gt;

&lt;p&gt;I don’t know about you but I prefer to say DRY.&lt;/p&gt;

&lt;h2&gt;
  
  
  To leverage API documentation 📑
&lt;/h2&gt;

&lt;p&gt;One of the most interesting propositions of GraphQL is that &lt;strong&gt;&lt;em&gt;API should be self-documenting.&lt;/em&gt;&lt;/strong&gt; While this is usually done through a mechanism named “introspection queries” — giving us detailed information about the fields and types in our schema — this doesn’t mean that we cannot add documentation material to the schema itself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  What if errors were part of the schema?
&lt;/h1&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is how we could exploit this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; We include errors in the schema:&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="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ErrorExtensions&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;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Error&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ErrorExtensions&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;errors&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="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&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;&lt;strong&gt;2.&lt;/strong&gt; We create the corresponding resolver on the Query field:&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="p"&gt;...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="na"&gt;errors&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  That’s cool but what about the client? 🤷
&lt;/h3&gt;

&lt;p&gt;Assuming that information about errors are accessible through our APIs, we need to find a way to access them from the client, keeping in mind that we want to avoid doing the same work twice.&lt;/p&gt;

&lt;p&gt;From here we can discuss two different implementations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Every time our app launches, the client could &lt;strong&gt;perform a query to fetch all errors codes and store them locally&lt;/strong&gt;. 😒 &lt;em&gt;Meh…&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle it on the dev-side by fetching and &lt;strong&gt;storing errors statically in the codebase&lt;/strong&gt; as part of the build process. 💁 &lt;em&gt;Why not?&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since correct error-handling is critical to the good functioning of your application, going with &lt;strong&gt;option 1&lt;/strong&gt; would make fetching all errors’ definitions a mandatory step of the app launch process — therefore increasing loading duration.&lt;/p&gt;

&lt;p&gt;That’s why for cleanliness and overall performance, I like the &lt;strong&gt;second option&lt;/strong&gt; better.&lt;/p&gt;
&lt;h2&gt;
  
  
  The prophet way? 🧙🏼‍
&lt;/h2&gt;

&lt;p&gt;I’ve started working on &lt;a href="https://github.com/theGlenn/apollo-prophecy" rel="noopener noreferrer"&gt;Apollo Prophecy&lt;/a&gt;: a code generation Command Line interface that does what we need (and a tiny bit more!). It will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Generate errors that we can throw in our resolvers and expose through the schema as documentation — &lt;code&gt;apollo-prophecy generate&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Query the server schema and generate file with methods and helpers to gracefully consume errors — &lt;code&gt;apollo-prophecy ask&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The goal is to always keep you server and client errors repository in sync.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, install it through your favorite package manager.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="o"&gt;[&lt;/span&gt;npm | yarn] &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; apollo-prophecy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  To generate errors like a greek God 🔮
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;generate&lt;/code&gt; command will create a file containing throwable error classes. It takes as input a JSON file formatted like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;It can be run like below (if nothing is specified it will look for an &lt;strong&gt;errors.json&lt;/strong&gt; file inside the running folder):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    apollo-prophecy generate errorsDef.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Using the above &lt;strong&gt;errosDef.json&lt;/strong&gt; the CLI will generate the following file.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Here are the generated file key components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;errorsList&lt;/code&gt; — plain JSON array meant to be used as documentation output. It contains all error representations with their static data: &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;extensions -&amp;gt; code&lt;/code&gt;. &lt;em&gt;Always generated but empty if there is no error to generate.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;errorType&lt;/code&gt; — GraphQL object type that we can include in our &lt;strong&gt;schema definition&lt;/strong&gt;. It should be used alongside &lt;code&gt;errorsList&lt;/code&gt; for documentation. &lt;em&gt;Always generated as is&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;PropheticError&lt;/code&gt; — class extending ApolloError meant to be &lt;strong&gt;inherited&lt;/strong&gt; by other errors in this file. &lt;em&gt;Always generated as is&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;NotAProphetError&lt;/code&gt; &lt;code&gt;ProphetNotFoundWithId&lt;/code&gt; — those are the two custom error classes generated with the information of the JSON file input.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can use all these elements in our server. Given that we need errors to be part of our schema, we can do the following:&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;errorsList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NotAProphetError&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="s1"&gt;./gen/GeneratedErrors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;errors&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="nx"&gt;errorsList&lt;/span&gt;
      &lt;span class="nx"&gt;getAllUsers&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NotAProphetError&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;h3&gt;
  
  
  Hmm ok… Does that make us prophets now? 🤔
&lt;/h3&gt;

&lt;p&gt;Not yet; prophets need to communicate with gods in order to anticipate the future, don’t they? Using &lt;a href="https://github.com/theGlenn/apollo-prophecy" rel="noopener noreferrer"&gt;Apollo-Prophecy&lt;/a&gt;, we can do something similar with the command &lt;code&gt;ask&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    apollo-prophecy ask &lt;span class="o"&gt;[&lt;/span&gt;http://localhost:3000/graphql]&lt;span class="o"&gt;(&lt;/span&gt;http://localhost:3000/graphql&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will send a request to the specified endpoint and try to perform a GraphQL query on the &lt;code&gt;--field&lt;/code&gt; option to try and fetch errors’ information (if nothing is specified, an &lt;em&gt;“errors” field&lt;/em&gt; will be queried by default).&lt;/p&gt;

&lt;p&gt;Below is an extremely simplified version of the generated file. If you want to have an idea of what it really looks like go and &lt;a href="https://github.com/theGlenn/apollo-prophecy" rel="noopener noreferrer"&gt;try it yourself&lt;/a&gt;!&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;PropheticErrorCode&lt;/code&gt; —an enum with the codes of all errors exposed in the schema.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;errorHere&lt;/code&gt; and &lt;code&gt;isThis&lt;/code&gt; are the real two helper methods that enable us to handle client-side errors in a clean and reusable way.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  - errorHere(error)
&lt;/h3&gt;

&lt;p&gt;When called, it returns an object that has &lt;strong&gt;one property named after each errors&lt;/strong&gt; found on the server. Depending on the supplied argument, the called property returns either &lt;strong&gt;true or false&lt;/strong&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;errorHere&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;`./_generated/Errors.ts`&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;errorHere&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="nx"&gt;isNotAProphetError&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="c1"&gt;// Do something&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;errorHere&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="nx"&gt;isProphetNotFoundWithId&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="c1"&gt;// Do something else&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;h3&gt;
  
  
  - isThis(error)
&lt;/h3&gt;

&lt;p&gt;When called, it returns an object that has &lt;strong&gt;one handler function named after each errors&lt;/strong&gt; found on the server.&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;isThis&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;`./_generated/Errors.ts`&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="nf"&gt;isThis&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="nc"&gt;UserNotFoundError&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="nc"&gt;NotAProphetError&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="nf"&gt;handle&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;Handlers return the same instance object than &lt;code&gt;isThis&lt;/code&gt;, so that each function call can be chained. Once the &lt;code&gt;handle&lt;/code&gt; method is called, it initiates the check and calls the corresponding handler if there is a match.&lt;/p&gt;

&lt;p&gt;And… voilà! Thanks to the &lt;code&gt;ask&lt;/code&gt; command we can keep our client-side error repository in sync with the API through the schema. By using &lt;code&gt;errorHere&lt;/code&gt; and &lt;code&gt;isThis&lt;/code&gt; we now have a clean and expressive way of handling errors — and look, the code is pretty too!&lt;/p&gt;

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

&lt;p&gt;Just like any young technology, &lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt; still has gaps to fill. &lt;a href="https://github.com/theGlenn/apollo-prophecy" rel="noopener noreferrer"&gt;Apollo-Prophecy&lt;/a&gt; is built to fill just one of these gaps: &lt;em&gt;how we implement error handling and documentation&lt;/em&gt;. But this isn’t the end of the conversation; &lt;a href="https://github.com/theGlenn/apollo-prophecy" rel="noopener noreferrer"&gt;Apollo-Prophecy&lt;/a&gt; is open-source, and I’m sure together we can come up with even better ways to improve it.&lt;/p&gt;

&lt;p&gt;Already there is a lot of work and fixes to be done on &lt;a href="https://github.com/theGlenn/apollo-prophecy" rel="noopener noreferrer"&gt;&lt;strong&gt;Apollo-Prophecy&lt;/strong&gt;&lt;/a&gt;; contributions and suggestions are both welcomed and needed. Please visit &lt;a href="https://github.com/theGlenn/apollo-prophecy/issues/new" rel="noopener noreferrer"&gt;Github&lt;/a&gt; and take look at existing &lt;a href="https://github.com/theGlenn/apollo-prophecy/issues" rel="noopener noreferrer"&gt;issues&lt;/a&gt; or even &lt;a href="https://github.com/theGlenn/apollo-prophecy/issues/new" rel="noopener noreferrer"&gt;create new one&lt;/a&gt;s.&lt;/p&gt;

&lt;p&gt;If you’ve come this far, thank you for reading ❤️ I really hope you enjoyed this post and I’d love to hear your thoughts and feedback 🙂.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>errors</category>
      <category>javascript</category>
      <category>node</category>
    </item>
  </channel>
</rss>
