Database Integration with GraphQL, Fiber, and SQLx in Go

Shivam DubeyShivam Dubey
5 min read

This article will guide you through building a GraphQL API that connects to a PostgreSQL database using Fiber, SQLx, and gqlgen. We'll cover everything from setting up the database to handling queries via resolvers.


Code Explanation and Execution Flow

1. Database Connection (db.go)

Code:

package main

import (
    "log"

    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"
)

var db *sqlx.DB

func initDB() {
    var err error
    db, err = sqlx.Connect("postgres", "user=postgres password=yourpassword dbname=graphql_db sslmode=disable")
    if err != nil {
        log.Fatalf("Failed to connect to database: %v", err)
    }
    log.Println("Database connected successfully")
}

Explanation:

  • Purpose: Initializes the connection to the PostgreSQL database.

  • sqlx.Connect: Connects to the database using the PostgreSQL driver.

  • Error Handling: If the connection fails, the application logs the error and stops.

  • Global Variable (db): Used across the app for executing queries.

Execution Flow:

  1. initDB is called in main.go to establish a connection with the database before the server starts.

  2. Queries in resolvers use this connection to fetch or modify data.


2. Resolvers (resolvers.go)

Code:

package main

type queryResolver struct{}

func (r *queryResolver) Users() ([]*User, error) {
    var users []*User
    err := db.Select(&users, "SELECT * FROM users")
    if err != nil {
        return nil, err
    }
    return users, nil
}

func (r *queryResolver) User(id string) (*User, error) {
    var user User
    err := db.Get(&user, "SELECT * FROM users WHERE id = $1", id)
    if err != nil {
        return nil, err
    }
    return &user, nil
}

Explanation:

  • Resolvers: Functions that handle GraphQL queries by interacting with the database.

  • Users Resolver:

    • Fetches all users from the users table.

    • Uses db.Select to map query results into a slice of User objects.

  • User Resolver:

    • Fetches a single user by ID using a parameterized query.

    • Uses db.Get to fetch and map the result to a User object.

  • Error Handling:

    • Returns nil and an error if the database query fails or no data is found.

Execution Flow:

  1. GraphQL server invokes the resolver based on the query type (users or user).

  2. The resolver executes a SQL query to fetch data from the database.

  3. The results are returned as GraphQL responses.


3. GraphQL Types (models.go)

Code:

package main

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

Explanation:

  • Defines the User type to represent the database structure.

  • JSON tags: Ensure that fields are serialized with these names in GraphQL responses.


4. GraphQL Handler (graphql_handler.go)

Code:

package main

import (
    "github.com/99designs/gqlgen/graphql/handler"
    "github.com/99designs/gqlgen/graphql/playground"
    "github.com/gofiber/fiber/v2"
)

func GraphQLHandler(app *fiber.App, srv *handler.Server) {
    app.All("/query", func(c *fiber.Ctx) error {
        srv.ServeHTTP(c.Context())
        return nil
    })

    app.Get("/", func(c *fiber.Ctx) error {
        c.Redirect("/playground")
        return nil
    })

    app.Get("/playground", func(c *fiber.Ctx) error {
        playground.Handler("GraphQL Playground", "/query").ServeHTTP(c.Context())
        return nil
    })
}

Explanation:

  • Purpose: Routes requests to the GraphQL server.

  • /query Route:

    • Handles GraphQL requests (queries, mutations) by invoking the server handler.
  • /playground Route:

    • Serves the GraphQL Playground for testing queries interactively.
  • Fiber Middleware: Uses Fiber's routing and HTTP handling for simplicity and speed.

Execution Flow:

  1. Users access /playground in a browser to open the testing UI.

  2. Queries and mutations are sent to /query, where the GraphQL server processes them.


5. Fiber Server (main.go)

Code:

package main

import (
    "log"

    "github.com/99designs/gqlgen/graphql/handler"
    "github.com/gofiber/fiber/v2"
)

func main() {
    initDB() // Initialize database

    // Create Fiber app
    app := fiber.New()

    // Create GraphQL server
    srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{
        Resolvers: &Resolver{},
    }))

    // Register GraphQL routes
    GraphQLHandler(app, srv)

    // Start server
    log.Println("Server running on http://localhost:8080/")
    log.Fatal(app.Listen(":8080"))
}

Explanation:

  • Initialize Database: Calls initDB to establish a database connection.

  • Fiber App: Creates an HTTP server using Fiber.

  • GraphQL Server:

    • Sets up the gqlgen executable schema and resolvers.

    • Links the Fiber app to the GraphQL server and playground routes.

  • Start Server: Starts the Fiber app on port 8080.

Execution Flow:

  1. Initializes the database connection with initDB.

  2. Sets up routes for GraphQL and starts the server.

  3. Logs the server URL, which can be accessed via a browser or GraphQL client.


6. Test GraphQL Queries

Query All Users:

query {
    users {
        id
        name
        age
    }
}

Execution Flow:

  1. The request hits the /query endpoint.

  2. The users resolver executes a SQL query (SELECT * FROM users).

  3. Results are returned as a list of User objects.

Sample Response:

{
    "data": {
        "users": [
            {
                "id": "1",
                "name": "Alice",
                "age": 25
            },
            {
                "id": "2",
                "name": "Bob",
                "age": 30
            }
        ]
    }
}

Query a User by ID:

query {
    user(id: "1") {
        name
        age
    }
}

Execution Flow:

  1. The request hits the /query endpoint.

  2. The user resolver executes a parameterized query (SELECT * FROM users WHERE id = $1).

  3. The result is returned as a single User object.

Sample Response:

{
    "data": {
        "user": {
            "name": "Alice",
            "age": 25
        }
    }
}

Conclusion

In this tutorial, you:

  1. Learned how to connect a PostgreSQL database to a GraphQL API.

  2. Built a server using Fiber, SQLx, and gqlgen.

  3. Queried and fetched data using GraphQL.

This setup can be extended with mutations, authentication, and more advanced features like subscriptions for real-time updates.

0
Subscribe to my newsletter

Read articles from Shivam Dubey directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shivam Dubey
Shivam Dubey