馃寠 A GraphQL implementation in Gleam
at main 270 lines 7.5 kB view raw
1import database 2import gleam/int 3import gleam/list 4import gleam/option.{None, Some} 5import sqlight 6import swell/schema 7import swell/value 8 9/// Build the User type (without nested posts to avoid circular dependency) 10pub fn user_type() -> schema.Type { 11 schema.object_type("User", "A user in the system", [ 12 schema.field("id", schema.id_type(), "User ID", fn(ctx) { 13 case ctx.data { 14 Some(value.Object(fields)) -> { 15 case list.key_find(fields, "id") { 16 Ok(id_val) -> Ok(id_val) 17 Error(_) -> Ok(value.Null) 18 } 19 } 20 _ -> Ok(value.Null) 21 } 22 }), 23 schema.field("name", schema.string_type(), "User name", fn(ctx) { 24 case ctx.data { 25 Some(value.Object(fields)) -> { 26 case list.key_find(fields, "name") { 27 Ok(name_val) -> Ok(name_val) 28 Error(_) -> Ok(value.Null) 29 } 30 } 31 _ -> Ok(value.Null) 32 } 33 }), 34 schema.field("email", schema.string_type(), "User email", fn(ctx) { 35 case ctx.data { 36 Some(value.Object(fields)) -> { 37 case list.key_find(fields, "email") { 38 Ok(email_val) -> Ok(email_val) 39 Error(_) -> Ok(value.Null) 40 } 41 } 42 _ -> Ok(value.Null) 43 } 44 }), 45 ]) 46} 47 48/// Build the Post type (without nested author to avoid circular dependency) 49pub fn post_type() -> schema.Type { 50 schema.object_type("Post", "A blog post", [ 51 schema.field("id", schema.id_type(), "Post ID", fn(ctx) { 52 case ctx.data { 53 Some(value.Object(fields)) -> { 54 case list.key_find(fields, "id") { 55 Ok(id_val) -> Ok(id_val) 56 Error(_) -> Ok(value.Null) 57 } 58 } 59 _ -> Ok(value.Null) 60 } 61 }), 62 schema.field("title", schema.string_type(), "Post title", fn(ctx) { 63 case ctx.data { 64 Some(value.Object(fields)) -> { 65 case list.key_find(fields, "title") { 66 Ok(title_val) -> Ok(title_val) 67 Error(_) -> Ok(value.Null) 68 } 69 } 70 _ -> Ok(value.Null) 71 } 72 }), 73 schema.field("content", schema.string_type(), "Post content", fn(ctx) { 74 case ctx.data { 75 Some(value.Object(fields)) -> { 76 case list.key_find(fields, "content") { 77 Ok(content_val) -> Ok(content_val) 78 Error(_) -> Ok(value.Null) 79 } 80 } 81 _ -> Ok(value.Null) 82 } 83 }), 84 schema.field("authorId", schema.int_type(), "Author ID", fn(ctx) { 85 case ctx.data { 86 Some(value.Object(fields)) -> { 87 case list.key_find(fields, "author_id") { 88 Ok(author_id_val) -> Ok(author_id_val) 89 Error(_) -> Ok(value.Null) 90 } 91 } 92 _ -> Ok(value.Null) 93 } 94 }), 95 ]) 96} 97 98/// Convert a User to a GraphQL Value 99fn user_to_value(user: database.User) -> value.Value { 100 value.Object([ 101 #("id", value.Int(user.id)), 102 #("name", value.String(user.name)), 103 #("email", value.String(user.email)), 104 ]) 105} 106 107/// Convert a Post to a GraphQL Value 108fn post_to_value(post: database.Post) -> value.Value { 109 value.Object([ 110 #("id", value.Int(post.id)), 111 #("title", value.String(post.title)), 112 #("content", value.String(post.content)), 113 #("author_id", value.Int(post.author_id)), 114 ]) 115} 116 117/// Build the Query type 118pub fn query_type(conn: sqlight.Connection) -> schema.Type { 119 schema.object_type("Query", "Root query type", [ 120 schema.field( 121 "users", 122 schema.list_type(user_type()), 123 "Get all users", 124 fn(_ctx) { 125 let users = database.get_users(conn) 126 Ok(value.List(list.map(users, user_to_value))) 127 }, 128 ), 129 schema.field_with_args( 130 "user", 131 user_type(), 132 "Get a user by ID", 133 [ 134 schema.argument( 135 "id", 136 schema.non_null(schema.id_type()), 137 "User ID", 138 None, 139 ), 140 ], 141 fn(ctx) { 142 case schema.get_argument(ctx, "id") { 143 Some(value.Int(user_id)) -> { 144 case database.get_user(conn, user_id) { 145 Ok(user) -> Ok(user_to_value(user)) 146 Error(err) -> Error(err) 147 } 148 } 149 Some(value.String(user_id_str)) -> { 150 case int.parse(user_id_str) { 151 Ok(user_id) -> { 152 case database.get_user(conn, user_id) { 153 Ok(user) -> Ok(user_to_value(user)) 154 Error(err) -> Error(err) 155 } 156 } 157 Error(_) -> Error("Invalid user ID format") 158 } 159 } 160 _ -> Error("User ID is required") 161 } 162 }, 163 ), 164 schema.field( 165 "posts", 166 schema.list_type(post_type()), 167 "Get all posts", 168 fn(_ctx) { 169 let posts = database.get_posts(conn) 170 Ok(value.List(list.map(posts, post_to_value))) 171 }, 172 ), 173 schema.field_with_args( 174 "post", 175 post_type(), 176 "Get a post by ID", 177 [ 178 schema.argument( 179 "id", 180 schema.non_null(schema.id_type()), 181 "Post ID", 182 None, 183 ), 184 ], 185 fn(ctx) { 186 case schema.get_argument(ctx, "id") { 187 Some(value.Int(post_id)) -> { 188 case database.get_post(conn, post_id) { 189 Ok(post) -> Ok(post_to_value(post)) 190 Error(err) -> Error(err) 191 } 192 } 193 Some(value.String(post_id_str)) -> { 194 case int.parse(post_id_str) { 195 Ok(post_id) -> { 196 case database.get_post(conn, post_id) { 197 Ok(post) -> Ok(post_to_value(post)) 198 Error(err) -> Error(err) 199 } 200 } 201 Error(_) -> Error("Invalid post ID format") 202 } 203 } 204 _ -> Error("Post ID is required") 205 } 206 }, 207 ), 208 ]) 209} 210 211/// Input type for creating a user 212pub fn create_user_input() -> schema.Type { 213 schema.input_object_type("CreateUserInput", "Input for creating a user", [ 214 schema.input_field( 215 "name", 216 schema.non_null(schema.string_type()), 217 "User name", 218 None, 219 ), 220 schema.input_field( 221 "email", 222 schema.non_null(schema.string_type()), 223 "User email", 224 None, 225 ), 226 ]) 227} 228 229/// Build the Mutation type 230pub fn mutation_type(conn: sqlight.Connection) -> schema.Type { 231 schema.object_type("Mutation", "Root mutation type", [ 232 schema.field_with_args( 233 "createUser", 234 user_type(), 235 "Create a new user", 236 [ 237 schema.argument( 238 "input", 239 schema.non_null(create_user_input()), 240 "User data", 241 None, 242 ), 243 ], 244 fn(ctx) { 245 case schema.get_argument(ctx, "input") { 246 Some(value.Object(fields)) -> { 247 let name_result = list.key_find(fields, "name") 248 let email_result = list.key_find(fields, "email") 249 250 case name_result, email_result { 251 Ok(value.String(name)), Ok(value.String(email)) -> { 252 case database.create_user(conn, name, email) { 253 Ok(user) -> Ok(user_to_value(user)) 254 Error(err) -> Error(err) 255 } 256 } 257 _, _ -> Error("Invalid input: name and email are required") 258 } 259 } 260 _ -> Error("Invalid input format") 261 } 262 }, 263 ), 264 ]) 265} 266 267/// Build the complete GraphQL schema 268pub fn build_schema(conn: sqlight.Connection) -> schema.Schema { 269 schema.schema(query_type(conn), Some(mutation_type(conn))) 270}