Type-safe GraphQL client generator for Gleam

format

Changed files
+30 -43
src
test
+15 -8
src/squall.gleam
··· 57 57 ) 58 58 } 59 59 60 + @target(erlang) 60 61 /// Create a new Erlang GraphQL client with custom headers. 61 62 /// This uses the Erlang HTTP adapter (gleam_httpc). 62 - @target(erlang) 63 63 pub fn new_erlang_client( 64 64 endpoint: String, 65 65 headers: List(#(String, String)), ··· 67 67 Client(endpoint: endpoint, headers: headers, send_request: erlang.adapter()) 68 68 } 69 69 70 - /// Create a new Erlang GraphQL client with bearer token authentication. 71 70 @target(erlang) 71 + /// Create a new Erlang GraphQL client with bearer token authentication. 72 72 pub fn new_erlang_client_with_auth(endpoint: String, token: String) -> Client { 73 73 Client( 74 74 endpoint: endpoint, ··· 77 77 ) 78 78 } 79 79 80 + @target(javascript) 80 81 /// Create a new JavaScript GraphQL client with custom headers. 81 82 /// This uses the JavaScript HTTP adapter (Fetch API). 82 - @target(javascript) 83 83 pub fn new_javascript_client( 84 84 endpoint: String, 85 85 headers: List(#(String, String)), 86 86 ) -> Client { 87 - Client(endpoint: endpoint, headers: headers, send_request: javascript.adapter()) 87 + Client( 88 + endpoint: endpoint, 89 + headers: headers, 90 + send_request: javascript.adapter(), 91 + ) 88 92 } 89 93 94 + @target(javascript) 90 95 /// Create a new JavaScript GraphQL client with bearer token authentication. 91 - @target(javascript) 92 - pub fn new_javascript_client_with_auth(endpoint: String, token: String) -> Client { 96 + pub fn new_javascript_client_with_auth( 97 + endpoint: String, 98 + token: String, 99 + ) -> Client { 93 100 Client( 94 101 endpoint: endpoint, 95 102 headers: [#("Authorization", "Bearer " <> token)], ··· 97 104 ) 98 105 } 99 106 107 + @target(javascript) 100 108 /// Execute a GraphQL query on JavaScript targets. 101 109 /// Returns a Promise that resolves to a Result containing the decoded response. 102 - @target(javascript) 103 110 pub fn execute_query( 104 111 client: Client, 105 112 query: String, ··· 157 164 } 158 165 } 159 166 167 + @target(erlang) 160 168 /// Execute a GraphQL query on Erlang targets. 161 169 /// Returns a Result containing the decoded response. 162 - @target(erlang) 163 170 pub fn execute_query( 164 171 client: Client, 165 172 query: String,
+2 -2
src/squall/adapter.gleam
··· 4 4 @target(javascript) 5 5 import gleam/javascript/promise.{type Promise} 6 6 7 + @target(erlang) 7 8 /// HTTP adapter function type for Erlang target. 8 9 /// Synchronously returns a Result with Response or error string. 9 - @target(erlang) 10 10 pub type HttpAdapter = 11 11 fn(Request(String)) -> Result(Response(String), String) 12 12 13 + @target(javascript) 13 14 /// HTTP adapter function type for JavaScript target. 14 15 /// Asynchronously returns a Promise containing a Result with Response or error string. 15 - @target(javascript) 16 16 pub type HttpAdapter = 17 17 fn(Request(String)) -> Promise(Result(Response(String), String))
+3 -1
src/squall/adapter/javascript.gleam
··· 16 16 /// This adapter works on both browser and Node.js JavaScript targets. 17 17 /// Returns a Promise that resolves to a Result. 18 18 pub fn adapter() -> adapter.HttpAdapter { 19 - fn(req: request.Request(String)) -> Promise(Result(response.Response(String), String)) { 19 + fn(req: request.Request(String)) -> Promise( 20 + Result(response.Response(String), String), 21 + ) { 20 22 fetch.send(req) 21 23 |> promise.try_await(fetch.read_text_body) 22 24 |> promise.map(result.map_error(_, fn(_) { "HTTP request failed" }))
+5 -2
src/squall/internal/codegen.gleam
··· 1118 1118 } 1119 1119 } 1120 1120 } 1121 - let inner_encoder = "fn(list) { json.array(from: list, of: " <> of_fn <> ") }" 1121 + let inner_encoder = 1122 + "fn(list) { json.array(from: list, of: " <> of_fn <> ") }" 1122 1123 call_doc("json.nullable", [ 1123 1124 doc.from_string(field_access), 1124 1125 doc.from_string(inner_encoder), ··· 1158 1159 // This is an Object type 1159 1160 case dict.get(schema_types, name) { 1160 1161 Ok(schema.ObjectType(_, _, _)) -> 1161 - call_doc(snake_case(name) <> "_to_json", [doc.from_string(field_access)]) 1162 + call_doc(snake_case(name) <> "_to_json", [ 1163 + doc.from_string(field_access), 1164 + ]) 1162 1165 _ -> 1163 1166 // Fallback to string if not an object type 1164 1167 call_doc("json.string", [doc.from_string(field_access)])
+5 -30
test/codegen_test.gleam
··· 1424 1424 [], 1425 1425 None, 1426 1426 ), 1427 - schema.Field( 1428 - "name", 1429 - schema.NamedType("String", schema.Scalar), 1430 - [], 1431 - None, 1432 - ), 1433 - schema.Field( 1434 - "email", 1435 - schema.NamedType("String", schema.Scalar), 1436 - [], 1437 - None, 1438 - ), 1427 + schema.Field("name", schema.NamedType("String", schema.Scalar), [], None), 1428 + schema.Field("email", schema.NamedType("String", schema.Scalar), [], None), 1439 1429 ] 1440 1430 1441 1431 let mock_schema = ··· 1495 1485 1496 1486 // Create mock schema with nested types 1497 1487 let location_fields = [ 1498 - schema.Field( 1499 - "city", 1500 - schema.NamedType("String", schema.Scalar), 1501 - [], 1502 - None, 1503 - ), 1504 - schema.Field( 1505 - "country", 1506 - schema.NamedType("String", schema.Scalar), 1507 - [], 1508 - None, 1509 - ), 1488 + schema.Field("city", schema.NamedType("String", schema.Scalar), [], None), 1489 + schema.Field("country", schema.NamedType("String", schema.Scalar), [], None), 1510 1490 ] 1511 1491 1512 1492 let user_fields = [ ··· 1685 1665 [], 1686 1666 None, 1687 1667 ), 1688 - schema.Field( 1689 - "metadata", 1690 - schema.NamedType("JSON", schema.Scalar), 1691 - [], 1692 - None, 1693 - ), 1668 + schema.Field("metadata", schema.NamedType("JSON", schema.Scalar), [], None), 1694 1669 ] 1695 1670 1696 1671 let mock_schema =