馃寠 A GraphQL implementation in Gleam
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}