<?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: Faruq</title>
    <description>The latest articles on DEV Community by Faruq (@faruq2).</description>
    <link>https://dev.to/faruq2</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%2F242347%2Fc253c4a9-bb3d-4684-8d83-210d7ed700e4.jpeg</url>
      <title>DEV Community: Faruq</title>
      <link>https://dev.to/faruq2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faruq2"/>
    <language>en</language>
    <item>
      <title>How to build a CRUD REST API with Go, Gin and Fauna</title>
      <dc:creator>Faruq</dc:creator>
      <pubDate>Mon, 07 Dec 2020 13:50:26 +0000</pubDate>
      <link>https://dev.to/faruq2/how-to-build-a-crud-rest-api-with-go-gin-and-fauna-37o6</link>
      <guid>https://dev.to/faruq2/how-to-build-a-crud-rest-api-with-go-gin-and-fauna-37o6</guid>
      <description>&lt;p&gt;APIs act as software intermediaries allowing different applications to communicate with one another. They are popularly used for facilitating communication between the client side of an application and its server side. In this article, you will learn how to build a REST API for a reading list. With this API, a client will be able to add reading items to a reading list, update items, delete items and of course get items from the list and it will be built using the Go programming language, Gin and Fauna. Before we start looking into some code, let's take a look at the components of our tech stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go&lt;/strong&gt; is an open source programming language built by Google for handling some of their internal work and over the years, it has grown to become a popular server-side language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gin&lt;/strong&gt; is a Go web framework that make it easier to write web apps in Go and greatly reduces the verbosity of your code. Though a large percentage of the Go community would prefer to write Go web apps without a framework, I have found Gin to be very helpful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fauna.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Fauna&lt;/strong&gt;&lt;/a&gt;&lt;a href="https://fauna.com/" rel="noopener noreferrer"&gt;,&lt;/a&gt; which is the database that our little project will be using, is a powerful relational NoSQL database that can also serve as a data API when building serverless applications. Fauna supports the use of GraphQL for manipulating and querying the data in your database and its own native query language, FQL(Fauna Query Language).&lt;/p&gt;

&lt;p&gt;FQL is a powerful querying language which allows you to perform conditional querying using &lt;strong&gt;if&lt;/strong&gt;-like functions, run transactions, perform a union(returning a combination) of data returned from different indexes and so on. You can learn more about FQL &lt;a href="https://docs.fauna.com/fauna/current/api/fql/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will make use of FQL to interact with our Fauna database throughout our project.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;A basic knowledge of building backend applications with Go is required to follow along with this article.&lt;/li&gt;
&lt;li&gt;A basic knowledge of Fauna and FQL. You can learn the basics &lt;a href="https://fauna.com/blog/getting-started-with-fql-faunadbs-native-query-language-part-1" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Installing necessary dependencies
&lt;/h1&gt;

&lt;p&gt;First of all, we need to create a new project. You can create one right away in your GOPATH or &lt;a href="https://golang.org/doc/tutorial/create-module" rel="noopener noreferrer"&gt;use Go Modules&lt;/a&gt; to create the project outside your GOPATH. Once that is done, you need to install the following dependencies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gin&lt;/li&gt;
&lt;li&gt;Fauna Go driver&lt;/li&gt;
&lt;li&gt;godotenv (for storing secrets in a .env file)&lt;/li&gt;
&lt;li&gt;Go playground's validator package for validating the fields of our models before storing them in the database
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get -u github.com/gin-gonic/gin
go get github.com/fauna/faundb-go/v3/faunadb 
go get github.com/joho/godotenv
go get github.com/go-playground/validator/v102

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

&lt;/div&gt;



&lt;p&gt;Now that you have your dependencies installed, we can take a look at the project folder structure and architecture&lt;/p&gt;

&lt;h1&gt;
  
  
  Project structure and architecture
&lt;/h1&gt;

&lt;p&gt;After installing your dependencies, you should create the following folders and files:&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%2Fpaper-attachments.dropbox.com%2Fs_9EC93381FA80D86F7E9A7080D67616DE4F3D776CDD4111F6F703FF1002949985_1605655751836_crud-fauna-1.JPG" 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%2Fpaper-attachments.dropbox.com%2Fs_9EC93381FA80D86F7E9A7080D67616DE4F3D776CDD4111F6F703FF1002949985_1605655751836_crud-fauna-1.JPG" alt="Project folder structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the image above, we have 4 packages: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Controllers - this is where we write request handlers to handle the HTTP requests to our API&lt;/li&gt;
&lt;li&gt;Customerrors - this is where we write our custom &lt;a href="https://golang.org/pkg/builtin/#error" rel="noopener noreferrer"&gt;error&lt;/a&gt; for representing app errors&lt;/li&gt;
&lt;li&gt;Database - here, we create a &lt;strong&gt;FaunaDB&lt;/strong&gt; interface to wrap the basic queries we will be creating in simple methods&lt;/li&gt;
&lt;li&gt;Models - this is where we define the structs for the entities(for example, a reading item) in our app&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As I said earlier, we will create a &lt;strong&gt;Fauna&lt;/strong&gt; interface whose methods are a wrapper around basic queries that I believe are necessary for any CRUD app interacting with Fauna. We will also create a &lt;strong&gt;faunaDB&lt;/strong&gt; struct that will implement this interface. This &lt;strong&gt;faunaDB&lt;/strong&gt; struct will depend on a &lt;strong&gt;FaunaClient&lt;/strong&gt;(which is available in FaunaDB Go driver) and a database secret(which will be discussed soon). &lt;/p&gt;

&lt;p&gt;For the controllers package, we will have a &lt;strong&gt;controller&lt;/strong&gt; struct whose methods will act as request handlers. The &lt;strong&gt;controller&lt;/strong&gt; struct will depend on a Fauna interface and as such, will have that as a struct field. We will then pass the &lt;strong&gt;controller&lt;/strong&gt; struct's methods to Gin for handling the requests.&lt;/p&gt;

&lt;p&gt;All of this will be clearer and make more sense as you progress in this article. A Github repository link will also be provided at the end of this article to make the code easier to understand.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting up Fauna
&lt;/h1&gt;

&lt;p&gt;Before we actually begin writing code, we need to set up our Fauna database and get a key secret which will allow us to access our database and perform operations on it from our code.&lt;/p&gt;

&lt;p&gt;Create an account on Fauna if you do not have one and create a database(you can learn how to do that &lt;a href="https://docs.fauna.com/fauna/current/start/cloud" rel="noopener noreferrer"&gt;here&lt;/a&gt;). Once you have created a database, you should be redirected to your database’s dashboard which should look like so:&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%2Fpaper-attachments.dropbox.com%2Fs_9EC93381FA80D86F7E9A7080D67616DE4F3D776CDD4111F6F703FF1002949985_1605734243732_crud-fauna-3.JPG" 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%2Fpaper-attachments.dropbox.com%2Fs_9EC93381FA80D86F7E9A7080D67616DE4F3D776CDD4111F6F703FF1002949985_1605734243732_crud-fauna-3.JPG" alt="Database dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we want to create an access key and obtain the corresponding secret which we can use the access our database from our code. To do that, open the &lt;strong&gt;Security&lt;/strong&gt; tab on the left and click the "New Key" button. Select the database you just created, change the role to &lt;strong&gt;Server&lt;/strong&gt; (because we will be using this access key from the server) and type in a name for the key if you prefer.&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%2Fpaper-attachments.dropbox.com%2Fs_9EC93381FA80D86F7E9A7080D67616DE4F3D776CDD4111F6F703FF1002949985_1605735332474_crud-fauna-5.JPG" 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%2Fpaper-attachments.dropbox.com%2Fs_9EC93381FA80D86F7E9A7080D67616DE4F3D776CDD4111F6F703FF1002949985_1605735332474_crud-fauna-5.JPG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have done that, click &lt;strong&gt;Save&lt;/strong&gt; and copy the key secret displayed. You can only view this secret once so you cannot copy it later. Also, because our access key has the &lt;strong&gt;server&lt;/strong&gt; role, this means anybody with the key has unrestricted access to our database and can cause serious damage if they happen to be a bad actor so we need to make sure we store our secret in a safe and secure place(environment variables for example). &lt;/p&gt;

&lt;p&gt;For the sake of this tutorial, we will use a .env file (which you should have created by now) to store our secret. You should store it like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FAUNA_DB_SERVER_SECRET=your_secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  Writing application code
&lt;/h1&gt;

&lt;p&gt;With that done, we can now start writing actual code.&lt;/p&gt;

&lt;p&gt;Open the &lt;em&gt;database/database.go&lt;/em&gt; file and create two constant variables called &lt;strong&gt;ReadingItemCollection&lt;/strong&gt; and &lt;strong&gt;ReadingItemByTypeIndex&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const (
   ReadingItemCollection = "reading_items"
   ReadingItemByTypeIndex = "reading_items_by_type"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;ReadingItemCollection&lt;/strong&gt; variable refers to the name of the collection where all the reading item documents will be stored and  the &lt;strong&gt;ReadingItemByTypeIndex&lt;/strong&gt; variable refers to the name of the index that we will use to filter the reading items by their type.&lt;/p&gt;

&lt;p&gt;Now, as I said earlier, we are going to create a &lt;strong&gt;Fauna&lt;/strong&gt; interface which will later be used by our controller and a &lt;strong&gt;faunaDB&lt;/strong&gt; struct to implement the interface:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package database

import (
   "fmt"
   f "github.com/fauna/faunadb-go/v3/faunadb"
   "github.com/gozaddy/crud-api-fauna/models"
   "strconv"
)

...

type faunaDB struct {
   client *f.FaunaClient
   secret string
}

type FaunaDB interface {
   Init() error
   NewID() (string, error)
   FaunaClient() *f.FaunaClient
   GetDocument(collection string, documentID string) (f.Value, error)
   AddDocument(collection string, object models.Model) (f.Value, error)
   DeleteDocument(collection string, documentID string) error
   UpdateDocument(collection string, documentID string, update interface{}) error
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;Init&lt;/strong&gt; method initializes our Fauna interface by creating a new Fauna client using the secret provided and assigning that client to the client field of the &lt;strong&gt;faunaDB&lt;/strong&gt; struct. In the init method, we also check if the reading_items collection exists and then create the collection and the reading_items_by_type index if it does not. To write a query, we use the &lt;strong&gt;Query&lt;/strong&gt; method of the FaunaDB client. &lt;/p&gt;

&lt;p&gt;Here's the code implementation for the Init method:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; func (fdb *faunaDB) Init() error{
   fdb.client = f.NewFaunaClient(fdb.secret)
   res, err := fdb.client.Query(
      f.If(
         f.Exists(f.Collection(ReadingItemCollection)),
         "Exists!",
         f.Do(
            f.CreateCollection(
               f.Obj{
                  "name": "reading_items",
               },
            ),
            f.CreateIndex(
               f.Obj{
                  "name": ReadingItemByTypeIndex,
                  "source": f.Collection(ReadingItemCollection),
                  "terms": f.Arr{f.Obj{"field": f.Arr{"data", "type"}}},
               },
            ),
         ),
      ),
   )
   if err != nil{
      return err
   }
   fmt.Println(res)
   return nil
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you can see, in the first line, we created a new FaunaDB client and assigned it to the client field of &lt;strong&gt;faunaDB&lt;/strong&gt;. Then, in the second line we ran an FQL query that uses the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/if?lang=go" rel="noopener noreferrer"&gt;&lt;strong&gt;If&lt;/strong&gt;&lt;/a&gt; FQL function to check if the reading_items collection exists and returns "Exists!" if it does or run a transaction (with the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/do?lang=go" rel="noopener noreferrer"&gt;&lt;strong&gt;Do&lt;/strong&gt;&lt;/a&gt; function) consisting of two functions(one to create the collection and another to create the filter by type index) if it does not.&lt;/p&gt;

&lt;p&gt;To create the index, we used the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/createindex?lang=go" rel="noopener noreferrer"&gt;CreateIndex&lt;/a&gt; function. As you may already know, the &lt;strong&gt;terms&lt;/strong&gt; property of an index allows us to filter results from a collection based on the value of a particular field of its documents(the &lt;strong&gt;type&lt;/strong&gt;  property of the &lt;strong&gt;data&lt;/strong&gt; property of the document in this case).&lt;/p&gt;

&lt;p&gt;By the way, the &lt;strong&gt;Obj&lt;/strong&gt; type is simply a wrapper around &lt;code&gt;map[string]interface{}&lt;/code&gt; and the &lt;strong&gt;Arr&lt;/strong&gt; type is simply a wrapper around &lt;code&gt;[]interface{}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After running the query, we then check for errors and handle them appropriately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating new document IDs
&lt;/h2&gt;

&lt;p&gt;The next method is the &lt;strong&gt;NewID&lt;/strong&gt; method which we will be using to generate new IDs for every new document that we create. This method will make use of the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/newid?lang=go" rel="noopener noreferrer"&gt;NewId&lt;/a&gt; FQL function. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; func (fdb *faunaDB) NewID() (string, error) {
   res, err := fdb.client.Query(f.NewId())
   if err != nil{
      return "", err
   }
   var id string
   if err = res.Get(&amp;amp;id); err != nil{
      return "", err
   }
   return id, nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After running the query and getting a response, we used the &lt;strong&gt;Get&lt;/strong&gt; method of the response to decode the response into a native Go string which we returned from the function. &lt;/p&gt;

&lt;p&gt;The next method is the &lt;strong&gt;FaunaClient&lt;/strong&gt; method which is a very simple method that returns the FaunaDB client so that we can perform other queries that are not among the FaunaDB interface's methods.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (fdb *faunaDB) FaunaClient() *f.FaunaClient {
   return fdb.client
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Getting a Document
&lt;/h2&gt;

&lt;p&gt;In FQL, we make use of the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/get?lang=go" rel="noopener noreferrer"&gt;&lt;strong&gt;Get&lt;/strong&gt;&lt;/a&gt; function to retrieve a particular document from a FaunaDB collection. The &lt;strong&gt;ref&lt;/strong&gt; of a document contains both the name of the collection the document belongs to and the document’s unique ID so to get a document, we need its collection name and document ID. Our &lt;strong&gt;GetDocument&lt;/strong&gt; method will therefore take those two values as arguments.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (fdb *faunaDB) GetDocument(collection string, documentID string) (f.Value, error) {
   return fdb.client.Query(f.Get(f.RefCollection(f.Collection(collection), documentID)))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The &lt;strong&gt;RefCollection&lt;/strong&gt; method returns a ref based on a collection name and document ID.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a document to a collection
&lt;/h2&gt;

&lt;p&gt;if you notice, from the Fauna interface, the &lt;strong&gt;AddDocument&lt;/strong&gt; method takes a collection name and a type of &lt;strong&gt;Model&lt;/strong&gt;. But what is Model?&lt;br&gt;
In our app’s code, Model is an interface which all models (types that will be stored in the database) must implement.&lt;/p&gt;

&lt;p&gt;Head over to &lt;em&gt;models/models.go&lt;/em&gt; and add this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package models

type Model interface{
   UniqueID() string
   Validate() error
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Model interface ensures that each entity being saved in the database has an ID by implementing the &lt;strong&gt;UniqueID&lt;/strong&gt; method which should return the ID of the entity, and has a &lt;strong&gt;Validate&lt;/strong&gt; method which can be used by methods and functions in other packages to validate the entity’s fields before adding it to the database.&lt;/p&gt;

&lt;p&gt;To add a document to a collection, we make use of the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/create?lang=go" rel="noopener noreferrer"&gt;Create&lt;/a&gt; FQL function. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (fdb *faunaDB) AddDocument(collection string, object models.Model) (f.Value, error) {
   if err := object.Validate(); err != nil{
      return nil, err
   }

   return fdb.client.Query(f.Create(f.RefCollection(f.Collection(collection), object.UniqueID()), f.Obj{"data": object}))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Deleting a document
&lt;/h2&gt;

&lt;p&gt;To delete a document, we use the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/delete?lang=go" rel="noopener noreferrer"&gt;Delete&lt;/a&gt; function and pass the ref of the document as an argument.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (fdb *faunaDB) DeleteDocument(collection string, documentID string) error {
   _, err := fdb.client.Query(f.Delete(f.RefCollection(f.Collection(collection), documentID)))
   return err
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Updating a document
&lt;/h2&gt;

&lt;p&gt;Now we have the &lt;strong&gt;UpdateDocument&lt;/strong&gt; method. This is used to update a document given its collection name, document ID and an empty interface type which contains the new data we are updating the document with. This method will make use of the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/update?lang=go" rel="noopener noreferrer"&gt;Update&lt;/a&gt; FQL function.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (fdb *faunaDB) UpdateDocument(collection string, documentID string, update interface{}) error {
   _, err := fdb.client.Query(
      f.Update(
         f.RefCollection(f.Collection(collection), documentID),
         f.Obj{"data": update},
      ),
   )
   return err
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that we have implemented the FaunaDB interface, let's write the model for a reading item. Models represent entities that will be used throughout our application. In this case, ReadingItem which represents a single reading item is our only model.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;models/models.go&lt;/em&gt;, import the validator package and append the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type ReadingItem struct{
   ID string `json:"id" fauna:"id" validate:"required"`
   Title string `json:"title" fauna:"title" validate:"required"`
   Link string `json:"link" fauna:"link" validate:"omitempty,url"`
   Type string `json:"type" fauna:"type" validate:"required"`
   Author string `json:"author" fauna:"author" validate:"required"`
}

func NewReadingItem(id, title, link, itemType, author string) ReadingItem {
   return ReadingItem{
      id,
      title,
      link,
      itemType,
      author,
   }
}

func (r ReadingItem) Validate() error {
   return validator.New().Struct(r)
}

func (r ReadingItem) UniqueID() string {
   return r.ID
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;fauna&lt;/strong&gt; field tag is used to encode native Go structs into FQL expressions and to decode values from FQL queries into native Go structs. They are pretty similar to how the &lt;strong&gt;json&lt;/strong&gt; field tag work. The &lt;strong&gt;validate&lt;/strong&gt; field tag, on the other hand, is used to add validation rules to struct fields which can later be validated like we did in the &lt;strong&gt;Validate&lt;/strong&gt; method of &lt;strong&gt;ReadingItem&lt;/strong&gt;. You can learn more about the validator package &lt;a href="https://godoc.org/gopkg.in/go-playground/validator.v10" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Handling
&lt;/h2&gt;

&lt;p&gt;Now that we have our models ready, we can move to the API layer which is where we handle requests by responding with the appropriate responses. But before we start writing the code for the API layer, we need to write that of the &lt;strong&gt;customerrors&lt;/strong&gt; package. Each method of the controller struct returns an &lt;a href="https://golang.org/pkg/builtin/#error" rel="noopener noreferrer"&gt;error&lt;/a&gt; so we need to write our custom errors to store other error information like status code and so on. Here's the code for the custom errors package:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package customerrors

import "strconv"

type AppError struct{
   StatusCode int
   ErrorText string
}

func (ae AppError) Error() string{
   return "status code: "+strconv.Itoa(ae.StatusCode)+", error message: "+ae.ErrorText
}

func NewAppError(statusCode int, errorText string) AppError{
   return AppError{statusCode, errorText}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  API Layer
&lt;/h2&gt;

&lt;p&gt;With that taken care of, we can finally move to the API layer. Here’s what our controller should look like:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package controllers

import (
   "fmt"
   f "github.com/fauna/faunadb-go/v3/faunadb"
   "github.com/gin-gonic/gin"
   "github.com/gozaddy/crud-api-fauna/customerrors"
   "github.com/gozaddy/crud-api-fauna/database"
   "github.com/gozaddy/crud-api-fauna/models"
   "github.com/mitchellh/mapstructure"
   "net/http"
)

type controller struct{
   DB database.FaunaDB
}


func NewController(db database.FaunaDB) *controller{
   return &amp;amp;controller{db}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's begin by writing the endpoint to add a new reading item to the reading list. &lt;br&gt;
We will call the method for this, &lt;strong&gt;AddReadingItem&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (ctrl *controller) AddReadingItem(c *gin.Context) error {
   var req struct{
      Title string `json:"title" form:"title" binding:"required"`
      Link string `json:"link" form:"link" binding:"omitempty,url"`
      Type string `json:"type" form:"type" binding:"required"`
      Author string `json:"author" form:"author" binding:"required"`
   }

   err := c.ShouldBind(&amp;amp;req)
   if err != nil{
      return customerrors.NewAppError(http.StatusBadRequest, err.Error())
   }

   //generate item id
   id, err := ctrl.DB.NewID()
   if err != nil{
      return err
   }

   //create new reading item
   item := models.NewReadingItem(id, req.Title, req.Link, req.Type, req.Author)

   //save new reading item to FaunaDB
  _, err := ctrl.DB.AddDocument(database.ReadingItemCollection, item)
   if err != nil{
      return err
   }
   c.JSON(200, gin.H{
      "message": "New reading item successfully created!",
   })

   return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;json&lt;/strong&gt; and &lt;strong&gt;form&lt;/strong&gt; field tags are used to bind the request body to our &lt;strong&gt;req&lt;/strong&gt; struct depending on the content-type of the request body(JSON or x-www-form-urlencoded). You can learn more about this &lt;a href="https://github.com/gin-gonic/gin#model-binding-and-validation" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The &lt;strong&gt;binding&lt;/strong&gt; field tag is used to validate the fields of the request's body. Gin uses the validator package to achieve this so it works exactly the same way we saw earlier in this article but with a different field tag.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;shouldBind&lt;/code&gt; method does the actual binding of the request body to our struct and returns an error when one of the request body's fields does not pass validation. In this case, we can now return an &lt;code&gt;AppError&lt;/code&gt; with the Bad request status code and the error message. In a real app, you would have to parse the error from &lt;code&gt;shouldBind&lt;/code&gt; returned to get a more understandable error message.&lt;/p&gt;

&lt;p&gt;We then create a new document ID using the &lt;code&gt;NewID&lt;/code&gt; method and a new reading item based off the request body which we add to our database with the &lt;code&gt;AddDocument&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;As you can see, writing our controller method is very straightforward because of the background code we already wrote in the other packages. Here's the endpoint for getting an individual reading item.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (ctrl *controller) GetOneReadingItem(c *gin.Context) error {
   var item models.ReadingItem
   val, err := ctrl.DB.GetDocument(database.ReadingItemCollection, c.Param("id"))
   if err != nil{
      if ferr, ok := err.(f.FaunaError); !ok{
         return err
      } else{
         if ferr.Status() == 404 {
            return customerrors.NewAppError(ferr.Status(),"The reading item with the provided ID does not exist")
         } else if ferr.Status() == 400 {
            return customerrors.NewAppError(ferr.Status(), err.Error())
         }
         return err
      }
   }

   err = val.At(f.ObjKey("data")).Get(&amp;amp;item)
   if err != nil{
      return err
   }

   c.JSON(200, gin.H{
      "message": "Reading item retrieved!",
      "data": item,
   })
   return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here, we simply got the document with the ID specified in the endpoint path, checked the error returned and handled each error case by returning the appropriate error from the method. &lt;br&gt;
The data returned from FaunaDB is in the form of:&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%2Fpaper-attachments.dropbox.com%2Fs_64390F46704890BEA832229C97DE3FE1B12D862D8C47066B7B3C5D235A75662F_1605980173759_crud-fauna-6.JPG" 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%2Fpaper-attachments.dropbox.com%2Fs_64390F46704890BEA832229C97DE3FE1B12D862D8C47066B7B3C5D235A75662F_1605980173759_crud-fauna-6.JPG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to find a way to get the value of the data property and decode it into a Go type. To accomplish this, we made use of the &lt;code&gt;At&lt;/code&gt; and &lt;code&gt;Get&lt;/code&gt; methods.  The &lt;code&gt;At&lt;/code&gt; method allows to select a particular property from the document while &lt;code&gt;Get&lt;/code&gt; does the decoding. We then return the item and a response message with the &lt;code&gt;JSON&lt;/code&gt; which is used for replying clients with JSON-encoded data(content-type: application/json).&lt;/p&gt;

&lt;p&gt;For the update endpoint:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (ctrl *controller) UpdateOneReadingItem(c *gin.Context) error {
   var req struct{
      Title string `json:"title" form:"title" binding:"omitempty" fauna:"title" mapstructure:"title,omitempty"`
      Link string `json:"link" form:"link" binding:"omitempty,url" fauna:"link" mapstructure:"link,omitempty"`
      Type string `json:"type" form:"type" binding:"omitempty" fauna:"type" mapstructure:"type,omitempty"`
      Author string `json:"author" form:"author" binding:"omitempty" fauna:"author" mapstructure:"author,omitempty"`
   }

   err := c.ShouldBind(&amp;amp;req)
   if err != nil{
      return customerrors.NewAppError(http.StatusBadRequest, err.Error())
   }

   var update map[string]interface{}

   err = mapstructure.Decode(req, &amp;amp;update)
   if err != nil{
      return err
   }

   fmt.Println(update)

   err = ctrl.DB.UpdateDocument(database.ReadingItemCollection, c.Param("id"), update)
   if err != nil{
      if ferr, ok := err.(f.FaunaError); !ok{
         return err
      } else{
         if ferr.Status() == 404 {
            return customerrors.NewAppError(ferr.Status(),"The reading item with the provided ID does not exist")
         } else if ferr.Status() == 400 {
            return customerrors.NewAppError(ferr.Status(), err.Error())
         }
         return err
      }
   }

   c.JSON(200, gin.H{
      "message": "Reading item updated successfully!",
   })

   return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The only thing new in this method is the use of the mapstructure library. Mapstructure allows us to decode maps into native Go structs and vice versa. A PATCH request will be used to access this endpoint and as is common with PATCH requests, the client only needs to include the new values of the resource they are attempting to update in the request body unlike in a PUT request where the entire new resource data is included in the request body. So &lt;strong&gt;req&lt;/strong&gt; contains all the fields of the resource(in this case, a reading item) and uses &lt;strong&gt;omitempty&lt;/strong&gt; to skip validation for fields that are empty. The only problem is that, if we are only updating some of the fields of the resource, some fields of &lt;strong&gt;req&lt;/strong&gt; will be empty and this would lead to a bug as we would be changing some of the fields of our resource to empty strings.&lt;/p&gt;

&lt;p&gt;Mapstructure solves this by using &lt;strong&gt;omitempty&lt;/strong&gt; as one of the values of the &lt;strong&gt;mapstructure&lt;/strong&gt; field tag. This ignores any empty string and converts the valid fields of our struct to a map which we can now pass to our &lt;code&gt;UpdateDocument&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Let's now move on to the &lt;code&gt;DeleteOneReadingItem&lt;/code&gt; method which handles requests for deleting reading items.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (ctrl *controller) DeleteOneReadingItem(c *gin.Context) error {
   err := ctrl.DB.DeleteDocument(database.ReadingItemCollection, c.Param("id"))
   if err != nil{
      if ferr, ok := err.(f.FaunaError); !ok{
         return err
      } else{
         if ferr.Status() == 404 {
            return customerrors.NewAppError(ferr.Status(),"The reading item with the provided ID does not exist")
         } else if ferr.Status() == 400 {
            return customerrors.NewAppError(ferr.Status(), err.Error())
         }
         return err
      }
   }

   c.JSON(200, gin.H{
      "message": "Reading item deleted successfully",
   })

   return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, let us move to the final endpoint - the endpoint for getting all the reading items. With this endpoint, we can get all the reading items stored in our database or all the reading items of a particular type so this is where we will make use of the index we created.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (ctrl *controller) GetAllReadingItems(c *gin.Context) error {
   itemType := c.Query("type")
   var match f.Expr

   if itemType == "" {
      match = f.Documents(f.Collection(database.ReadingItemCollection))
   } else{
      match = f.MatchTerm(f.Index(database.ReadingItemByTypeIndex), itemType)
   }
   var items []models.ReadingItem
   val, err := ctrl.DB.FaunaClient().Query(
      f.Map(
         f.Paginate(
            match,
         ),
         f.Lambda("docRef", f.Select(f.Arr{"data"},f.Get(f.Var("docRef")))),
      ),
   )
   if err != nil{
      return err
   }

   err = val.At(f.ObjKey("data")).Get(&amp;amp;items)
   if err != nil{
      return err
   }

   c.JSON(200, gin.H{
      "message": "Reading items retrieved successfully",
      "data": items,
   })

   return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the first line, we used the &lt;code&gt;Query&lt;/code&gt; method to get the query parameter called &lt;strong&gt;type&lt;/strong&gt;. If the user did not specify a query parameter, we use the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/documents?lang=go" rel="noopener noreferrer"&gt;Documents&lt;/a&gt; function to get all the documents in the collection otherwise, we use the &lt;strong&gt;ReadingItemByTypeIndex&lt;/strong&gt; to get the reading items that are of the specified type.&lt;/p&gt;

&lt;p&gt;We later use the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/lambda?lang=go" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt; function to get the individual documents instead of only returning the references. &lt;/p&gt;

&lt;p&gt;With that done, we just need to set up our server in &lt;em&gt;server.go.&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
   "github.com/gin-gonic/gin"
   "github.com/gozaddy/crud-api-fauna/controllers"
   "github.com/gozaddy/crud-api-fauna/customerrors"
   "github.com/gozaddy/crud-api-fauna/database"
   "github.com/joho/godotenv"
   "log"
   "os"
)


func handle(f func(c *gin.Context) error) gin.HandlerFunc {
   return func(context *gin.Context){
      if err := f(context); err != nil{
         if ae, ok := err.(customerrors.AppError); ok{
            context.JSON(ae.StatusCode, gin.H{
               "message": ae.ErrorText,
            })
         } else {
            log.Println(err.Error())
            context.JSON(500, gin.H{
               "message": "Internal server error",
            })
         }
      }

   }
}

func init(){
   //load .env file with godotenv so we can access our FaunaDB secret
   err := godotenv.Load()
   if err != nil{
      log.Fatalln("Error loading .env file:"+ err.Error())
   }
}

func main(){
   fdb := database.NewFaunaDB(os.Getenv("FAUNA_DB_SERVER_SECRET"))
   err := fdb.Init()
   if err != nil{
      log.Fatalln(err)
   }


   controller := controllers.NewController(fdb)
   router := gin.Default()
   router.GET("/api/", func(c *gin.Context) {
      c.JSON(200, gin.H{
         "message": "Welcome!",
      })
   })
   router.GET("/api/items", handle(controller.GetAllReadingItems))
   router.POST("/api/items", handle(controller.AddReadingItem))
   router.GET("/api/items/:id", handle(controller.GetOneReadingItem))
   router.PATCH("/api/items/:id", handle(controller.UpdateOneReadingItem))
   router.DELETE("/api/items/:id", handle(controller.DeleteOneReadingItem))

   //run our server on port 4000
   _ = router.Run(":4000")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this file, we just set up our server with Gin, load our .env file and write the &lt;strong&gt;handle&lt;/strong&gt; function which responds to the client based on the errors received from controller's methods.&lt;/p&gt;

&lt;p&gt;Now, we can start our server with &lt;code&gt;go run .&lt;/code&gt; and test the different endpoints with Postman or a similar tool.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;You should now know how to build a simple CRUD API using Go, Gin and Fauna. You can find the code for this API &lt;a href="https://github.com/GoZaddy/crud-api-fauna" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You can also learn more about FaunaDB using &lt;a href="https://docs.fauna.com/fauna/current/" rel="noopener noreferrer"&gt;its amazing documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>fauna</category>
      <category>go</category>
    </item>
  </channel>
</rss>
