{"id":13566861,"url":"https://github.com/dbcls/grasp","last_synced_at":"2026-04-01T20:55:58.191Z","repository":{"id":40415477,"uuid":"235031813","full_name":"dbcls/grasp","owner":"dbcls","description":"Grasp: A bridge between GraphQL and SPARQL","archived":false,"fork":false,"pushed_at":"2026-02-17T08:34:32.000Z","size":478,"stargazers_count":42,"open_issues_count":12,"forks_count":11,"subscribers_count":9,"default_branch":"main","last_synced_at":"2026-02-17T13:33:01.562Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dbcls.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}},"created_at":"2020-01-20T06:15:19.000Z","updated_at":"2026-02-17T08:34:36.000Z","dependencies_parsed_at":"2024-01-06T13:11:25.379Z","dependency_job_id":"f05310dd-f49a-4340-8171-c218d0b53a59","html_url":"https://github.com/dbcls/grasp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dbcls/grasp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbcls%2Fgrasp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbcls%2Fgrasp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbcls%2Fgrasp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbcls%2Fgrasp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dbcls","download_url":"https://codeload.github.com/dbcls/grasp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbcls%2Fgrasp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31013964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-27T02:58:54.984Z","status":"ssl_error","status_checked_at":"2026-03-27T02:58:46.993Z","response_time":164,"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":[],"created_at":"2024-08-01T13:02:18.268Z","updated_at":"2026-03-27T03:15:19.603Z","avatar_url":"https://github.com/dbcls.png","language":"TypeScript","funding_links":[],"categories":["GraphQL","TypeScript"],"sub_categories":["SPARQL Applications"],"readme":"# Grasp: A bridge between GraphQL and SPARQL\n\n## What is Grasp?\n\nGrasp is a bridge software that provides a GraphQL endpoint wrapping SPARQL endpoints.\n\nGrasp works as follows:\n\n1. Grasp receives a GraphQL query from a client.\n2. Grasp translates it into a SPARQL query (or queries).\n3. Grasp sends the query to a SPARQL endpoint (or endpoints).\n4. The SPARQL endpoints return the results to Grasp.\n5. Grasp reforms the results to fit the given GraphQL query.\n6. Grasp sends the results back to the client.\n\n![](https://raw.githubusercontent.com/dbcls/grasp/master/docs/overview.svg?sanitize=true)\n\nWe need to define a GraphQL schema with some Grasp specific notations, which are carefully designed to keep full-compatibility with the GraphQL specification. More specifically, we need to define a SPARQL endpoint URL and a SPARQL query template per a *concept*, or a *type* in GraphQL terms. We also use GraphQL decorators for metadata (described later).\n\nLet's look at a simple example.\n\n## How to Use\n\n### Requirements\n\n* [Node.js](https://nodejs.org/) 24 or later\n\n### Install\n\n    $ git clone https://github.com/dbcls/grasp.git\n    $ cd grasp\n    $ npm install\n\n`npm install` automatically builds GraphiQL assets and writes them to `public/graphiql`.\nIf you need to rebuild them manually, run:\n\n    $ npm run build:graphiql-ui\n\n### Run\n\n    $ RESOURCES_DIR=./examples npm run watch\n\nThis loads the example resource definitions from `./example`.\n\nVisit http://localhost:4000. You will see GraphQL Playground.\n\nNote: You can change the port to listen on with `PORT` environment variable. Other configurations are described at  [Configuration](#configuration) section.\n\nWrite a GraphQL query below in the query editor (left pane):\n\n```graphql\nquery {\n  dataset(iri: \"http://purl.jp/bio/03/dbcatalog/nbdc00012\") {\n    iri\n    title_en\n    references {\n      iri\n      title\n    }\n  }\n}\n```\n\nThen, press Ctrl+Enter. The query will be issued and results will be shown (on right pane).\n\n![](https://raw.githubusercontent.com/dbcls/grasp/master/docs/graphql-playground.png)\n\nNow we've queried [Integbio Database Catalog/RDF](https://integbio.jp/rdf/?view=detail\u0026id=dbcatalog) with GraphQL. Let's see how it works.\n\n### Run in Docker container\n\n    $ docker run --rm -it -p 4000:4000 -v /full/path/to/resources:/app/resources ghcr.io/dbcls/grasp:\u003ctag\u003e\n\nAnd access `localhost:4000`. See available image tags at [dbcls/grasp](https://github.com/dbcls/grasp/pkgs/container/grasp).\n\n## How does this works?\n\nThe GraphQL query was translated into SPARQL queries and sent to a SPARQL endpoint, then the SPARQL results were returned to Grasp, finally the results were reformed into the GraphQL result.\n\nGrasp does those translation according to a GraphQL schema (type definition), SPARQL Endpoint URL and SPARQL query, which a Grasp admin (who sets up Grasp) provides. We refer to this as *resource* in Grasp. Let us dig into the definition.\n\nYou will see the resource definition at  [examples/dataset.graphql](https://github.com/dbcls/grasp/blob/master/examples/dataset.graphql).\n\nSPARQL Endpoint URL and SPARQL query are written in the GraphQL comment of the type in a special form. SPARQL Endpoint is specified after the `--- endpoint ---` line. SPARQL query is placed after the `--- sparql ---` line.\n\nThe query returns a RDF graph by the `CONSTRUCT` query form.  The graph has triples which consist of the IRI identifying the object, the predicate corresponding to the field name of the object, and its value.\n\nSee the first part of the SPARQL query:\n\n```sparql\nPREFIX : \u003chttps://github.com/dbcls/grasp/ns/\u003e\nPREFIX dcterms: \u003chttp://purl.org/dc/terms/\u003e\nPREFIX db: \u003chttp://purl.jp/bio/03/dbcatalog/\u003e\nPREFIX foaf: \u003chttp://xmlns.com/foaf/0.1/\u003e\nCONSTRUCT {\n  ?iri :iri ?iri .\n  ?iri :title_ja ?title_ja .\n  ?iri :title_en ?title_en .\n  # ...\n} WHERE {\n  # ...\n  {{#if iri}}VALUES ?iri { {{join \" \" (as-iriref iri)}} }{{/if}}\n}\n```\n\nHere, we can see that the `Dataset` object has fields `iri`, `title_ja` and `title_en`, where`iri` works as an identifier.\n\nWe use predicates with the special namespace (`https://github.com/dbcls/grasp/ns/`) in order to specify the field names.\n\nThe last part,\n\n```\n  {{#if iri}}VALUES ?iri { {{join \" \" (as-iriref iri)}} }{{/if}}\n```\n\nshould look complicated. Let us explain.\n\nIn the first place, the reason why this is needed is that *Grasp requires this SPARQL query to return certain triples*. More specifically, whose subject is any of `iri` (possibly `iri` contains multiple values).\n\nThe SPARQL query is actually written in [Handlebars](https://handlebarsjs.com/guide/) template. This part can be roughly interpreted as \"If `iri` is given, render the VALUE-clause to select bindings by the `iri` (possibly contains multiple IRIs): `VALUES ?iri {\u003chttp://example.com/...\u003e \u003chttp://example.com/...\u003e}`\".\n\n`if` is a built-in helper of Handlebars. The argument of `if` helper, in this case `iri`, is *falsy* (that is, not passed to the query), it isn't rendered.\n\n`join` is a helper defined by Grasp that concatenates the elements of the second argument using the first argument as the delimiter.\n\n`as-iriref` is a helper that wraps the elements of the second parameter with `\u003c` and `\u003e`.\n\nTaken together, this part consequently selects triples by `iri`, if `iri` given. For more about the use of Grasp-defined helpers, see the later section.\n\n\nAfter the comment block, we have `Dataset` GraphQL object type as follows. This corresponds to the above-mentioned SPARQL query.\n\n```graphql\ntype Dataset {\n  iri: String!\n  title_ja: String!\n  title_en: String\n```\n\nNote: `!` means that the field is non-nullable. See https://graphql.org/learn/schema/#lists-and-non-null\n for detail.\n\nNow we've defined the `Dataset` object type. In addition, we need to define a field to query type in order to fetch a `Dataset` with a GraphQL query. We're showing the corresponding part of [examples/index.graphql](https://github.com/dbcls/grasp/blob/master/examples/index.graphql):\n\n```graphql\ntype Query {\n  # ...\n  dataset(iri: String!): Dataset\n  # ...\n}\n```\n\nHere, we've defined the `dataset` field to fetch a `Dataset`.\n\n### Fields with multiple values\n\nSPARQL results may have multiple values for one field. In the previous example, as for `iri`, `title_ja` and `title_en`, Grasp returns only one value arbitrarily picked from found values.\n\nIf you want to have all values in GraphQL results, you need to use a list type in GraphQL schema:\n\n```\ntype SequenceStatisticsReport {\n  # ...\n  contributor: [String!]!\n  # ...\n}\n```\n\nWith this definition, you can all values for `contributor` field in a list of `String`s.\n\n### Relations\n\nYou might notice that we have `references` in a `Dataset` object in the GraphQL result:\n\n```javascript\n{\n  \"data\": {\n    \"dataset\": {\n      \"iri\": \"http://purl.jp/bio/03/dbcatalog/nbdc00012\",\n      \"title_en\": \"Atlas of Genetics and Cytogenetics in Oncology and Haematology\",\n      \"references\": [\n        {\n          \"iri\": \"http://rdf.ncbi.nlm.nih.gov/pubmed/12520000\",\n          \"title\": \"Atlas of Genetics and Cytogenetics in Oncology and Haematology, year 2003.\"\n        },\n        {\n          \"iri\": \"http://rdf.ncbi.nlm.nih.gov/pubmed/11125120\",\n          \"title\": \"Atlas of Genetics and Cytogenetics in Oncology and Haematology, updated.\"\n        },\n        ...\n      }\n    }\n  }\n}\n```\n\nIn  [examples/dataset.graphql](https://github.com/dbcls/grasp/blob/master/examples/dataset.graphql), `references` field is defined as follows:\n\n```graphql\ntype Dataset {\n   # ...\n   references: [Pubmed!]!\n   # ...\n}\n```\n\nIn this case, Grasp issues two SPARQL queries to complete a GraphQL query to fetch `Dataset` including all its `references` field. The first is to fetch a `Dataset`, with `references` having their IRIs. The second is to fetch `references` using the IRIs. The second query is processed according to your `Pubmed` resource definition ([examples/pubmed.graphql](https://github.com/dbcls/grasp/blob/master/examples/pubmed.graphql)). Grasp combines these SPARQL results from these queries into the final GraphQL response.\n\n### Treat blank nodes as relations\n\nWe cannot handle relations with a blank node in the previously mentioned way, as the blank node can't be pointed with an IRI. In order to handle such relations, we introduce *embedded resources*.\n\nConsider the following case:\n\n![](https://raw.githubusercontent.com/dbcls/grasp/master/docs/embedded.svg?sanitize=true)\n\nIn [examples/pubmed.graphql](https://github.com/dbcls/grasp/blob/master/examples/publisher.graphql), we have the following definition of `Publisher`:\n\n```graphql\ntype Dataset {\n  # ...\n  publisher: Publisher\n  # ...\n}\n```\n\nThe `Publisher` resource is defined as follows:\n\n```graphql\ntype Publisher @embedded {\n  name_ja: String\n  name_en: String\n  page: String\n}\n```\n\nNote that `Publisher` doesn't have a special comment containing an endpoint definition and SPARQL query, and that we marked `Publisher` as `@embedded`. When resolving resources with `@embedded`, Grasp doesn't fetch the resource with a separate query. Instead, fills the fields from the query of its parent resource.\n\nThe following query fetches `Dataset`. In this query, values of `publisher` are fetched all together:\n\n```sparql\nCONSTRUCT {\n  ?iri :iri ?iri .\n  ?iri :title_ja ?title_ja .\n  # ...\n  ?iri :publisher ?publisher .\n  ?publisher :name_ja ?publisher_name_ja .\n  ?publisher :name_en ?publisher_name_en .\n  ?publisher :page ?publisher_page .\n  # ...\n}\n# ...\nWHERE\n{\n  # ...\n  OPTIONAL { ?iri dcterms:publisher ?publisher . }\n  OPTIONAL { ?publisher rdfs:label ?publisher_name_ja . FILTER (lang(?publisher_name_ja) = \"ja\") }\n  OPTIONAL { ?publisher rdfs:label ?publisher_name_en . FILTER (lang(?publisher_name_en) = \"en\") }\n  OPTIONAL { ?publisher foaf:page ?publisher_page . }\n  # ...\n}\n```\n\nThis query returns triples representing a `Dataset` and representing the `Publisher` of the `Dataset` at the same time. Grasp distinguish them by their subjects (of a graph generated by the `CONSTRUCT` query form) and build GraphQL objects.\n\nNote that we need to return graph containing triples whose 1) subject points the embedded resource and 2) predicate reflects its field name. `?publisher` is bound to the blank node representing the `Publisher`.\n\n\n## Write your own definition\n\nYou can add your own definitions in the directory specified with `RESOURCES_DIR` (default is `./resources`).\nThe resource definition files must start with `[0-9a-zA-Z]` and end with `.graphql`. The other files in the directory are ignored.\n\nYou need to restart Grasp to reload the definitions. You can use `npm run watch` to restart the server automatically.\n\n## Advanced Topics\n\n### Filter triples by parameters other than IRIs in SPARQL Query\n\nConsider that you want to query a `Dataset` with an `id` (like `dcterms:identifier`). Add the following field definition into the `Query`:\n\n```graphql\ntype Query {\n  # ...\n  datasetById(id: String): Dataset\n}\n```\n\nIf we issue the following GraphQL query:\n\n``` graphql\nquery {\n  datasetById(id: \"NBDC00012\") {\n    ...\n  }\n}\n```\n\nThe SPARQL template is written in Handlebars.\nIn this case, we can obtain `\"NBDC00012\"` with `{{id}}` notation in the SPARQL query.\nYou can also use Handlebars' built-in helpers such as `if` and `each`.\nSee https://handlebarsjs.com/guide/ for details.\n\n```hbs\nWHERE\n{\n  ?iri dcterms:identifier ?id .\n  {{#if id}}\n    ?iri dcterms:identifier \"{{id}}\" .\n  {{/if}}\n}\n```\n\n### Handling multiple values in query templates\n\nConsider you are defining a GraphQL query, which takes multiple values as a parameter.\nThe field in Query should be like below:\n\n```graphql\ntype Query {\n  datasetsByIds(ids: [String!]): [Dataset!]!\n}\n```\n\nHere, you want to issue a SPARQL query like:\n\n```sparql\nVALUES ?id { \"NBDC00012\" \"NBDC00013\" }\n```\n\nIn this case, instead of doing simple interpolation (as previously mentioned),\nyou can also use `join` and `as-strings` helpers:\n\n```hbs\nWHERE\n{\n  ?iri dcterms:identifier ?id .\n  {{#if ids}}\n    VALUES ?id { {{join \" \" (as-string ids)}} }\n  {{/if}}\n}\n```\n\nHere, `as-string` wraps elements of the given array with `\"` (double-quotations).\n`join` combines them using the first argument as the delimiter.\n\nConsider another example of generating an `IN` clause from a parameter like this:\n\n```sparql\nFILTER (?iri IN (\u003chttp://...\u003e, \u003chttp://...\u003e))\n```\n\nYou can write the template using helpers as follows:\n\n```hbs\nWHERE\n{\n  {{#if iris}}\n    FILTER (?iri IN ({{join \", \" (as-iriref iris)}}))\n  {{/if}}\n}\n```\n\nNote that we've specified `, ` as the delimiter for `join`.\n`as-iriref` works almost same as `as-string` except wrapping the elements with `\u003c` and `\u003e`.\n\n\n## Configuration\n\nGrasp can be configured with the following environment variables.\n\n### `PORT`\n\n(default: `4000`)\n\nPort to listen on.\n\n### `ROOT_PATH`\n\n(default: `/`)\n\nIf you want to run Grasp on other than `/` (say, `/foo`), configure `ROOT_PATH` to point the path.\n\n### `MAX_BATCH_SIZE`\n\n(default: Infinity)\n\nGrasp issues queries in batches to reduce number of queries. This may result in too large query to be processed by some SPARQL endpoints. You can use `MAX_BATCH_SIZE` in order to avoid this problem by restricting the number of items to fetch.\n\n### `RESOURCES_DIR`\n\n(default: `resources`)\n\nLoad resources from the specified directory.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdbcls%2Fgrasp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdbcls%2Fgrasp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdbcls%2Fgrasp/lists"}