馃寠 A GraphQL implementation in Gleam
at main 6.0 kB view raw
1import gleam/dynamic/decode 2import gleam/result 3import gleam/string 4import sqlight 5 6pub type User { 7 User(id: Int, name: String, email: String) 8} 9 10pub type Post { 11 Post(id: Int, title: String, content: String, author_id: Int) 12} 13 14/// Create an in-memory database with users and posts tables 15pub fn setup_database() -> Result(sqlight.Connection, sqlight.Error) { 16 use conn <- result.try(sqlight.open(":memory:")) 17 18 // Create users table 19 let users_sql = 20 " 21 CREATE TABLE users ( 22 id INTEGER PRIMARY KEY, 23 name TEXT NOT NULL, 24 email TEXT NOT NULL 25 ) 26 " 27 28 use _ <- result.try(sqlight.exec(users_sql, conn)) 29 30 // Create posts table 31 let posts_sql = 32 " 33 CREATE TABLE posts ( 34 id INTEGER PRIMARY KEY, 35 title TEXT NOT NULL, 36 content TEXT NOT NULL, 37 author_id INTEGER NOT NULL, 38 FOREIGN KEY (author_id) REFERENCES users(id) 39 ) 40 " 41 42 use _ <- result.try(sqlight.exec(posts_sql, conn)) 43 44 // Insert sample users (surfers) 45 let insert_users = 46 " 47 INSERT INTO users (id, name, email) VALUES 48 (1, 'Coco Ho', 'coco@shredmail.com'), 49 (2, 'Carissa Moore', 'carissa@barrelmail.com'), 50 (3, 'Kelly Slater', 'kslater@bombmail.com') 51 " 52 53 use _ <- result.try(sqlight.exec(insert_users, conn)) 54 55 // Insert sample posts (surf reports and stories) 56 let insert_posts = 57 " 58 INSERT INTO posts (id, title, content, author_id) VALUES 59 (1, 'Sick Barrels at Pipe', 'Bruh, Pipeline was absolutely firing today! Scored some gnarly 8ft tubes, got shacked so hard. Offshore winds all day, totally glassy. So stoked!', 1), 60 (2, 'Sunset Bombing', 'Just got done charging some heavy sets at Sunset. Waves were massive, super clean faces. If you are not out here you are missing out big time brah!', 1), 61 (3, 'Epic Dawn Patrol Session', 'Woke up at 5am for dawn patrol and it was totally worth it dude. Glassy perfection, no kooks in the lineup. Just me and the ocean vibing. Radical!', 2), 62 (4, 'Tow-in at Jaws Was Insane', 'Yo, just finished tow-in session at Jaws. 50 footers rolling through, absolutely gnarly! Got worked on one bomb but that is part of the game. Respect the ocean always bruh.', 3) 63 " 64 65 use _ <- result.try(sqlight.exec(insert_posts, conn)) 66 67 Ok(conn) 68} 69 70/// Get all users from the database 71pub fn get_users(conn: sqlight.Connection) -> List(User) { 72 let sql = "SELECT id, name, email FROM users" 73 74 let decoder = { 75 use id <- decode.field(0, decode.int) 76 use name <- decode.field(1, decode.string) 77 use email <- decode.field(2, decode.string) 78 decode.success(User(id:, name:, email:)) 79 } 80 81 sqlight.query(sql, on: conn, with: [], expecting: decoder) 82 |> result.unwrap([]) 83} 84 85/// Get a user by ID 86pub fn get_user(conn: sqlight.Connection, user_id: Int) -> Result(User, String) { 87 let sql = "SELECT id, name, email FROM users WHERE id = ?" 88 89 let decoder = { 90 use id <- decode.field(0, decode.int) 91 use name <- decode.field(1, decode.string) 92 use email <- decode.field(2, decode.string) 93 decode.success(User(id:, name:, email:)) 94 } 95 96 case 97 sqlight.query( 98 sql, 99 on: conn, 100 with: [sqlight.int(user_id)], 101 expecting: decoder, 102 ) 103 { 104 Ok([user]) -> Ok(user) 105 Ok([]) -> Error("User not found") 106 Ok(_) -> Error("Multiple users found") 107 Error(err) -> Error("Database error: " <> string.inspect(err)) 108 } 109} 110 111/// Get all posts from the database 112pub fn get_posts(conn: sqlight.Connection) -> List(Post) { 113 let sql = "SELECT id, title, content, author_id FROM posts" 114 115 let decoder = { 116 use id <- decode.field(0, decode.int) 117 use title <- decode.field(1, decode.string) 118 use content <- decode.field(2, decode.string) 119 use author_id <- decode.field(3, decode.int) 120 decode.success(Post(id:, title:, content:, author_id:)) 121 } 122 123 sqlight.query(sql, on: conn, with: [], expecting: decoder) 124 |> result.unwrap([]) 125} 126 127/// Get a post by ID 128pub fn get_post(conn: sqlight.Connection, post_id: Int) -> Result(Post, String) { 129 let sql = "SELECT id, title, content, author_id FROM posts WHERE id = ?" 130 131 let decoder = { 132 use id <- decode.field(0, decode.int) 133 use title <- decode.field(1, decode.string) 134 use content <- decode.field(2, decode.string) 135 use author_id <- decode.field(3, decode.int) 136 decode.success(Post(id:, title:, content:, author_id:)) 137 } 138 139 case 140 sqlight.query( 141 sql, 142 on: conn, 143 with: [sqlight.int(post_id)], 144 expecting: decoder, 145 ) 146 { 147 Ok([post]) -> Ok(post) 148 Ok([]) -> Error("Post not found") 149 Ok(_) -> Error("Multiple posts found") 150 Error(err) -> Error("Database error: " <> string.inspect(err)) 151 } 152} 153 154/// Get posts by author ID 155pub fn get_posts_by_author( 156 conn: sqlight.Connection, 157 author_id: Int, 158) -> List(Post) { 159 let sql = 160 "SELECT id, title, content, author_id FROM posts WHERE author_id = ?" 161 162 let decoder = { 163 use id <- decode.field(0, decode.int) 164 use title <- decode.field(1, decode.string) 165 use content <- decode.field(2, decode.string) 166 use author_id <- decode.field(3, decode.int) 167 decode.success(Post(id:, title:, content:, author_id:)) 168 } 169 170 sqlight.query( 171 sql, 172 on: conn, 173 with: [sqlight.int(author_id)], 174 expecting: decoder, 175 ) 176 |> result.unwrap([]) 177} 178 179/// Create a new user 180pub fn create_user( 181 conn: sqlight.Connection, 182 name: String, 183 email: String, 184) -> Result(User, String) { 185 let sql = 186 "INSERT INTO users (name, email) VALUES (?, ?) RETURNING id, name, email" 187 188 let decoder = { 189 use id <- decode.field(0, decode.int) 190 use name <- decode.field(1, decode.string) 191 use email <- decode.field(2, decode.string) 192 decode.success(User(id:, name:, email:)) 193 } 194 195 case 196 sqlight.query( 197 sql, 198 on: conn, 199 with: [sqlight.text(name), sqlight.text(email)], 200 expecting: decoder, 201 ) 202 { 203 Ok([user]) -> Ok(user) 204 Ok([]) -> Error("Failed to create user") 205 Ok(_) -> Error("Unexpected result") 206 Error(err) -> Error("Database error: " <> string.inspect(err)) 207 } 208}