Configuring the SDKs
The Ruby and Node clients can be configured globally once, or per instance; the Python client is always per instance. This page covers where that initialization belongs and which options you should set rather than inherit.
Settings
All three SDKs accept the same four things, under language-appropriate names.
| Setting | Ruby | Node.js | Python | Default |
|---|---|---|---|---|
| API key | api_key | apiKey | api_key | none — required (Python falls back to BIBLEQL_API_KEY) |
| Endpoint | api_url | apiUrl | api_url | Ruby/Node: a Render-hosted URL, not bibleql.org. Python: https://bibleql.org/graphql |
| Fallback translation | default_translation | defaultTranslation | default_translation | eng-web |
| Timeout | timeout (seconds) | timeout (milliseconds) | timeout (seconds) | 30s / 30000ms / 30s |
Note the timeout unit is milliseconds in Node and seconds in the other two.
Those SDKs currently default api_url / apiUrl to a Render-hosted hostname rather than
https://bibleql.org/graphql. If you leave it unset, your traffic goes somewhere other than
the canonical endpoint. Pin it in your initializer. The Python client already defaults to the
canonical endpoint.
Where to initialize
- Ruby
- Node.js
- Python
In a Rails app, configure once in an initializer:
require "bibleql"
BibleQL.configure do |config|
config.api_key = ENV.fetch("BIBLEQL_API_KEY")
config.api_url = "https://bibleql.org/graphql"
config.default_translation = "eng-web"
config.timeout = 30
end
Then anywhere in the app:
BibleQL.client.passage("John 3:16")
ENV.fetch without a default is deliberate — it fails loudly at boot if the key is missing,
rather than at the first request in production.
Using Rails credentials instead
If you keep secrets in config/credentials.yml.enc rather than the environment:
BibleQL.configure do |config|
config.api_key = Rails.application.credentials.bibleql!(:api_key)
config.api_url = "https://bibleql.org/graphql"
end
Edit them with bin/rails credentials:edit:
bibleql:
api_key: bql_live_...
Per-instance clients
Global configuration is convenient but shared. For a distinct translation, a different timeout, or an isolated client in tests, build one directly:
client = BibleQL::Client.new(
api_key: ENV.fetch("BIBLEQL_API_KEY"),
api_url: "https://bibleql.org/graphql",
default_translation: "spa-rv1909"
)
A per-instance client ignores the global configuration, which makes it the better choice in tests — no global state to reset between examples.
Plain Ruby, no Rails
There is no initializer directory, so configure once at load time, before first use:
require "bibleql"
BibleQL.configure do |config|
config.api_key = ENV.fetch("BIBLEQL_API_KEY")
config.api_url = "https://bibleql.org/graphql"
end
Export a single configured client from one module and import it everywhere. Creating a new client per request works, but it is needless object churn.
import { BibleQLClient } from "bibleql-js";
const apiKey = process.env.BIBLEQL_API_KEY;
if (!apiKey) {
// Fail at startup rather than on the first request.
throw new Error("BIBLEQL_API_KEY is not set");
}
export const bibleql = new BibleQLClient({
apiKey,
apiUrl: "https://bibleql.org/graphql",
defaultTranslation: "eng-web",
timeout: 30_000,
});
import { bibleql } from "./lib/bibleql";
const passage = await bibleql.passage("John 3:16");
The explicit apiKey check also narrows the type from string | undefined to string, which
is why the module above needs no non-null assertion.
Global defaults
The package also supports process-wide configuration:
import { configure } from "bibleql-js";
configure({
apiKey: process.env.BIBLEQL_API_KEY!,
apiUrl: "https://bibleql.org/graphql",
defaultTranslation: "eng-web",
});
Prefer the exported-module approach for application code — it keeps the dependency explicit
and is far easier to substitute in tests. configure() is handy in a script or a REPL.
Next.js and other frameworks
Import the client only from server-side code — a route handler, a server component, a server
action, getServerSideProps. If a module that imports bibleql-js is reachable from a client
component, your key ends up in the browser bundle.
Keep the key in .env.local as BIBLEQL_API_KEY, never NEXT_PUBLIC_BIBLEQL_API_KEY —
the NEXT_PUBLIC_ prefix publishes it by design.
Serverless and edge
Cold starts re-run module initialization, so a module-level client is created once per instance and reused across invocations on that instance — which is what you want. Just make sure the key comes from the platform's secret store, not a bundled file.
There is no global configuration: build a bibleql.Client. In a script, use it as a context
manager so its HTTP connection pool is closed when you are done:
import bibleql
with bibleql.Client() as client: # reads BIBLEQL_API_KEY
print(client.passage("John 3:16").text)
In an application, create one client in a module and import it everywhere, so connections are reused across requests:
import os
import bibleql
bibleql_client = bibleql.Client(
api_key=os.environ["BIBLEQL_API_KEY"],
default_translation="eng-web",
timeout=30,
)
from app.bibleql_client import bibleql_client
passage = bibleql_client.passage("John 3:16")
os.environ["BIBLEQL_API_KEY"] fails loudly at import time if the key is missing. If you omit
api_key, the client reads the same variable and raises bibleql.ConfigurationError instead.
Django, Flask, FastAPI
The module-level client above works in all three. Call bibleql_client.close() from your
framework's shutdown hook — FastAPI's lifespan, for example — if you want to release
connections cleanly.
.env files
The library never reads .env files itself. Load them before creating the client, for example
with python-dotenv:
from dotenv import load_dotenv
load_dotenv()
client = bibleql.Client()
Choosing a default translation
default_translation / defaultTranslation only applies when a call omits the translation. It
does not restrict anything — any call can pass a different one. (Python's semantic_search is
the one exception: it ignores the default and lets the API choose a translation with
embeddings.)
Set it to whatever your application shows most often. If you serve several languages, prefer
passing translation explicitly at each call site so the behaviour does not depend on
configuration read elsewhere.
Timeouts
The defaults (30 seconds) are generous for most queries. Two things worth knowing:
semanticSearchcalls an external embedding service and is the slowest query in the API. If you use it behind a user-facing request, a shorter timeout plus a fallback beats a hung page.- Large
concordancepages take longer than a verse lookup. Reducefirstbefore you raise the timeout.
Verifying your setup
A cheap query that touches no Bible text:
- Ruby
- Node.js
- Python
translations = BibleQL.client.translations
puts "#{translations.size} translations available"
const translations = await bibleql.translations();
console.log(`${translations.length} translations available`);
translations = bibleql_client.translations()
print(f"{len(translations)} translations available")
If the key is wrong you get an authentication error immediately — see Errors for what each failure looks like, and SDKs for the exception class each one maps to.
Point development at a bql_test_ key. Production refuses test-environment keys, so a key
leaked from a laptop or a CI log cannot spend your live quota. See
API Keys.