Your First Query
If you have not used GraphQL before, this page is the short version: you describe the shape of the response you want, and that is the shape you get.
Ask for one verse
- GraphQL
- cURL
- Ruby
- Node.js
- Response
query {
verse(translation: "eng-web", book: "JHN", chapter: 3, verse: 16) {
bookName
chapter
verse
text
}
}
curl https://bibleql.org/graphql \
-H "Authorization: Bearer $BIBLEQL_API_KEY" \
-H "Content-Type: application/json" \
--data '{"query":"query { verse(translation: \"eng-web\", book: \"JHN\", chapter: 3, verse: 16) { bookName chapter verse text } }"}'
require "bibleql"
client = BibleQL::Client.new(
api_key: ENV.fetch("BIBLEQL_API_KEY"),
api_url: "https://bibleql.org/graphql"
)
verse = client.verse("JHN", 3, 16, translation: "eng-web")
puts "#{verse.book_name} #{verse.chapter}:#{verse.verse}"
puts verse.text
import { BibleQLClient } from "bibleql-js";
const client = new BibleQLClient({
apiKey: process.env.BIBLEQL_API_KEY!,
apiUrl: "https://bibleql.org/graphql",
});
const verse = await client.verse("JHN", 3, 16, {
translation: "eng-web",
});
console.log(`${verse.bookName} ${verse.chapter}:${verse.verse}`, verse.text);
{
"data": {
"verse": {
"bookName": "John",
"chapter": 3,
"verse": 16,
"text": "For God so loved the world, that he gave his one and only Son, that whoever believes in him should not perish, but have eternal life."
}
}
}
Note book: "JHN". The canonical three-letter id works in every translation. A localized name
such as "Juan" works too, as long as it matches the translation you are querying.
Ask for more fields
Fields are free to add — nothing comes back unless you request it, and nothing is hidden if you
do. Adding bookId to the previous query returns it alongside the rest; removing text stops
the verse text being sent at all.
That is the main practical difference from a REST API: response size is your decision, not the server's.
Ask for a whole chapter
- GraphQL
- cURL
- Ruby
- Node.js
- Response
query {
chapter(translation: "eng-web", book: "JHN", chapter: 3) {
verse
text
}
}
curl https://bibleql.org/graphql \
-H "Authorization: Bearer $BIBLEQL_API_KEY" \
-H "Content-Type: application/json" \
--data '{"query":"query { chapter(translation: \"eng-web\", book: \"JHN\", chapter: 3) { verse text } }"}'
require "bibleql"
client = BibleQL::Client.new(
api_key: ENV.fetch("BIBLEQL_API_KEY"),
api_url: "https://bibleql.org/graphql"
)
verses = client.chapter("JHN", 3, translation: "eng-web")
verses.each { |v| puts "#{v.verse}. #{v.text}" }
import { BibleQLClient } from "bibleql-js";
const client = new BibleQLClient({
apiKey: process.env.BIBLEQL_API_KEY!,
apiUrl: "https://bibleql.org/graphql",
});
const verses = await client.chapter("JHN", 3, {
translation: "eng-web",
});
verses.forEach((v) => console.log(`${v.verse}. ${v.text}`));
{
"data": {
"chapter": [
{
"verse": 1,
"text": "Now there was a man of the Pharisees named Nicodemus, a ruler of the Jews."
},
{
"verse": 2,
"text": "The same came to him by night, and said to him, “Rabbi, we know that you are a teacher come from God, for no one can do these signs that you do, unless God is with him.”"
},
{
"verse": 3,
"text": "Jesus answered him,
“Most certainly, I tell you, unless one is born anew,
he can’t see God’s Kingdom.”"
},
// ... 33 more
]
}
}
chapter returns a list of verses. Every list field in the schema behaves the same way — you
select the fields you want on each element.
Nest through relationships
Types connect to each other, so one request can cross several levels:
query {
translation(identifier: "eng-web") {
name
books {
bookId
name
chapterCount
}
}
}
That returns the translation plus every book with its chapter count, in one round trip.
Queries are capped at depth 15 and complexity 300. A deeply nested query — for example every
chapter and every verse of every book — will be rejected rather than served slowly. Use
bibleIndex for structure, then fetch the text you actually need.
Use variables instead of string interpolation
Once a query lives in application code, pass values as variables rather than building the document with string concatenation:
query GetPassage($translation: String!, $reference: String!) {
passage(translation: $translation, reference: $reference) {
reference
text
}
}
{
"translation": "eng-web",
"reference": "John 3:16"
}
Send them as the variables key beside query in the POST body. This keeps the document
static and cacheable, and avoids quoting bugs.
Name your operations
query GetPassage(...) above is a named operation. Names cost nothing and make server logs
and client tooling far easier to read. Anonymous query { ... } is fine for exploration.
Next
- Bible References — everything the
referenceargument accepts - API Reference — the full schema
- SDKs — skip GraphQL entirely and call methods