Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ryan-haskell/elm-graphql-server
Create a GraphQL API with Elm!
https://github.com/ryan-haskell/elm-graphql-server
apollo backend elm graphql sql
Last synced: 23 days ago
JSON representation
Create a GraphQL API with Elm!
- Host: GitHub
- URL: https://github.com/ryan-haskell/elm-graphql-server
- Owner: ryan-haskell
- Created: 2022-03-18T22:59:28.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2023-03-31T20:44:49.000Z (over 1 year ago)
- Last Synced: 2024-11-27T09:29:31.546Z (26 days ago)
- Topics: apollo, backend, elm, graphql, sql
- Language: Elm
- Homepage:
- Size: 530 KB
- Stars: 8
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# @ryannhg/elm-graphql-server
> Create a GraphQL API with Elm!![A screenshot of a demo query in the GraphQL playground](./screenshot.png)
## Playing around locally
_You'll need [Node.js](https://nodejs.org) to run this project on your local computer._
```bash
npm start
```- Visit http://localhost:4000 to use the GraphQL API
- Mess with Elm files in `src/Resolvers` to change how the API responds## Overview
The server starts in a file called `src/index.js`. It is powered by an Elm program, compiled
as a `Platform.worker`. That means you'll need to compile your Elm app first with `npm run elm:build`, or you can use the `npm run dev` script that automatically compiles things as you code!Right now, the API server doesn't do too much. It listens for GraphQL requests at `http://localhost:4000`, and sends that request information to our Elm worker, so we can handle our resolvers with Elm code.
For now, you can modify the GraphQL schema by editing `src/schema.gql`, and add a new resolver in `src/Worker.elm`.
I have been making modules in `src/Resolvers` to organize the code, but that's just so you can easily follow what's going on!
This is not a production-ready thing, but I thought it would be a fun experiment to see what using the Elm language would be like for a backend GraphQL API.
A real implementation would require a nice way to talk to a database, a third-party HTTP service, or do things like application logging– but for now this is all we have 🙂
### Talking to a SQL Database
I've been exploring communicating to a `sqlite` database. This project supports basic forms of:
- [x] `SELECT` statements
- Example of `findOne` with the [`user` query](./src/Resolvers/Query/User.elm)
- Example of `findAll` with the [`users` query](./src/Resolvers/Query/Users.elm)
- [x] `INSERT` statements
- Example of `insertOne` with the [`createUser` mutation](./src/Resolvers/Mutation/CreateUser.elm)
- [x] `UPDATE` statements
- Example of `updateOne` with the [`updateUser` mutation](./src/Resolvers/Mutation/UpdateUser.elm)
- [x] `DELETE` statements
- Example of `deleteOne` with the [`deleteUser` mutation](./src/Resolvers/Mutation/DeleteUser.elm)### Solving the N + 1 problem
This repo also features examples of queries that need to access data across multiple tables. For example, if a user creates a post, the database looks something like this:
#### `users`
id | username | avatarUrl
--- | --- | ---
1 | "ryan" | NULL#### `posts`
id | caption | imageUrls
--- | --- | ---
3 | "Elm conf 2019 was sweet!" | [ ... ]#### `user_authored_post`
id | postId | userId
--- | --- | ---
5 | 3 | 1A user of our GraphQL API might write a query like this, to get the `id` and `username` of the author of post #3:
```graphql
query {
post(id: 3) {
id
caption
author {
id
username
}
}
}
```When this GraphQL query comes through [the Query.post resolver](./src/Resolvers/Query/Post.elm) runs a few SQL statements:
```sql
-- Get the data stored for that post
SELECT id, caption FROM posts WHERE id = 3;
``````sql
-- Check the "user_authored_post" table for any authors
SELECT userId FROM user_authored_post WHERE postId = 3;
``````sql
-- The last query found a row with userId = 1,
-- so we pass that into the final SQL query that
-- fetches the `id` and `username` for our author
SELECT id, username FROM users WHERE id = 1;
```If we need 3 SQL statements for one post and its author, how does this work when fetching multiple posts?
```graphql
query {
posts {
id
caption
author {
id
username
}
}
}
```This GraphQL request will be sent to [our Query.posts resolver](./src/Resolvers/Query/Posts.elm), which lists the first 25 posts in the system. If we repeated the previous strategy for each of those posts– that would mean 3 * 25... 75 SQL queries!?
Luckily for us, all our resolvers can access [an `info` argument for the GraphQL query](https://graphql.org/learn/execution/#root-fields-resolvers). This allows us to perform these queries in bulk– using a similar approach to Facebook's DataLoader!
Rather than making a query for each post, we use SQL's `IN` keyword to fetch author data for all 25 posts using only 3 SQL statements! (This would still only need 3 queries if we brought back 10, 100, or 1000 posts):
```sql
-- First, we ask for the first 25 posts
SELECT id, caption FROM posts LIMIT 25;
``````sql
-- Now that we have all those posts, we use their IDs
-- to query the `user_authored_post` table:
SELECT postId, userId FROM user_authored_post WHERE postId IN ( 1, 2, 3, ... 25 );
``````sql
-- Once we have all those edges, we can use the userId column
-- to do one final SQL query for the author data for all those author ids
SELECT id, username FROM users WHERE id IN ( 1, 2, 3, 4, ... );
```Using this `WHERE id IN ...` strategy helps us avoid a common GraphQL pitfall called the [N + 1 problem](https://www.youtube.com/watch?v=ld2_AS4l19g). Try making nested queries, and see the SQL that is generated!