{"id":48852095,"url":"https://github.com/qwexvf/mochi","last_synced_at":"2026-04-15T10:01:16.766Z","repository":{"id":308256656,"uuid":"1031900498","full_name":"qwexvf/mochi","owner":"qwexvf","description":"Code First GraphQL library for Gleam with TypeScript and SDL codegen","archived":false,"fork":false,"pushed_at":"2026-04-13T16:30:59.000Z","size":17319,"stargazers_count":1,"open_issues_count":14,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-13T18:25:33.394Z","etag":null,"topics":["apollo-federation","beam","code-first","gleam","graphql","graphql-schema","graphql-server"],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/mochi/","language":"Gleam","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qwexvf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-04T13:56:06.000Z","updated_at":"2026-04-13T16:32:16.000Z","dependencies_parsed_at":"2025-08-05T01:28:57.738Z","dependency_job_id":null,"html_url":"https://github.com/qwexvf/mochi","commit_stats":null,"previous_names":["qwexvf/geql","qwexvf/mochi"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/qwexvf/mochi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Fmochi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Fmochi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Fmochi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Fmochi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qwexvf","download_url":"https://codeload.github.com/qwexvf/mochi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qwexvf%2Fmochi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31835820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T07:17:56.427Z","status":"ssl_error","status_checked_at":"2026-04-15T07:17:30.007Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apollo-federation","beam","code-first","gleam","graphql","graphql-schema","graphql-server"],"created_at":"2026-04-15T10:01:05.135Z","updated_at":"2026-04-15T10:01:16.758Z","avatar_url":"https://github.com/qwexvf.png","language":"Gleam","readme":"# mochi - Code First GraphQL for Gleam\n\n[![Package Version](https://img.shields.io/hexpm/v/mochi)](https://hex.pm/packages/mochi)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/mochi/)\n\n**mochi** is a type-safe, Code First GraphQL library for Gleam. Define your GraphQL schemas using Gleam types and automatically generate TypeScript types and GraphQL SDL.\n\nInspired by:\n- [Absinthe](https://github.com/absinthe-graphql/absinthe) - The GraphQL toolkit for Elixir\n- [dataloader](https://github.com/graphql/dataloader) - DataLoader pattern for batching and caching\n- [gqlkit](https://github.com/izumin5210/gqlkit) - Write TypeScript, generate GraphQL\n- [Pothos](https://github.com/hayes/pothos) - Code First GraphQL for TypeScript\n- [TypeGraphQL](https://github.com/MichalLytek/type-graphql) - Code First GraphQL with TypeScript decorators\n- [Nexus](https://github.com/graphql-nexus/nexus) - Declarative, Code First GraphQL for TypeScript\n\n## Installation\n\n```sh\ngleam add mochi\n```\n\n## Quick Start\n\n```gleam\nimport mochi/query\nimport mochi/schema\nimport mochi/types\n\n// 1. Define your Gleam types\npub type User {\n  User(id: String, name: String, email: String, age: Int)\n}\n\n// 2. Create GraphQL type with type-safe field extractors\nfn user_type() -\u003e schema.ObjectType {\n  types.object(\"User\")\n  |\u003e types.description(\"A user in the system\")\n  |\u003e types.id(\"id\", fn(u: User) { u.id })\n  |\u003e types.string(\"name\", fn(u: User) { u.name })\n  |\u003e types.string(\"email\", fn(u: User) { u.email })\n  |\u003e types.int(\"age\", fn(u: User) { u.age })\n  |\u003e types.build(decode_user)\n}\n\n// 3. Define queries\nfn users_query() {\n  query.query(\n    \"users\",\n    schema.list_type(schema.named_type(\"User\")),\n    fn(_ctx) { Ok(get_users()) },\n    fn(users) { types.to_dynamic(users) },\n  )\n}\n\nfn user_query() {\n  query.query_with_args(\n    name: \"user\",\n    args: [query.arg(\"id\", schema.non_null(schema.id_type()))],\n    returns: schema.named_type(\"User\"),\n    decode: fn(args) { query.get_id(args, \"id\") },\n    resolve: fn(id, _ctx) { get_user_by_id(id) },\n    encode: types.to_dynamic,\n  )\n}\n\n// 4. Build the schema\npub fn create_schema() -\u003e schema.Schema {\n  query.new()\n  |\u003e query.add_query(users_query())\n  |\u003e query.add_query(user_query())\n  |\u003e query.add_type(user_type())\n  |\u003e query.build\n}\n\n// 5. Execute queries\npub fn main() {\n  let my_schema = create_schema()\n  let result = mochi.execute(my_schema, \"{ users { id name } }\")\n}\n```\n\n## Performance\n\nMochi is built for performance on the BEAM VM.\n\n**Test System:** AMD Ryzen 7 PRO 7840U (8 cores, 16 threads) | 64GB RAM | Docker containers (no resource limits)\n\n### Simple Query: `{ users { id name } }`\n\n| Server | Runtime | Req/sec | Latency (avg) |\n|--------|---------|---------|---------------|\n| **Mercurius** | Node.js + Fastify | 36,485 | 2.28ms |\n| **Mochi** | Gleam/Erlang | 27,770 | 3.13ms |\n| **Bun + Yoga** | Bun | 19,067 | 4.68ms |\n| **GraphQL Yoga** | Node.js | 13,798 | 6.78ms |\n| **Apollo Server** | Node.js | 6,659 | 14.53ms |\n| **graphql-js** | Node.js (reference) | 4,604 | 21.21ms |\n\n### Medium Query: `{ users { id name email posts { id title } } }`\n\n| Server | Runtime | Req/sec | Latency (avg) |\n|--------|---------|---------|---------------|\n| **Mercurius** | Node.js + Fastify | 32,696 | 2.50ms |\n| **Mochi** | Gleam/Erlang | 25,850 | 3.39ms |\n| **Bun + Yoga** | Bun | 14,854 | 6.25ms |\n| **GraphQL Yoga** | Node.js | 11,705 | 8.09ms |\n| **Apollo Server** | Node.js | 5,286 | 18.43ms |\n| **graphql-js** | Node.js (reference) | 3,463 | 28.37ms |\n\nMochi achieves **4x faster** performance than Apollo Server and **6x faster** than the reference graphql-js implementation, while providing:\n\n- **Type safety** - Full compile-time guarantees from Gleam\n- **Fault tolerance** - BEAM VM supervision and process isolation\n- **Scalability** - Leverages Erlang's lightweight process model\n- **Zero N+1** - Built-in DataLoader support\n\nRun benchmarks yourself:\n```bash\ncd examples/mochi_wisp/benchmark\ndocker compose up -d --build\n./run-host-bench.sh\n```\n\n## Features\n\n- **Code First Schema Definition** - Define GraphQL schemas using Gleam types with type-safe field extractors\n- **TypeScript Codegen** - Generate `.d.ts` type definitions from your schema\n- **SDL Generation** - Generate `.graphql` schema files\n- **Query Validation** - Validate GraphQL queries against your schema\n- **Custom Directives** - Define and execute custom directives with handlers\n- **@deprecated Support** - Mark fields and enum values as deprecated\n- **Interface Types** - Define GraphQL interfaces with type resolution\n- **Union Types** - Define union types with runtime type resolution\n- **Fragment Support** - Full support for GraphQL fragments\n- **Subscriptions** - Real-time updates with PubSub pattern\n- **Error Extensions** - GraphQL-spec compliant errors with locations, path, and extensions\n- **JSON Serialization** - Built-in JSON encoding for responses\n- **Null Propagation** - Proper null bubbling per GraphQL specification\n- **DataLoader** - N+1 query prevention with automatic batching and caching\n- **Query Security** - Depth limiting, complexity analysis, alias limits\n- **Persisted Queries** - Automatic Persisted Queries (APQ) with SHA256 hashing\n- **GraphQL Playgrounds** - Built-in GraphiQL, Playground, Apollo Sandbox, and simple explorer\n- **BEAM Powered** - Built for performance on Erlang/OTP\n- **Zero Config** - Simple, intuitive API with sensible defaults\n\n## TypeScript Codegen\n\nGenerate TypeScript type definitions from your schema:\n\n```gleam\nimport mochi/codegen/typescript\n\nlet ts_code = typescript.generate(schema)\n// Write to: types.generated.ts\n```\n\n**Output:**\n\n```typescript\n// Generated by mochi\n\nexport type Maybe\u003cT\u003e = T | null | undefined;\n\nexport type Scalars = {\n  ID: string;\n  String: string;\n  Int: number;\n  Float: number;\n  Boolean: boolean;\n};\n\nexport interface User {\n  id: Scalars[\"ID\"];\n  name?: Maybe\u003cScalars[\"String\"]\u003e;\n  email?: Maybe\u003cScalars[\"String\"]\u003e;\n  age?: Maybe\u003cScalars[\"Int\"]\u003e;\n}\n\nexport interface QueryUserArgs {\n  id: Scalars[\"ID\"];\n}\n\nexport interface Query {\n  user(args: QueryUserArgs): Maybe\u003cUser\u003e;\n  users: Maybe\u003cMaybe\u003cUser\u003e[]\u003e;\n}\n```\n\n## SDL Generation\n\nGenerate GraphQL SDL from your schema:\n\n```gleam\nimport mochi/codegen/sdl\n\nlet graphql_schema = sdl.generate(schema)\n// Write to: schema.graphql\n```\n\n**Output:**\n\n```graphql\n# Generated by mochi\n\n\"A user in the system\"\ntype User {\n  id: ID!\n  name: String\n  email: String\n  age: Int\n}\n\ntype Query {\n  \"Get a user by ID\"\n  user(id: ID!): User\n  \"Get all users\"\n  users: [User]!\n}\n```\n\n## API Reference\n\n### Type Builders (`mochi/types`)\n\nBuild GraphQL object types with type-safe field extractors. See module docs for full API.\n\n```gleam\nimport mochi/types\n\n// Object type with field extractors\nlet user_type = types.object(\"User\")\n  |\u003e types.id(\"id\", fn(u: User) { u.id })\n  |\u003e types.string(\"name\", fn(u: User) { u.name })\n  |\u003e types.int(\"age\", fn(u: User) { u.age })\n  |\u003e types.build(decode_user)\n\n// Enum type\nlet role_enum = types.enum_type(\"Role\")\n  |\u003e types.value(\"ADMIN\")\n  |\u003e types.value(\"USER\")\n  |\u003e types.build_enum\n\n// Dynamic conversion helpers for DataLoader encoders\nfn user_to_dynamic(u: User) -\u003e Dynamic {\n  types.record([\n    types.field(\"id\", u.id),\n    types.field(\"name\", u.name),\n    #(\"age\", types.option(u.age)),  // Option -\u003e null if None\n  ])\n}\n```\n\n### Query Builders (`mochi/query`)\n\nDefine queries and mutations with type-safe resolvers. See module docs for full API.\n\n```gleam\nimport mochi/query\n\n// Query with arguments (labeled for clarity)\nlet user_query = query.query_with_args(\n  name: \"user\",\n  args: [query.arg(\"id\", schema.non_null(schema.id_type()))],\n  returns: schema.named_type(\"User\"),\n  decode: fn(args) { query.get_id(args, \"id\") },\n  resolve: fn(id, ctx) { get_user_by_id(id) },\n  encode: types.to_dynamic,\n)\n\n// Build schema\nlet my_schema = query.new()\n  |\u003e query.add_query(user_query)\n  |\u003e query.add_type(user_type)\n  |\u003e query.build\n```\n\n### Argument Parsing Helpers\n\nConvenient helpers for extracting arguments in resolvers:\n\n```gleam\n// Required arguments (return Result)\nquery.get_string(args, \"name\")   // -\u003e Result(String, String)\nquery.get_id(args, \"id\")         // -\u003e Result(String, String)\nquery.get_int(args, \"age\")       // -\u003e Result(Int, String)\nquery.get_float(args, \"price\")   // -\u003e Result(Float, String)\nquery.get_bool(args, \"active\")   // -\u003e Result(Bool, String)\n\n// Optional arguments (return Option)\nquery.get_optional_string(args, \"filter\")  // -\u003e Option(String)\nquery.get_optional_int(args, \"limit\")      // -\u003e Option(Int)\n\n// List arguments\nquery.get_string_list(args, \"tags\")  // -\u003e Result(List(String), String)\nquery.get_int_list(args, \"ids\")      // -\u003e Result(List(Int), String)\n```\n\n### Schema Module (`mochi/schema`)\n\nLow-level schema building and type definitions. See module docs for full API.\n\n```gleam\nimport mochi/schema\n\n// Field types\nschema.string_type()      // String\nschema.int_type()         // Int\nschema.list_type(inner)   // [Type]\nschema.non_null(inner)    // Type!\nschema.named_type(\"User\") // Custom type\n\n// Interface and union types\nlet node = schema.interface(\"Node\")\n  |\u003e schema.interface_field(schema.field_def(\"id\", schema.non_null(schema.id_type())))\n\nlet search = schema.union(\"SearchResult\")\n  |\u003e schema.union_member(user_type)\n  |\u003e schema.union_member(post_type)\n```\n\n### Custom Directives\n\nDefine custom directives for your schema. See module docs for full API.\n\n```gleam\nimport mochi/schema\n\nlet auth = schema.directive(\"auth\", [schema.FieldLocation])\n  |\u003e schema.directive_argument(schema.arg(\"role\", schema.string_type()))\n  |\u003e schema.directive_handler(fn(args, value) { Ok(value) })\n```\n\n### Subscriptions (`mochi/subscription`)\n\nReal-time updates with a PubSub pattern. See module docs for full API.\n\n```gleam\nimport mochi/subscription\n\nlet pubsub = subscription.new_pubsub()\nlet result = subscription.subscribe(pubsub, \"user:created\", \"onUserCreated\", dict.new(), handler)\nsubscription.publish(result.pubsub, \"user:created\", user_data)\n```\n\n### Error Handling (`mochi/error`)\n\nGraphQL-spec compliant errors with extensions. See module docs for full API.\n\n```gleam\nimport mochi/error\n\nlet err = error.error(\"Something went wrong\")\n  |\u003e error.with_code(\"INTERNAL_ERROR\")\n  |\u003e error.with_extension(\"retryAfter\", types.to_dynamic(60))\n```\n\n### Response Handling (`mochi/response`)\n\nConstruct and serialize GraphQL responses. See module docs for full API.\n\n```gleam\nimport mochi/response\n\nlet resp = response.from_execution_result(exec_result)\nlet json_string = response.to_json(resp)\n```\n\n### JSON Serialization (`mochi/json`)\n\nBuilt-in JSON encoding. See module docs for full API.\n\n```gleam\nimport mochi/json\n\nlet json_string = json.encode(dynamic_value)\n```\n\n### Query Security (`mochi/security`)\n\nProtect against malicious queries. See module docs for full API.\n\n```gleam\nimport mochi/security\n\ncase security.validate(document, security.default_config()) {\n  Ok(_) -\u003e execute_query(document)\n  Error(err) -\u003e error_response(err)\n}\n```\n\n### Persisted Queries (`mochi/persisted_queries`)\n\nAutomatic Persisted Queries (APQ) for caching and security. See module docs for full API.\n\n```gleam\nimport mochi/persisted_queries as apq\n\nlet store = apq.new_store()\ncase apq.process_apq(store, query_opt, hash_opt) {\n  apq.ExecuteQuery(query, new_store) -\u003e execute(query)\n  apq.PersistedQueryNotFound -\u003e error_response(\"PersistedQueryNotFound\")\n  apq.HashMismatch(_, _) -\u003e error_response(\"Hash mismatch\")\n}\n```\n\n### GraphQL Playgrounds (`mochi/playground`)\n\nBuilt-in interactive GraphQL explorers.\n\n```gleam\nimport mochi/playground\n\nplayground.graphiql(\"/graphql\")      // GraphiQL IDE\nplayground.playground(\"/graphql\")    // GraphQL Playground\nplayground.apollo_sandbox(\"/graphql\") // Apollo Sandbox\nplayground.simple_explorer(\"/graphql\") // Lightweight explorer\n```\n\n### WebSocket Transport (`mochi/transport/websocket`)\n\nReal-time subscriptions over WebSocket using the graphql-ws protocol. See module docs for full API.\n\n```gleam\nimport mochi/transport/websocket\n\nlet state = websocket.new_connection(schema, pubsub, execution_context)\nlet result = websocket.handle_message(state, client_msg)\n```\n\n### DataLoader (`mochi/dataloader`)\n\nPrevent N+1 queries with automatic batching. See module docs for full API.\n\n```gleam\nimport mochi/dataloader\nimport mochi/schema\n\n// Create loader from find function (one-liner)\nlet pokemon_loader = dataloader.int_loader_result(\n  data.find_pokemon, pokemon_to_dynamic, \"Pokemon not found\",\n)\n\n// Register loaders and load data\nlet ctx = schema.execution_context(types.to_dynamic(dict.new()))\n  |\u003e schema.with_loaders([#(\"pokemon\", pokemon_loader)])\n\nlet #(ctx, result) = schema.load_by_id(ctx, \"pokemon\", 25)\n```\n\n### Codegen Configuration\n\nCustomize code generation output. See module docs for full API.\n\n```gleam\nimport mochi/codegen/typescript\nimport mochi/codegen/sdl\n\nlet ts_code = typescript.generate(schema)\nlet graphql_code = sdl.generate(schema)\n```\n\n## Examples\n\nSee the [`examples/`](examples/) directory for complete working examples:\n\n- **`code_first_example.gleam`** - Basic User/Post schema with queries and mutations\n- **`core_library_examples/`** - Pure GraphQL functionality demonstrations\n- **`mochi_wisp/`** - Full web application with Wisp framework\n\n### Basic Schema Example\n\n```gleam\nimport mochi/query\nimport mochi/schema\nimport mochi/types\n\npub type User {\n  User(id: String, name: String, email: String)\n}\n\npub type Post {\n  Post(id: String, title: String, author_id: String)\n}\n\nfn user_type() -\u003e schema.ObjectType {\n  types.object(\"User\")\n  |\u003e types.id(\"id\", fn(u: User) { u.id })\n  |\u003e types.string(\"name\", fn(u: User) { u.name })\n  |\u003e types.string(\"email\", fn(u: User) { u.email })\n  |\u003e types.build(fn(_) { Ok(User(\"\", \"\", \"\")) })\n}\n\nfn post_type() -\u003e schema.ObjectType {\n  types.object(\"Post\")\n  |\u003e types.id(\"id\", fn(p: Post) { p.id })\n  |\u003e types.string(\"title\", fn(p: Post) { p.title })\n  |\u003e types.string(\"authorId\", fn(p: Post) { p.author_id })\n  |\u003e types.build(fn(_) { Ok(Post(\"\", \"\", \"\")) })\n}\n\npub fn create_schema() -\u003e schema.Schema {\n  query.new()\n  |\u003e query.add_query(query.query(\n    \"users\",\n    schema.list_type(schema.named_type(\"User\")),\n    fn(_) { Ok([]) },\n    types.to_dynamic,\n  ))\n  |\u003e query.add_type(user_type())\n  |\u003e query.add_type(post_type())\n  |\u003e query.build\n}\n```\n\n### Mutation Example\n\n```gleam\npub type CreateUserInput {\n  CreateUserInput(name: String, email: String)\n}\n\nfn create_user_mutation() {\n  query.mutation(\n    name: \"createUser\",\n    args: [query.arg(\"input\", schema.non_null(schema.named_type(\"CreateUserInput\")))],\n    returns: schema.named_type(\"User\"),\n    decode: decode_create_input,\n    resolve: fn(input, ctx) {\n      // Insert into database\n      let user = User(id: generate_id(), name: input.name, email: input.email)\n      db.insert_user(user)\n      Ok(user)\n    },\n    encode: types.to_dynamic,\n  )\n  |\u003e query.mutation_description(\"Create a new user\")\n}\n\nlet schema = query.new()\n  |\u003e query.add_mutation(create_user_mutation())\n  |\u003e query.build\n```\n\n## Module Structure\n\n```\nmochi/\n|-- query.gleam              # Query/Mutation/Subscription builders (Code First API)\n|-- types.gleam              # Type builders (object, enum, fields)\n|-- schema.gleam             # Core schema types and low-level API\n|-- parser.gleam             # GraphQL query parser\n|-- executor.gleam           # Query execution engine with null propagation\n|-- validation.gleam         # Query validation\n|-- subscription.gleam       # Subscription PubSub system\n|-- subscription_executor.gleam  # Subscription execution\n|-- error.gleam              # GraphQL-spec compliant errors\n|-- response.gleam           # Response construction and serialization\n|-- json.gleam               # JSON encoding\n|-- dataloader.gleam         # N+1 query prevention\n|-- security.gleam           # Query security (depth, complexity limits)\n|-- persisted_queries.gleam  # Automatic Persisted Queries (APQ)\n|-- playground.gleam         # GraphQL IDE HTML generators\n|-- codegen/\n|   |-- typescript.gleam     # TypeScript .d.ts generation\n|   +-- sdl.gleam            # GraphQL SDL generation\n|-- transport/\n|   +-- websocket.gleam      # WebSocket transport (graphql-ws protocol)\n+-- ...\n```\n\n## Running Tests\n\n```sh\ngleam test\n```\n\n## Development\n\n```sh\ngleam build          # Build the library\ngleam test           # Run all tests\ngleam run            # Run demo\n```\n\n## License\n\nApache 2.0\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqwexvf%2Fmochi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqwexvf%2Fmochi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqwexvf%2Fmochi/lists"}