Node.js
Server-side requests with global fetch and axios: retries, env-loaded tokens, streaming JSON.
This guide is for calling the Go REST API from a Node.js server, script, serverless function, or worker. Node 18 madefetch a global, so the basic call pattern is straightforward; the interesting concerns are operational: where the token comes from, how to reuse connections across many calls, when to reach for 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 andURL are all globals. Older Node needsundici ornode-fetch installed and assigned toglobalThis.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 thatapplication/json is on both theAccept and theContent-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 globalfetch 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. TheX-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 relay a multi-megabyte payload from the API to a client without buffering it in memory, use the.body stream fromfetch 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 code dotenv | in 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 code nock | or code msw | rather than hitting the network. Tests pass faster and do not burn rate-limit budget.
- In a Lambda or Cloudflare Worker, do not create a new fetch agent per invocation; re-use the global one. Cold start is expensive enough.
- Keep timeouts conservative on the server (10-15s). The default code fetch | has 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.