Skip to content

Postman Migration

Switching from Postman? t-req replaces GUI-locked collections with plain .http files that live in Git, run from the CLI, and integrate with any test framework. You can import your existing collection automatically or rebuild from scratch.

Export from Postman (Collection v2.1 JSON), then run:

Terminal window
treq import postman my-collection.json

This creates a directory of .http files mirroring your folder structure, plus a treq.jsonc with extracted variables.

Key options:

FlagDefaultDescription
--output./<collection-name>Output directory
--strategyrequest-per-filerequest-per-file or folder-per-file
--dry-runPreview without writing files
--on-conflictfailfail, skip, overwrite, or rename
--merge-variablesMerge collection variables into existing treq.jsonc

Preview first:

Terminal window
treq import postman my-collection.json --dry-run

Pre-request scripts, test scripts, and OAuth2 auth configurations are not imported — they are reported as diagnostics. The sections below explain how to replace each one.

Postmant-req
Collection / FolderDirectory of .http files
EnvironmentProfile in treq.jsonc (--profile staging)
Global / Collection variablesvariables in treq.jsonc
Environment variablesProfile-scoped variables
Local variable override--var key=value or client.setVariable()
{{variable}} syntaxSame — {{variable}}
Dynamic variables ($guid, $timestamp)Resolvers — {{$uuid()}}, {{$timestamp()}}
Pre-request scriptPlugin request.before hook
Test script / pm.test()@t-req/plugin-assert or BYO test runner
Collection Runner / Newmantreq run
Postman FlowsTypeScript script with createClient()
Cookie jarcookies.enabled in treq.jsonc (auto)
Team workspace / forkGit repository / branch

Postman environments map to treq.jsonc profiles:

{
"variables": {
"baseUrl": "https://api.example.com"
},
"profiles": {
"local": {
"variables": { "baseUrl": "http://localhost:3000" }
},
"staging": {
"variables": {
"baseUrl": "https://staging.example.com",
"token": "{env:STAGING_TOKEN}"
}
}
}
}

{env:STAGING_TOKEN} reads from the process environment — equivalent to Postman’s “secret” variable type. Activate a profile:

Terminal window
treq run requests/users/list.http --profile staging

See Configuration Reference for the full schema.

Variables work the same way — {{variableName}} in .http files:

GET {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{token}}

Override per-request from the CLI:

Terminal window
treq run requests/users/get.http --var userId=42

For dynamic values like Postman’s {{$guid}} or {{$timestamp}}, configure resolvers in treq.jsonc:

{
"resolvers": {
"timestamp": { "type": "command", "command": "date +%s" },
"uuid": { "type": "command", "command": "uuidgen" }
}
}

Then use them in .http files:

POST {{baseUrl}}/events
Content-Type: application/json
{ "id": "{{$uuid()}}", "timestamp": "{{$timestamp()}}" }

Auth is just headers — explicit and version-controlled.

Bearer:

GET {{baseUrl}}/api/users
Authorization: Bearer {{token}}

Basic (static):

GET {{baseUrl}}/protected
Authorization: Basic dXNlcjpwYXNz

API key:

GET {{baseUrl}}/data
X-API-Key: {{apiKey}}

OAuth2 token refresh — model as a request you call in a script:

# @name refresh-token
POST {{baseUrl}}/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type = refresh_token
refresh_token = {{refreshToken}}
client_id = {{clientId}}
client_secret = {{clientSecret}}

JSON — body after a blank line:

POST {{baseUrl}}/users
Content-Type: application/json
{ "name": "Alice", "email": "alice@example.com" }

Form (URL-encoded):

POST {{baseUrl}}/login
Content-Type: application/x-www-form-urlencoded
username = alice
password = secret123

File upload (multipart):

POST {{baseUrl}}/upload
Content-Type: multipart/form-data
title = Quarterly Report
document = @./files/report.pdf

Body from file:

POST {{baseUrl}}/users
Content-Type: application/json
< ./fixtures/user.json

See HTTP File Format for the complete syntax.

Postman pre-request scripts become a plugin with a request.before hook:

plugins/add-headers.ts
import { definePlugin } from '@t-req/core';
export default definePlugin({
name: 'add-headers',
version: '1.0.0',
hooks: {
async 'request.before'(input, output) {
output.request.headers['X-Request-Id'] = crypto.randomUUID();
output.request.headers['X-Timestamp'] = String(Date.now());
}
}
});

Register in treq.jsonc:

{
"plugins": ["file://./plugins/add-headers.ts"]
}

Use request.compiled instead if you need access to the fully-interpolated body (e.g. for HMAC signing). See Plugins for the full hook lifecycle.

Test scripts → assertions or test runner

Section titled “Test scripts → assertions or test runner”

Path 1: Inline assertions (closest to Postman test scripts):

{
"plugins": ["@t-req/plugin-assert"]
}
# @assert status == 200
# @assert header Content-Type contains application/json
# @assert jsonpath $.users[0].id exists
GET {{baseUrl}}/users
Authorization: Bearer {{token}}

treq run exits with code 1 on failure — works in CI with no test framework needed.

Path 2: BYO test runner (full assertion power):

import { describe, it, expect, afterAll } from 'vitest';
import { createClient } from '@t-req/core';
const client = createClient({
variables: { baseUrl: 'https://api.example.com' },
});
afterAll(() => client.close());
it('lists users', async () => {
const res = await client.run('./requests/users/list.http');
expect(res.status).toBe(200);
const users = await res.json();
expect(Array.isArray(users)).toBe(true);
expect(users.length).toBeGreaterThan(0);
});

See BYO Test Runner for Jest, Bun, and pytest examples.

Single request:

Terminal window
treq run requests/auth/login.http --profile staging

CI pipeline (GitHub Actions):

steps:
- uses: actions/checkout@v4
- name: Install t-req
run: curl -fsSL https://t-req.io/install.sh | bash
- name: Smoke test
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: treq run requests/smoke-test.http --profile production

Parameterized runs (Postman data files equivalent):

import { createClient } from '@t-req/core';
const client = createClient({
variables: { baseUrl: 'https://api.example.com' },
});
const users = [
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
];
for (const user of users) {
const res = await client.run('./requests/users/create.http', {
variables: user,
});
console.log(`${user.name}: ${res.status}`);
}
await client.close();

Postman Flows maps to a TypeScript script with createClient(). You get the full language — loops, conditionals, error handling:

import { createClient } from '@t-req/core';
const client = createClient({
variables: { baseUrl: 'https://api.example.com' },
});
// Login and store token
const loginRes = await client.run('./requests/auth/login.http', {
variables: { email: 'alice@example.com', password: 'secret' },
});
const { accessToken, id } = await loginRes.json();
client.setVariable('token', accessToken);
client.setVariable('userId', id);
// Use token in subsequent requests
const profile = await (await client.run('./requests/users/get.http')).json();
console.log('Profile:', profile.name);
await client.close();

See Core Library for the full createClient() API.

.http files are plain text — share them with Git instead of Postman’s cloud sync. Diffs are readable, PRs review API changes, and branches isolate work.

Keep secrets out of treq.jsonc by using {env:VAR} substitution:

{
"profiles": {
"production": {
"variables": { "token": "{env:API_TOKEN}" }
}
}
}

Set API_TOKEN in your shell, a git-ignored .env file, or CI secrets.