Skip to content

Core Library

@t-req/core is the JavaScript library that powers the CLI and TUI. Use it to parse and execute .http files programmatically.

Terminal window
npm install @t-req/core

Or with other package managers:

Terminal window
bun add @t-req/core
pnpm add @t-req/core
import { createClient } from '@t-req/core';
const client = createClient({
variables: { baseUrl: 'https://api.example.com' },
});
const response = await client.run('./requests/get-users.http');
const data = await response.json();
console.log(data);
await client.close();

Create a client instance for executing requests.

import { createClient, createCookieJar } from '@t-req/core';
const client = createClient({
variables: {
baseUrl: 'https://api.example.com',
token: 'my-api-token',
},
cookieJar: createCookieJar(),
timeout: 30000,
});
OptionTypeDefaultDescription
variablesRecord<string, unknown>{}Variables available to all requests
resolversRecord<string, Resolver>{}Custom resolver functions
cookieJarCookieJarCookie jar for automatic cookie handling
timeoutnumber30000Default timeout in milliseconds
defaults.headersRecord<string, string>Default headers for all requests
defaults.followRedirectsbooleantrueFollow HTTP redirects
defaults.validateSSLbooleantrueValidate SSL certificates
defaults.proxystringProxy URL

Connect to a running t-req server instead of executing locally:

OptionTypeDescription
serverstringServer URL (e.g., http://localhost:4097)
serverTokenstringBearer token for authentication
profilestringServer-side config profile to use
const client = createClient({
server: 'http://localhost:4097',
serverToken: process.env.TREQ_TOKEN,
profile: 'staging',
});

Execute a request from a .http file. Returns a native Response object.

const response = await client.run('./requests/api.http');
const data = await response.json();

If the file contains multiple requests, the first one is executed by default. Use request index or name to select a specific request:

// By index (0-based)
await client.run('./api.http', { variables: { __requestIndex: 0 } });
// The file is parsed and the first request is executed

Execute a request from an in-memory string:

const httpContent = `
GET https://api.example.com/users
Authorization: Bearer {{token}}
`;
const response = await client.runString(httpContent, {
variables: { token: 'my-token' },
basePath: process.cwd(), // For resolving file references
});
OptionTypeDescription
variablesRecord<string, unknown>Additional variables for this request
timeoutnumberTimeout for this request (overrides default)
signalAbortSignalAbort signal for cancellation
basePathstringBase path for file references (runString only)

Set a single variable:

client.setVariable('token', newToken);

Merge multiple variables:

client.setVariables({
token: newToken,
userId: '123',
});

Get a copy of all current variables:

const vars = client.getVariables();
console.log(vars.baseUrl);

Close the client and release resources:

await client.close();

For server-mode clients, this finishes the current flow.

With TypeScript 5.2+, use await using for automatic cleanup:

await using client = createClient({ server: 'http://localhost:4097' });
const res = await client.run('./auth/login.http');
// client.close() called automatically when scope exits

Parse .http file content into structured request objects:

import { parse } from '@t-req/core';
const requests = parse(`
### Get users
GET https://api.example.com/users
### Create user
POST https://api.example.com/users
Content-Type: application/json
{"name": "Alice"}
`);
console.log(requests.length); // 2
console.log(requests[0].name); // "Get users"
console.log(requests[0].method); // "GET"

Parse a .http file from the filesystem:

import { parseFile } from '@t-req/core';
const requests = await parseFile('./requests/api.http');

The structure returned by parse() and parseFile():

interface ParsedRequest {
/** Optional request name from ### or @name */
name?: string;
/** HTTP method (GET, POST, etc.) */
method: string;
/** Full URL with any query parameters */
url: string;
/** Request headers */
headers: Record<string, string>;
/** Request body (if present) */
body?: string;
/** File reference if using < ./path syntax */
bodyFile?: { path: string };
/** Form fields if using form syntax */
formData?: FormField[];
/** Original raw content */
raw: string;
/** Meta directives (@name, @timeout, etc.) */
meta: Record<string, string>;
}

Create custom resolvers for dynamic values:

const client = createClient({
resolvers: {
$timestamp: () => String(Date.now()),
$uuid: () => crypto.randomUUID(),
$env: (key) => process.env[key] || '',
$random: (min = '0', max = '100') => {
const n = Math.floor(
Math.random() * (Number(max) - Number(min) + 1)
) + Number(min);
return String(n);
},
},
});

Resolvers can be async:

const client = createClient({
resolvers: {
$secret: async (key) => {
const secret = await fetchFromVault(key);
return secret;
},
},
});

Resolvers receive arguments as strings from the template:

{{$random(1, 100)}}
{{$env(API_KEY)}}

Arguments are parsed as JSON if possible, otherwise passed as single string.

Create a cookie jar for automatic cookie handling:

import { createClient, createCookieJar } from '@t-req/core';
const cookieJar = createCookieJar();
const client = createClient({
cookieJar,
});
// Cookies from Set-Cookie headers are automatically stored
await client.run('./auth/login.http');
// And sent with subsequent requests
await client.run('./api/profile.http');

The cookie jar follows RFC 6265 (same behavior as browsers).

try {
const response = await client.run('./api.http');
if (!response.ok) {
console.error(`HTTP ${response.status}: ${response.statusText}`);
return;
}
const data = await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request was cancelled');
} else if (error.message.includes('Undefined variable')) {
console.error('Missing variable:', error.message);
} else {
console.error('Request failed:', error);
}
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await client.run('./api.http', {
signal: controller.signal,
});
clearTimeout(timeoutId);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request timed out');
}
}

Or use the built-in timeout option:

const response = await client.run('./api.http', {
timeout: 5000, // 5 seconds
});

All types are exported from the package:

import type {
Client,
ClientConfig,
RunOptions,
ParsedRequest,
FormField,
FileReference,
Resolver,
InterpolateOptions,
} from '@t-req/core';
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createClient, createCookieJar } from '@t-req/core';
describe('User API', () => {
let client;
beforeAll(() => {
client = createClient({
variables: { baseUrl: 'http://localhost:3000' },
cookieJar: createCookieJar(),
});
});
afterAll(async () => {
await client.close();
});
it('lists users', async () => {
const res = await client.run('./requests/users/list.http');
expect(res.status).toBe(200);
const data = await res.json();
expect(data.users).toBeInstanceOf(Array);
});
it('creates a user', async () => {
client.setVariable('userName', 'Test User');
const res = await client.run('./requests/users/create.http');
expect(res.status).toBe(201);
const user = await res.json();
expect(user.name).toBe('Test User');
});
});

See BYO Test Runner for more testing patterns.