How to make Requests in Hooks
At some point in your hook, you’ll need to make an HTTP request to an API. This guide shows you how to do that with the utils.request() method.
The utils.request() Method
Section titled “The utils.request() Method”Every hook receives a utils object with a type-safe request() method:
utils.request( url, // The endpoint to call requestData, // Headers, body, cookies, etc. options // Control hook execution)Basic Usage
Section titled “Basic Usage”Making a Simple Request
Section titled “Making a Simple Request”import { BeforeEachRequestHook } from '@thymian/hooks';
const hook: BeforeEachRequestHook = async (request, context, utils) => { const response = await utils.request( 'POST http://localhost:3000/api/v1/launches', { body: { missionName: 'Apollo 11', launchDate: '2026-12-31T00:00:00Z', rocketType: 'Saturn V' }, headers: { 'content-type': 'application/json' } } );
console.log(response.statusCode); // 201 console.log(response.body); // { id: '...', missionName: 'Apollo 11', ... }
return request;};
export default hook;Type Safety
Section titled “Type Safety”The request() method is fully type-safe based on your OpenAPI specification.
Autocomplete for URLs
Section titled “Autocomplete for URLs”Your IDE suggests available endpoints:
// Type "utils.request('" and get autocomplete:utils.request('POST http://localhost:3000/api/v1/launches', ...)utils.request('GET http://localhost:3000/api/v1/launches', ...)utils.request('GET http://localhost:3000/api/v1/astronauts', ...)Type-Checked Request Bodies
Section titled “Type-Checked Request Bodies”Request bodies are validated against your API schema:
const response = await utils.request('POST http://localhost:3000/api/v1/launches', { body: { missionName: 'Apollo 11', // ✓ Valid launchDate: '2026-12-31T00:00:00Z', // ✓ Valid rocketType: 'Saturn V', // ✓ Valid // invalidField: 'test' // ✗ TypeScript error }, headers: { 'content-type': 'application/json' }});Type-Checked Responses
Section titled “Type-Checked Responses”Response bodies are typed based on your OpenAPI responses:
const response = await utils.request('GET http://localhost:3000/api/v1/launches', {});
// TypeScript knows the response structureif (response.statusCode === 200) { const launches = response.body; // Type: Launch[] console.log(launches[0].missionName);}Request Options
Section titled “Request Options”Disable Hooks
Section titled “Disable Hooks”By default, utils.request() runs all hooks (beforeEach, authorize, afterEach) for the target endpoint. Disable this to prevent infinite loops:
const response = await utils.request('POST http://localhost:3000/api/v1/astronauts', { body: { name: 'Buzz Aldrin', password: 'secret', role: 'Pilot' }, headers: { 'content-type': 'application/json' }}, { runHooks: false // Don't execute hooks for this request});Use case: Creating users in an authorize hook to avoid circular execution.
Disable Authorization
Section titled “Disable Authorization”Skip only the authorize hook while keeping other hooks:
const response = await utils.request('GET http://localhost:3000/api/v1/launches', {}, { authorize: false // Skip authorize, but run beforeEach/afterEach});Use case: Testing public endpoints that don’t require authentication.
Specify Status Code
Section titled “Specify Status Code”If an endpoint has multiple response samples (e.g., 200, 404), specify which one to use:
// Request the 404 response sampleconst response = await utils.request('GET http://localhost:3000/api/v1/launches/123', {}, { forStatusCode: 404});Use case: Testing error handling in your hooks.
Error Handling
Section titled “Error Handling”Check Status Codes
Section titled “Check Status Codes”Always verify responses succeeded:
const response = await utils.request('POST http://localhost:3000/api/v1/launches', { body: { /* ... */ }, headers: { 'content-type': 'application/json' }});
if (response.statusCode !== 201) { utils.fail(`Unexpected status code: ${response.statusCode}`);}Handle Missing Resources
Section titled “Handle Missing Resources”const response = await utils.request('GET http://localhost:3000/api/v1/launches/123', {});
if (response.statusCode === 404) { utils.skip('Launch not found, skipping test');}Graceful Degradation
Section titled “Graceful Degradation”try { const response = await utils.request('POST http://localhost:3000/api/v1/launches', { body: { /* ... */ }, headers: { 'content-type': 'application/json' } });
if (response.statusCode !== 201) { utils.warn('Launch creation failed, using default data'); // Fallback logic here }} catch (error) { utils.warn('Request failed entirely', error.message);}Limitations
Section titled “Limitations”Must Be Defined in OpenAPI
Section titled “Must Be Defined in OpenAPI”You can only request endpoints that exist in your OpenAPI specification. If an endpoint isn’t defined, you’ll get a TypeScript error.
URL Format Must Match
Section titled “URL Format Must Match”The URL must exactly match the format: METHOD http://host:port/path
// ✓ Correctutils.request('POST http://localhost:3000/api/v1/launches', ...)
// ✗ Incorrect - Missing methodutils.request('http://localhost:3000/api/v1/launches', ...)
// ✗ Incorrect - Wrong formatutils.request('localhost:3000/api/v1/launches', ...)