https://github.com/hypermedia-app/sparqlc
https://github.com/hypermedia-app/sparqlc
Last synced: 3 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/hypermedia-app/sparqlc
- Owner: hypermedia-app
- Created: 2026-02-20T13:52:15.000Z (4 months ago)
- Default Branch: master
- Last Pushed: 2026-02-24T20:18:41.000Z (4 months ago)
- Last Synced: 2026-02-24T20:54:40.336Z (4 months ago)
- Language: TypeScript
- Size: 271 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# sparqlc monorepo
A set of tools for compiling and using SPARQL queries as first‑class modules across Node.js, Vite, esbuild, and TypeScript.
Packages (in usage order):
- `sparqlc` – core compiler and runtime (JS API + CLI)
- `node-loader-sparql` – Node.js loader to import `.rq` files directly
- `vite-plugin-sparql` – Vite plugin to import `.rq` in web apps
- `esbuild-plugin-sparql` – esbuild plugin to import `.rq`
- `ts-plugin-sparqlc` – TypeScript language service plugin to get strong types for `.rq` imports
## Installation
In most projects you do not install `sparqlc` directly. Instead, add the high‑level integration you use — it will pull `sparqlc` as a dependency:
- Node.js (import `.rq` in Node):
```sh
npm i -D node-loader-sparql
```
- Vite (web apps):
```sh
npm i -D vite-plugin-sparql
```
- esbuild:
```sh
npm i -D esbuild-plugin-sparql
```
- TypeScript editor types for `.rq` imports:
```sh
npm i -D ts-plugin-sparqlc
```
Only install `sparqlc` itself if you want to call the core API or use the CLI directly:
```sh
npm i sparqlc
```
Below you’ll find usage for each package.
---
## 1) `sparqlc` (core)
`sparqlc` compiles a SPARQL string into a small executable function with:
- `code` – the emitted function source (stringified)
- `returnType` – inferred SPARQL result kind: `Select | Construct | Ask | Update | unknown`
- `execute` – a function that, when given parameters and an executor `{ env, client, processors }`, will either:
- return a SPARQL string (when no `client` is provided), or
- execute the query using `sparql-http-client` and return typed results based on `returnType`
### API usage
```ts
import { compile } from 'sparqlc'
import env from '@zazuko/env'
import { StreamClient } from 'sparql-http-client'
const source = `
PREFIX foaf:
SELECT ?name WHERE { ?s foaf:name ?name }
LIMIT 10
`
const { execute, returnType } = compile(source)
console.log(returnType) // e.g. 'Select'
// Build executor options
const client = new StreamClient({ endpointUrl: 'https://dbpedia.org/sparql' })
// Optional parameterization examples (URLSearchParams, object, or Map)
const params = { name: env.literal('Alice') }
// Execute:
const rows = await execute(params, { env, client })
// If you omit `client`, execute(...) returns the final SPARQL string instead
```
### Reference parameters with `sparqlc:param`
You can declare and consume query parameters inside `.rq` files using the RDF property/function identified by `https://sparqlc.described.at/param` (IRI). Use a prefix for convenience:
```sparql
PREFIX sparqlc:
```
- (Recommended) Function style: `sparqlc:param("name")`
- Can be easily inlined in `BIND`, `FILTER`, or anywhere an expression is allowed.
- Example:
```sparql
PREFIX foaf:
PREFIX sparqlc:
SELECT ?name ?age WHERE {
?s foaf:name ?name ; foaf:age ?age .
FILTER (?age >= sparqlc:param("minAge"))
}
```
- Pattern style: `?variable sparqlc:param "name" .`
- This binds the value of the parameter `"name"` to the SPARQL variable `?variable`.
- Example:
```sparql
PREFIX foaf:
PREFIX sparqlc:
SELECT ?person WHERE {
?name sparqlc:param "person" .
?person a foaf:Person ;
foaf:name ?name .
}
```
Runtime call:
```ts
import env from '@zazuko/env'
const rows = await execute({ person: env.namedNode('http://example.com/alice') }, { env, client })
```
Passing parameters at runtime
- You may pass parameters as:
- `Record`: `{ person: env.namedNode('...'), minAge: env.literal('18', xsdInteger) }`
- `URLSearchParams`: `new URLSearchParams([["person", "http://example.com/alice"], ["minAge", "18"]])` (simple string values)
- `Map` for full control
- Values are RDFJS `Term`s. For typed literals, construct them via your RDF environment (e.g., `@zazuko/env`).
Notes
- The parameter IRI is exactly `https://sparqlc.described.at/param`.
- Use `PREFIX sparqlc: ` so `sparqlc:param` expands to that IRI in both triple and function forms.
- Arrays (`Term[]`) are supported by the runtime API for positions that accept multiple terms (e.g., via custom processors). Your usage may vary depending on processors you apply.
### CLI usage
`sparqlc` also ships a tiny CLI wrapper that can be used to process a query and print the effective query:
```sh
npx sparqlc file.rq [param1=value1 param2=value2 ...]
```
Parameter values are string parsed using [rdf-string](https://npm.im/rdf-string).
Typical pattern is to use the library API in your build tool via the plugins below; the CLI is primarily for local debugging.
---
## 2) `node-loader-sparql`
Import `.rq` files directly in Node.js. The loader compiles the query on‑the‑fly and makes the default export be the executable query function (typed at runtime) compatible with `sparqlc`’s `execute` signature.
### Run with Node’s loader flag
```sh
node --experimental-loader=node-loader-sparql ./app.mjs
# Node 22+ keeps the loader flag; ES module specifiers are supported
```
Now you can do:
```ts
// app.mjs (or .ts with tsx/ts-node)
import query from './queries/find-people.rq'
import rdf from '@zazuko/env'
import { StreamClient } from 'sparql-http-client'
const env = rdf
const client = new StreamClient({ endpointUrl: 'https://dbpedia.org/sparql' })
const rows = await query({ env, client })
console.log(rows)
```
---
## 3) `vite-plugin-sparql`
Use `.rq` files seamlessly in Vite projects. The plugin compiles the query at transform time.
### Setup
```ts
// vite.config.ts
import { defineConfig } from 'vite'
import sparql from 'vite-plugin-sparql'
export default defineConfig({
plugins: [sparql],
})
```
### Usage in app code
```ts
import query from './queries/people.rq'
import rdf from '@zazuko/env'
import { StreamClient } from 'sparql-http-client'
const env = rdf
const client = new StreamClient({ endpointUrl: '/sparql' })
const data = await query({ env, client })
```
---
## 4) `esbuild-plugin-sparql`
Add support for importing `.rq` in esbuild builds.
### Minimal build script
```ts
// build.ts
import { build } from 'esbuild'
import sparql from 'esbuild-plugin-sparql'
await build({
entryPoints: ['src/index.ts'],
bundle: true,
format: 'esm',
platform: 'node',
plugins: [sparql],
})
```
Then in your sources:
```ts
import query from './queries/people.rq'
// … use query.execute({ env, client })
```
If you’re bundling for Node ESM and also using `require()` somewhere, you may need to add a `banner` to create a `require` shim (see the repo tests for an example).
---
## 5) `ts-plugin-sparqlc`
TypeScript language service plugin that gives proper types for `.rq` default exports based on the query’s `returnType`. That means you’ll get:
- Autocomplete/typed signatures for `execute`
- Distinct return types for `Select`, `Construct`, `Ask`, and `Update`
### Configure `tsconfig.json`
```jsonc
{
"compilerOptions": {
// Let TS consider non‑TS imports so the plugin can attach types
"allowArbitraryExtensions": true,
// Enable the plugin
"plugins": [
{ "name": "ts-plugin-sparqlc" }
]
}
}
```
### What you get in TS
```ts
import query from './queries/people.rq'
// Hovering shows: ExecuteSelect | ExecuteConstruct | ... depending on the query
const result = await query({ env, client })
```
The plugin internally generates virtual `.d.rq.ts` type stubs next to your `.rq` files and keeps them in sync with your editor session—no files are written to disk.
---
## Runtime expectations
All environments ultimately call `query.execute(..., { env, client, processors? })` produced by `sparqlc`.
- `env`: an RDF/JS environment (e.g. `@zazuko/env`)
- `client`: optional `sparql-http-client` client; when omitted, `execute` returns the final SPARQL string instead of performing a request
- `processors`: optional array of `@hydrofoil/sparql-processor` instances to transform the parsed query before serialization
## Examples
A typical `.rq` file:
```sparql
# queries/people.rq
PREFIX foaf:
SELECT ?name WHERE { ?s foaf:name ?name }
LIMIT 5
```
And consuming it (works with the loader/plugins):
```ts
import query from './queries/people.rq'
import rdf from '@zazuko/env'
import { StreamClient } from 'sparql-http-client'
const env = rdf
const client = new StreamClient({ endpointUrl: 'https://dbpedia.org/sparql' })
const rows = await query.execute({ env, client })
```
---
## License
MIT © Tomasz Pluskiewicz