{
  "slug": "Building-a-GraphQL-API-in-Go-with-Custom-Scalars-0b1a1b3e52f9",
  "title": "Building a GraphQL API in Go with Custom Scalars",
  "subtitle": "GraphQL has become a powerful alternative to REST APIs, offering clients the ability to request exactly the data they need. In Go, the…",
  "excerpt": "GraphQL has become a powerful alternative to REST APIs, offering clients the ability to request exactly the data they need. In Go, the…",
  "date": "2025-12-26",
  "tags": [
    "Go",
    "API",
    "GraphQL"
  ],
  "readingTime": "3 min",
  "url": "https://medium.com/@mobinshaterian/building-a-graphql-api-in-go-with-custom-scalars-0b1a1b3e52f9",
  "hero": "https://cdn-images-1.medium.com/max/800/1*WlVGDJC4kBCrpprBT_BkPA.png",
  "content": [
    {
      "type": "heading",
      "level": 2,
      "text": "Building a GraphQL API in Go with Custom Scalars"
    },
    {
      "type": "paragraph",
      "html": "GraphQL has become a powerful alternative to REST APIs, offering clients the ability to request exactly the data they need. In Go, the gqlgen library makes it straightforward to build type‑safe GraphQL servers that integrate cleanly with existing service layers."
    },
    {
      "type": "paragraph",
      "html": "In this article, we’ll walk through how to expose a <code>Product</code> type via GraphQL, handle custom scalars like <code>Time</code> and <code>Int64</code>, and wire resolvers to your service layer."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Go-GraphQL Project"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Why GraphQL?"
    },
    {
      "type": "paragraph",
      "html": "Unlike REST, GraphQL allows clients to:"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "Fetch multiple resources in a single request.",
        "Specify exactly which fields they want.",
        "Handle evolving schemas without breaking existing clients."
      ]
    },
    {
      "type": "paragraph",
      "html": "This flexibility is especially useful in e‑commerce or product catalog systems, where clients may need different slices of product data depending on context."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*WlVGDJC4kBCrpprBT_BkPA.png",
      "alt": "Building a GraphQL API in Go with Custom Scalars",
      "caption": "",
      "width": 1024,
      "height": 1024
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Defining Custom Scalars"
    },
    {
      "type": "paragraph",
      "html": "By default, GraphQL only supports a limited set of scalar types (<code>Int</code>, <code>String</code>, <code>Boolean</code>, etc.). In Go, we often need richer types like <code>time.Time</code> or <code>int64</code>. To handle this, we declare custom scalars in our schema and configure them in <code>gqlgen.yml</code>."
    },
    {
      "type": "code",
      "lang": "text",
      "code": "models:  Time:    model:      - go-graphql/internal/graph/model.Time  Int64:    model:      -\ngithub.com/99designs/gqlgen/graphql.Int64"
    },
    {
      "type": "paragraph",
      "html": "And in the schema:"
    },
    {
      "type": "code",
      "lang": "graphql",
      "code": "scalar Time\nscalar Int64"
    },
    {
      "type": "paragraph",
      "html": "This tells gqlgen to map GraphQL <code>Time</code> to our Go <code>model.Time</code> type, and <code>Int64</code> to the provided implementation in <code>graphql.Int64</code>. Without this mapping, gqlgen would default to <code>int</code> for <code>Int</code>, which is only 32‑bit and unsuitable for large values like product prices."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Schema for Products"
    },
    {
      "type": "paragraph",
      "html": "We define our product types and queries in <code>schema.graphqls</code>:"
    },
    {
      "type": "code",
      "lang": "graphql",
      "code": "type Product {\n  id: Int!\n  name: String!\n  description: String!\n  price: Int64!\n}\ntype ProductListResponse {\n  id: Int!\n  name: String!\n  description: String!\n  price: Int64!\n}\ntype ErrorResponse {\n  code: String!\n  message: String!\n}\ntype Query {\n  \"\"\"\n  Get a single product by ID\n  \"\"\"\n  product(id: Int!): Product\n  \"\"\"\n  Get a list of all products\n  \"\"\"\n  products: [Product!]!\n}"
    },
    {
      "type": "paragraph",
      "html": "Here, <code>price</code> is explicitly declared as <code>Int64!</code> to ensure we can handle large numeric values safely."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Implementing Resolvers"
    },
    {
      "type": "paragraph",
      "html": "Resolvers connect GraphQL queries to your service layer. For example:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "// Product is the resolver for the product field.\nfunc (r *queryResolver) Product(ctx context.Context, id int) (*model.Product, error) {\n    product, err := r.ProductService.GetProductByID(ctx, int32(id))\n    if err != nil {\n        return nil, fmt.Errorf(\"failed to get product: %w\", err)\n    }\n    return &model.Product{\n        ID: int(product.ID), Name: product.Name, Description: product.Description, Price:\n        product.Price,\n    }, nil\n}\n// Products is the resolver for the products field.\nfunc (r *queryResolver) Products(ctx context.Context) ([]*model.Product, error) {\n    products, err := r.ProductService.ListProducts(ctx)\n    if err != nil {\n        return nil, fmt.Errorf(\"failed to list products: %w\", err)\n    }\n    var result []*model.Product\n    for _, p := range products {\n        result = append(result, &model.Product{\n            ID: int(p.ID), Name: p.Name, Description: p.Description, Price: p.Price,\n        })\n    }\n    return result, nil\n}"
    },
    {
      "type": "paragraph",
      "html": "These resolvers delegate to the existing <code>ProductService</code>, ensuring that both REST and GraphQL layers share the same business logic."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Example Queries"
    },
    {
      "type": "paragraph",
      "html": "Clients can now query your API like this:"
    },
    {
      "type": "code",
      "lang": "graphql",
      "code": "query {  product(id: 1) {    id    name    price  }}query {  products {    id    name    description\nprice  }}j"
    },
    {
      "type": "paragraph",
      "html": "The server responds with exactly the requested fields, no more and no less."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Git Commit"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Conclusion"
    },
    {
      "type": "paragraph",
      "html": "By introducing custom scalars (<code>Time</code>, <code>Int64</code>) and wiring resolvers to your service layer, you’ve created a robust GraphQL API in Go. This approach ensures type safety, handles large numeric values correctly, and provides clients with flexible queries over your product catalogue."
    },
    {
      "type": "paragraph",
      "html": "GraphQL in Go with gqlgen is not only powerful but also developer‑friendly, letting you evolve your API without breaking existing clients. With these changes, your system is ready to support modern, efficient data access patterns."
    }
  ]
}
