Type-safe GraphQL client generator for Gleam
Gleam 79.1%
HTML 0.1%
Other 20.8%
34 1 2

Clone this repository

https://tangled.org/chadtmiller.com/squall
git@tangled.org:chadtmiller.com/squall

For self-hosted knots, clone URLs may differ based on your setup.

README.md

Squall#

A type-safe sans-io GraphQL client generator for Gleam.

Squall

This project is in early development and may contain bugs/unsupported GraphQL queries.

What is Sans-IO?#

Squall generates functions to build HTTP requests and parse HTTP responses, but doesn't send them. You control the HTTP layer.

Benefits:

  • Can be used on both erlang and javascript runtimes
  • Use any HTTP client you want
  • Easy to test without mocking
  • Full control over retries, timeouts, logging

Installation#

gleam add squall

Quick Start#

  1. Create a .gql file at src/my_app/graphql/get_user.gql:
query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}
  1. Generate code:
gleam run -m squall generate https://api.example.com/graphql
  1. Use it:
import squall
import my_app/graphql/get_user
import gleam/httpc

pub fn main() {
  // 1. Create client (just config)
  let client = squall.new("https://api.example.com/graphql", [])

  // 2. Build request (no I/O)
  let assert Ok(request) = get_user.get_user(client, "123")

  // 3. Send request (you control this)
  let assert Ok(response) = httpc.send(request)

  // 4. Parse response (no I/O)
  let assert Ok(data) = get_user.parse_get_user_response(response.body)

  // 5. Use the data
  io.debug(data.user)
}

How It Works#

For each .gql file, Squall generates two functions:

// Builds HTTP request - no I/O
pub fn get_user(client: Client, id: String) -> Result(Request(String), String)

// Parses response - no I/O
pub fn parse_get_user_response(body: String) -> Result(GetUserResponse, String)

You send the request with your own HTTP client.

JavaScript Target#

Same generated code works on JavaScript. Just use a different HTTP client:

import gleam/fetch
import gleam/javascript/promise

let client = squall.new("https://api.example.com/graphql", [])
let assert Ok(request) = get_user.get_user(client, "123")

fetch.send(request)
|> promise.try_await(fetch.read_text_body)
|> promise.map(fn(result) {
  case result {
    Ok(response) -> {
      let assert Ok(data) = get_user.parse_get_user_response(response.body)
      io.debug(data.user)
    }
    Error(_) -> io.println("Request failed")
  }
})

Type Mapping#

GraphQL Type Gleam Type
String String
Int Int
Float Float
Boolean Bool
ID String
[Type] List(Type)
Type (nullable) Option(Type)
Type! (non-null) Type
Custom Objects Custom Gleam types

CLI Commands#

# Generate code
gleam run -m squall generate <endpoint>

# Example
gleam run -m squall generate https://rickandmortyapi.com/graphql

Examples#

See the examples/ directory for complete working examples:

  • 01-basic - Basic usage with Erlang/JavaScript targets
  • 02-lustre - Frontend app using Lustre

Roadmap#

  • Directive handling (@include, @skip)
  • Custom scalar type mapping
  • Schema caching

License#

Apache-2.0