Node.js
Server-side requests with global fetch and axios: retries, env-loaded tokens, streaming JSON.
This guide is for calling the GoRest API from a Node.js server, script, serverless function, or worker. Node 18 made fetch a global, so the basic call pattern is simple; the real concerns are operational: where the token comes from, how to reuse connections across many calls, when to use axios, how to wrap an Express route around the API, and how to retry on rate limits in one place.
Setup
If you are on Node 18 or newer, there is nothing to install for the basics: fetch, AbortController and URL are all globals. Older Node needs undici or node-fetch installed and assigned to globalThis.fetch before use.
// package.json
// "type": "module",
// "engines": { "node": ">=18" }
import "dotenv/config"; // optional but useful
const API = "https://gorest.co.in/public/v2";
const TOKEN = process.env.GOREST_TOKEN;
if (!TOKEN) throw new Error("Set GOREST_TOKEN in your env");Reusable headers
A small helper saves repeating yourself. Notice that application/json is on both the Accept and the Content-Type. The first tells the server what you want, and the second tells it what you are sending.
const headers = {
Authorization: `Bearer ${TOKEN}`,
Accept: "application/json",
"Content-Type": "application/json"
};
async function call(method, path, body) {
const res = await fetch(`${API}${path}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined
});
if (res.status === 204) return null;
const data = await res.json();
if (!res.ok) {
const err = new Error(`${method} ${path} -> ${res.status}`);
err.status = res.status;
err.body = data;
throw err;
}
return data;
}List, fetch, write
const users = await call("GET", "/users?status=active&page=1");
const user = await call("GET", `/users/${users[0].id}`);
const created = await call("POST", "/users", {
name: "Avani Iyer",
email: `avani-${Date.now()}@example.com`,
gender: "female",
status: "active"
});
await call("PATCH", `/users/${created.id}`, { status: "inactive" });
await call("DELETE", `/users/${created.id}`);axios for production code
The Node global fetch is fine for scripts. For long-running services, axios is still worth it. It gives you typed responses out of the box, named transformers, interceptors, and a smaller error contract. The same calls in axios:
import axios from "axios";
const api = axios.create({
baseURL: API,
headers: { Authorization: `Bearer ${TOKEN}` },
timeout: 15000
});
const { data: users } = await api.get("/users", { params: { status: "active" }});
const { data: created } = await api.post("/users", {
name: "Pranav Kapoor",
email: `pranav-${Date.now()}@example.com`,
gender: "male",
status: "active"
});Retry on 429 with an interceptor
axios interceptors let you handle rate limits in one place rather than per call. The X-RateLimit-Reset header is in seconds:
api.interceptors.response.use(
(res) => res,
async (err) => {
if (err.response?.status !== 429) throw err;
const wait = (+err.response.headers["x-ratelimit-reset"] || 1) * 1000;
await new Promise(r => setTimeout(r, wait));
return api.request(err.config); // replay
}
);Wrap as an Express route
A common pattern: your frontend wants the data, but the token must stay on the server. Put one Express route per resource and proxy through:
import express from "express";
const app = express();
app.use(express.json());
app.get("/api/users", async (req, res) => {
try {
const data = await call("GET",
`/users?status=${req.query.status ?? "active"}&page=${req.query.page ?? 1}`);
res.json(data);
} catch (err) {
res.status(err.status ?? 500).json({ message: err.message });
}
});
app.listen(3000);The browser never sees your access token; the proxy server is the only thing that holds it. This is the right architecture for any production frontend. Never embed an API token in client-side JavaScript.
Stream large responses
If you ever need to pass a multi-megabyte payload from the API to a client without buffering it in memory, use the .body stream from fetch directly:
import { Readable } from "node:stream";
app.get("/proxy/users", async (req, res) => {
const upstream = await fetch(`${API}/users`, { headers });
res.status(upstream.status);
upstream.headers.forEach((v, k) => res.setHeader(k, v));
// upstream.body is a Web ReadableStream; convert it before piping
Readable.fromWeb(upstream.body).pipe(res);
});Tips
- Use
dotenvin dev only. In production, inject env via your platform (PM2, systemd, Docker, Heroku config, etc.). - When you write tests for code that calls this API, mock with
nockormswrather than hitting the network. Tests pass faster and do not use up your rate-limit budget. - In a Lambda or Cloudflare Worker, do not create a new fetch agent per invocation; reuse the global one. Cold start is expensive enough.
- Keep timeouts conservative on the server (10-15s). The default
fetchhas none, which means a slow upstream will quietly hold a worker forever.
Keep going
JavaScript (Fetch API)
Browser-native fetch with async/await, bearer-token auth, error handling, and pagination.
Python (requests)
Calls with the requests library, JSON bodies, query filtering, and dataclass parsing.
Ruby (Net::HTTP)
Net::HTTP idioms, persistent connections, and consuming the JSON response.