devRant API: Client scripts
Ready-to-run clients for the devRant-compatible protocol. The Python client uses only the
standard library; the JavaScript client uses Node 18+ (global fetch). The full versions, plus
example scripts and an end-to-end conformance test, ship in the repository under
examples/devrant/.
Python client (drop-in)
import json, urllib.parse, urllib.request
class DevRant:
def __init__(self, base_url, username=None, password=None):
self.base_url = base_url.rstrip("/")
self.username, self.password = username, password
self.auth = {}
def _request(self, method, path, params=None, body=None):
merged = dict(params or {}); merged.update(self.auth)
url = f"{self.base_url}/api/{path.lstrip('/')}"
data, headers = None, {"Accept": "application/json"}
if method in ("GET", "DELETE"):
if merged:
url += "?" + urllib.parse.urlencode(merged)
else:
payload = dict(merged); payload.update(body or {})
data = urllib.parse.urlencode(payload).encode()
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode())
def login(self):
out = self._request("POST", "users/auth-token",
body={"username": self.username, "password": self.password})
if not out.get("success"):
raise RuntimeError(out.get("error", "login failed"))
t = out["auth_token"]
self.auth = {"user_id": t["user_id"], "token_id": t["id"], "token_key": t["key"]}
return self.auth
def rants(self, sort="recent", limit=20, skip=0):
return self._request("GET", "devrant/rants",
{"sort": sort, "limit": limit, "skip": skip}).get("rants", [])
def post_rant(self, text, tags=""):
return self._request("POST", "devrant/rants", body={"rant": text, "tags": tags})
def vote_rant(self, rant_id, vote):
return self._request("POST", f"devrant/rants/{rant_id}/vote", body={"vote": vote})
def comment(self, rant_id, text):
return self._request("POST", f"devrant/rants/{rant_id}/comments", body={"comment": text})
api = DevRant("https://devplace.net", "USERNAME", "PASSWORD")
api.login()
print(api.post_rant("Hello from Python", "python,devrant"))
JavaScript client (drop-in)
export class DevRant {
constructor(baseUrl, username, password) {
this.baseUrl = baseUrl.replace(/\/$/, "");
this.username = username; this.password = password; this.auth = {};
}
async _request(method, path, params = {}, body = null) {
const merged = { ...params, ...this.auth };
const headers = { Accept: "application/json" };
let url = new URL(`${this.baseUrl}/api/${path.replace(/^\//, "")}`);
const init = { method, headers };
if (method === "GET" || method === "DELETE") {
for (const [k, v] of Object.entries(merged)) url.searchParams.set(k, v);
} else {
headers["Content-Type"] = "application/x-www-form-urlencoded";
init.body = new URLSearchParams({ ...merged, ...(body || {}) }).toString();
}
return (await fetch(url, init)).json();
}
async login() {
const out = await this._request("POST", "users/auth-token", {},
{ username: this.username, password: this.password });
if (!out.success) throw new Error(out.error || "login failed");
const t = out.auth_token;
this.auth = { user_id: t.user_id, token_id: t.id, token_key: t.key };
return this.auth;
}
async rants(sort = "recent", limit = 20, skip = 0) {
return (await this._request("GET", "devrant/rants", { sort, limit, skip })).rants || [];
}
postRant(text, tags = "") {
return this._request("POST", "devrant/rants", {}, { rant: text, tags });
}
voteRant(rantId, vote) {
return this._request("POST", `devrant/rants/${rantId}/vote`, {}, { vote });
}
comment(rantId, text) {
return this._request("POST", `devrant/rants/${rantId}/comments`, {}, { comment: text });
}
}
const api = new DevRant("https://devplace.net", "USERNAME", "PASSWORD");
await api.login();
console.log(await api.postRant("Hello from Node", "javascript,devrant"));
Example scripts in the repository
examples/devrant/ contains the complete clients and runnable scripts:
| File | Language | What it does |
|---|---|---|
client.py / client.mjs |
Python / JS | Full reusable client (every endpoint). |
post_rant.py / post_rant.mjs |
Python / JS | Post one rant from the command line. |
feed_watch.py / feed_watch.mjs |
Python / JS | Live feed ticker, optional keyword auto-upvote. |
smoke_test.py / smoke_test.mjs |
Python / JS | End-to-end conformance test, prints PASS/FAIL. |
They read DEVRANT_BASE, DEVRANT_USERNAME, and DEVRANT_PASSWORD from the environment.
# post a rant
DEVRANT_USERNAME=you DEVRANT_PASSWORD=secret6 \
python examples/devrant/post_rant.py "Posted from a script" "python"
# run the full conformance test against a running server
DEVRANT_BASE=https://devplace.net python examples/devrant/smoke_test.py
DEVRANT_BASE=https://devplace.net node examples/devrant/smoke_test.mjs