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

> 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
```bash
gleam add squall
```
## Quick Start
1. **Create a `.gql` file** at `src/my_app/graphql/get_user.gql`:
```graphql
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
```
2. **Generate code:**
```bash
gleam run -m squall generate https://api.example.com/graphql
```
3. **Use it:**
```gleam
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:
```gleam
// 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:
```gleam
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
```bash
# Generate code
gleam run -m squall generate <endpoint>
# Example
gleam run -m squall generate https://rickandmortyapi.com/graphql
```
## Examples
See the [example/](./example/) directory for a complete working example.
## Roadmap
- [ ] Directive handling (`@include`, `@skip`)
- [ ] Custom scalar type mapping
- [ ] Schema caching
## License
Apache-2.0