DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones Build AI Agents That Are Ready for Production
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
Build AI Agents That Are Ready for Production

Just dropped: New 2026 “Cloud-Native Foundations” Trend Report. See how teams are tackling complexity, cost & reliability.

Building drone software? Explore QGroundControl customization and certification considerations in an Oct. 29 webinar.

JavaScript

JavaScript (JS) is an object-oriented programming language that allows engineers to produce and implement complex features within web browsers. JavaScript is popular because of its versatility and is preferred as the primary choice unless a specific function is needed. In this Zone, we provide resources that cover popular JS frameworks, server applications, supported data types, and other useful topics for a front-end engineer.

icon
Latest Premium Content
Trend Report
Low-Code Development
Low-Code Development
Refcard #363
JavaScript Test Automation Frameworks
JavaScript Test Automation Frameworks
Refcard #288
Getting Started With Low-Code Development
Getting Started With Low-Code Development

DZone's Featured JavaScript Resources

How to Verify Response Data in API Testing With Playwright TypeScript

How to Verify Response Data in API Testing With Playwright TypeScript

By Faisal Khatri DZone Core CORE
One of the most important parts of API test automation is validating the response body to ensure data integrity. This step plays a key role in functional API testing, as it helps confirm that the API is returning the right data in the expected format. Response body validation isn’t limited to a specific request type; it applies equally to POST, GET, PUT, and PATCH APIs. The same validation approach can be used for any API response to verify the data returned by the service. Playwright offers multiple ways to validate response bodies. In this tutorial, I’ll walk you through these approaches to help you efficiently perform assertions on the response data using best practices. Checkout the previous tutorial blog to learn about Installation, the demo application, and how to send GET API requests with Playwright. How to Verify the Response Structure Response structure checks ensure that an API consistently returns data in the expected format, protecting the contract between backend services and their consumers. They help catch breaking changes early, such as missing or renamed fields, even when the API still returns a successful status code. TypeScript test("GET Order details and perform structure check", async ({ request }) => { const response = await request.get("http://localhost:3004/getOrder/", { params: { user_id: "1", }, failOnStatusCode: true, }); const responseBody = await response.json(); expect(responseBody).toHaveProperty("message"); expect(responseBody).toHaveProperty("orders"); expect(responseBody.orders[0]).toHaveProperty("id"); expect(responseBody.orders[0]).toHaveProperty("product_name"); }); This test focuses on validating the structure of the API response. It validates that the response body contains the expected top-level keys and that each order object includes the required fields. Basic Assertions The basic assertions validate API success and data presence, making them a good first layer of verification before deeper structure or data-level checks. TypeScript test("Get order details and perform basic level verification", async ({ request, }) => { const response = await request.get("http://localhost:3004/getOrder/", { params: { user_id: 1, }, failOnStatusCode: true, }); const responseBody = await response.json(); expect(responseBody.message).toBe("Order found!!"); expect(Array.isArray(responseBody.orders)).toBeTruthy(); expect(responseBody.orders.length).toBeGreaterThan(0); }); This test performs a basic level check to confirm that the endpoint works as expected and returns the expected data in the response. After parsing the response body, the assertions focus on the following essential basic-level checks: TypeScript expect(responseBody.message).toBe("Order found!!"); The above line of code verifies that the API returns the expected message text in the response body. TypeScript expect(Array.isArray(responseBody.orders)).toBeTruthy(); This line of code ensures that the orders field in the response is an array, validating the basic response format. TypeScript expect(responseBody.orders.length).toBeGreaterThan(0); This part of the test confirms that at least one order is returned in the orders array, ensuring the response contains required data. How to Verify Response Data With Details Validating the actual data returned in the response is essential to ensure that the API response contains the correct values. TypeScript test("Get order and verify order details", async ({ request }) => { const response = await request.get("http://localhost:3004/getOrder/", { params: { user_id: "1", }, failOnStatusCode: true, }); const responseBody = await response.json(); const order = responseBody.orders[0]; expect(order.id).not.toBeNull(); expect(order.id).toBeDefined(); expect(order.user_id).toEqual("1"); expect(order.product_id).toEqual("79"); expect(order.product_name).toEqual("5 star 10gm Chocobar"); }); The following code ensures that the response has a valid identifier and it is not missing or empty. TypeScript expect(order.id).not.toBeNull(); expect(order.id).toBeDefined(); This check is required because the API generates the order ID when a new order is created in the system. It ensures that the “id” field has a valid value generated and assigned to it, since this “id” is used to retrieve, update, or delete order data. TypeScript expect(order.user_id).toEqual("1"); expect(order.product_id).toEqual("79"); expect(order.product_name).toEqual("5 star 10gm Chocobar"); These statements assert that the order details are retrieved correctly for the respective request. The “user_id” - “1” was sent in the request, and verifying it in the response, along with the other order details such as “product_id” and “product_name,” ensures that the correct data is returned. How to Verify Response Data by Matching Objects and Arrays Playwright allows response data verification by matching objects and arrays partially within the API response. This approach is useful because it makes tests more flexible and confirms that the API returns the correct data structure and values. TypeScript test("Get order and verify matching object and array", async ({ request }) => { const response = await request.get("http://localhost:3004/getOrder/", { params: { user_id: 1, }, failOnStatusCode: true, }); const responseBody = await response.json(); expect(responseBody).toMatchObject({ message: "Order found!!", orders: expect.arrayContaining([ expect.objectContaining({ product_id: "79", product_name: "5 star 10gm Chocobar", product_amount: 5, qty: 1, tax_amt: 0.5, total_amt: 5.5, }), ]), }); }); In this test, the toMatchObject assertion verifies that the response contains a “message” with the expected value “Order found!!” and an orders array. Within the array, "expect.arrayContaining" ensures that at least one order matches the expected data, while "expect.objectContaining" verifies only the values in the specified fields of that order. Using Best Practices to Perform Assertions Best practices create stable, maintainable API automation tests by combining basic checks with flexible data matching. TypeScript test("Get Order details API test with best practice", async ({ request }) => { const response = await request.get("http://localhost:3004/getOrder/", { params: { user_id: "1", }, failOnStatusCode: true, }); const responseBody = await response.json(); expect(responseBody.message).toBe("Order found!!"); expect(responseBody.orders.length).toBeGreaterThan(0); expect(responseBody.orders).toEqual( expect.arrayContaining([ expect.objectContaining({ id: 1, product_name: "5 star 10gm Chocobar", }), ]) ); }); The test sends a GET request to fetch order details for “user_id”-“1". The use of failOnStatusCode: true ensures the test fails immediately if the API does not return a 2xx status code. The response is then parsed into a JSON object for validation. The assertions are structured in layers: TypeScript expect(responseBody.message).toBe("Order found!!"); This assertion verifies the message text, confirming that the API returns the correct message when an order is found. TypeScript expect(responseBody.orders.length).toBeGreaterThan(0); This statement ensures meaningful data is returned and avoids false positives when the array is empty. TypeScript expect(responseBody.orders).toEqual( expect.arrayContaining([ expect.objectContaining({ id: 1, product_name: "5 star 10gm Chocobar", }), ]) ); The final part of the code performs the final assertion using arrayContaining and objectContaining to verify that at least one order has the expected “id” and “product_name”, without asserting every field. These layered validations improve clarity by verifying structure, data presence, and key data values in sequence. Extracting Data From the Response Extracting data from the API response is a common and widely used pattern in API test automation. It is important in multiple ways, such as reusing the data in further tests for dynamic testing and end-to-end validation. TypeScript test('Get order details and extract the order id', async({request}) => { const response = await request.get("http://localhost:3004/getOrder/", { params: { id: 1, }, failOnStatusCode: true, }); const responseBody = await response.json(); expect(responseBody.message).toBe("Order found!!"); expect(responseBody.orders.length).toBeGreaterThan(0); expect(responseBody.orders).toEqual( expect.arrayContaining([ expect.objectContaining({ id: 1, product_name: "5 star 10gm Chocobar", }), ]) ); const order = responseBody.orders[0]; expect(order.id).not.toBeNull(); const order_id= order.id; console.log(order_id); const product_name = order.product_name console.log(product_name) }); This test sends a GET API request and performs basic validations to ensure the API response is reliable. TypeScript const order = responseBody.orders[0]; expect(order.id).not.toBeNull(); const order_id= order.id; console.log(order_id); The code above extracts the “order_id” from the order object in the response. Before accessing it, an assertion is made to verify that the value is not null. Finally, the value of the order_id is printed in the console. TypeScript const product_name = order.product_name console.log(product_name) Similarly, other values, such as product_name, can also be extracted. Attaching the Response Body to the Playwright Report The Playwright report, by default, shows the steps executed, the number of tests run, pass/fail status, and time taken to run the tests. However, it does not attach the response body to the test report. Attaching the response body to the report improves visibility and makes the test report more informative and transparent. The following code shows how to extract the required metadata and attach it to the Playwright report. TypeScript test("Get order details API and attach the response details to the report", async ({ request, }, testInfo) => { const response = await request.get("http://localhost:3004/getOrder/", { params: { user_id: "1", }, }); expect(response.status()).toBe(200); const status = response.status(); const statusText = response.statusText(); const headers = response.headers(); const body = await response.json(); const fullResponse = { status, statusText, headers, body, }; await testInfo.attach("Full API Response", { body: JSON.stringify(fullResponse, null, 2), contentType: "application/json", }); }); The testInfo is a built-in Playwright fixture and provides utilities to manage and inspect test execution, such as attaching files to reports, updating test timeouts, and identifying the currently running test. The following lines of code extract the response metadata, such as the status code, status text, headers, and response body. TypeScript const status = response.status(); const statusText = response.statusText(); const headers = response.headers(); const body = await response.json(); Next, let’s combine all response details and create a single object containing: Status codeStatus textHeadersResponse body TypeScript const fullResponse = { status, statusText, headers, body, }; Finally, let’s attach these details to the report using the testInfo.attach() method as shown below: TypeScript await testInfo.attach("Full API Response", { body: JSON.stringify(fullResponse, null, 2), contentType: "application/json", }); The testInfo.attach() adds an attachment to the Playwright report. The attach() method has 3 parameters: Name of the attachment: The first parameter is the name, “Full API Response”, that will be shown for the attachment.Body of the attachment: The second parameter is for the body of the attachment. The JSON.stringify(fullResponse, null, 2) has 3 arguments. The first argument converts the fullResponse object into a readable, pretty-formatted JSON. The second argument is the replacer, which is null. It ensures that all properties from the fullResponse object are included as they are, without modifying anything. The third argument controls pretty-printing. Here, “2” means indent nested JSON by 2 spaces.Content type: This parameter ensures that the report treats the attachment as JSON. The following screenshot is generated after the tests are run: Test Execution Running the tests in Playwright is simple and easy. We can run the following command from the terminal: Plain Text npx playwright test To generate the report, the following command can be used: Plain Text npx playwright show-report Summary Playwright provides multiple approaches, including structure checks and matching objects and arrays for verifying response data. The right strategy should be chosen based on your project’s requirements. Based on my experience, combining response structure checks with response data validation, including the matching object and array strategy, can be used as an effective approach for validating API responses. Happy testing! More
When TypeScript Types Meet Untrusted AI: Building Type-Safe LLM Pipelines With Runtime Validation

When TypeScript Types Meet Untrusted AI: Building Type-Safe LLM Pipelines With Runtime Validation

By Bhanu Sekhar Guttikonda DZone Core CORE
TypeScript can make an LLM integration look safer than it is. A function may promise Promise and every branch may compile under strict settings, yet none of those guarantees prove that a model returned a valid Classification. TypeScript annotations are erased during compilation and do not alter runtime behavior, so data crossing an AI boundary remains untrusted until executable validation proves otherwise. The practical goal is a pipeline in which model output becomes domain data only after passing a runtime contract. Static Types Stop at the Model Boundary A type assertion immediately after JSON parsing suppresses compiler uncertainty without establishing any runtime fact. Parsed data can contain missing fields, unexpected strings, invalid ranges, or extra properties, while an assertion simply tells TypeScript to accept the declared shape. Because unknown requires narrowing before operations are permitted, it is the safer representation for an untrusted boundary. TypeScript type Classification = { label: "bug" | "feature" | "question"; confidence: number; }; const candidate: unknown = JSON.parse(raw); const result = candidate as Classification; The final line creates a compile-time claim with no runtime check. Risk rises when the result controls database writes, tool calls, or authorization-sensitive workflows. A reliable boundary keeps the value as unknown until validation establishes the required structure. Make the Schema the Executable Contract A runtime schema library closes the gap between erased TypeScript types and actual JavaScript values. Zod is designed to define runtime schemas while inferring static TypeScript types from the same definition, which allows one artifact to serve both validation and compile-time ergonomics. z.strictObject() is especially useful at an LLM boundary because unexpected keys become validation failures rather than silently extending the accepted surface. TypeScript const ClassificationSchema = z.strictObject({ label: z.enum(["bug", "feature", "question"]), confidence: z.number().min(0).max(1), rationale: z.string().min(1).max(800), }); type Classification = z.infer; The schema carries runtime constraints that a TypeScript type alone cannot enforce. The enum limits labels, numeric checks enforce the confidence interval, string bounds constrain explanations, and strict object handling rejects undeclared fields. The inferred Classification type follows the schema instead of being maintained separately, reducing static/runtime drift. Zod documents z.infer for static inference and structured errors for failed parses. Parse Before Data Enters Domain Logic Validation works best when it is treated as a boundary operation rather than scattered defensive checks. Raw model text first has to satisfy JSON syntax, then the resulting JavaScript value has to satisfy the runtime schema. Only the successfully parsed value should enter business logic. safeParse() returns a discriminated result that contains either validated data or a ZodError, which makes rejection paths explicit without using exceptions for normal validation flow. TypeScript function parseClassification(raw: string): Classification { let candidate: unknown; try { candidate = JSON.parse(raw); } catch { throw new Error("Model output is not valid JSON"); } const parsed = ClassificationSchema.safeParse(candidate); if (!parsed.success) { throw new Error(z.prettifyError(parsed.error)); } return parsed.data; } The important property is provenance. Classification comes from parsed.data after runtime validation, not from a cast. Validation errors can also remain structured telemetry because Zod exposes issue codes, paths, and messages identifying contract violations. Zod additionally provides z.prettifyError() when a human-readable representation is needed. Structured Output Reduces Syntax Risk, Not Trust Risk Modern LLM APIs can constrain generation against JSON Schema. OpenAI Structured Outputs, for example, is documented as enforcing supplied JSON Schema rather than merely producing syntactically valid JSON, and the current API distinguishes structured output from older JSON mode. That substantially reduces malformed payloads and schema-shape errors. It does not remove the need for an application-side trust boundary, because structured responses can still be interrupted, refused, or semantically wrong even when their shape is valid. OpenAI explicitly documents incomplete responses, refusal handling, and the possibility of mistakes inside structured outputs. Zod 4 can convert schemas directly to JSON Schema with z.toJSONSchema(), making it possible to drive model-side constrained generation and application-side validation from the same source definition. The conversion targets JSON Schema Draft 2020-12 by default, although not every Zod feature is representable as JSON Schema; transforms, Date, Map, Set, and several other constructs require different handling. That limitation favors separating wire contracts from richer domain representations. The model-facing schema can remain JSON-native, while post-validation code converts ISO strings into Date objects, resolves identifiers, or calculates derived fields. Zod distinguishes schema input and output types and documents that some transformations cannot be soundly represented in JSON Schema. TypeScript const responseSchema = z.toJSONSchema(ClassificationSchema); const response = await client.responses.create({ model: modelName, input: prompt, text: { format: { type: "json_schema", name: "classification", strict: true, schema: responseSchema, }, }, }); Schema-constrained decoding and runtime validation solve different problems. Provider-side constraints narrow generation; the local parser verifies what reached the application boundary. Both layers remain useful when responses are cached or replayed, multiple providers feed the same pipeline, or tests bypass generation. JSON Schema defines structure and constraints, while validation still requires a validator where data is consumed. Model Business Invariants Explicitly Structural validity is necessary but insufficient. A payload can satisfy field types while violating domain rules. A confidence value within range does not prove that the classification is correct, a valid identifier does not prove that the referenced record exists, and a syntactically valid tool argument does not prove that an action is authorized. Structured Outputs documentation similarly notes that schema-conforming responses can still contain mistakes. Runtime schemas should therefore encode deterministic invariants while leaving truth, authorization, and external-state checks to domain services. Cross-field rules belong in the executable contract when they are deterministic. A routing decision, for example, may require an escalation reason whenever the model chooses an escalation action. Zod refinements make such conditions enforceable without widening downstream code with repeated checks. TypeScript const DecisionSchema = z.strictObject({ action: z.enum(["answer", "escalate"]), answer: z.string().optional(), reason: z.string().optional(), }).refine( value => value.action !== "escalate" || Boolean(value.reason), { error: "Escalation requires a reason" } ); This keeps deterministic validation close to the contract without implying that a schema can establish facts outside the payload. Database existence, permissions, rate limits, and transactional constraints remain separate runtime responsibilities. JSON Schema is defined around the structure and constraints of a JSON instance, making it a format contract rather than external-state verification. Fail Closed and Treat Validation as a Signal A production pipeline should not blindly coerce invalid output into the expected type. Silent defaults can turn model failures into plausible data. Invalid output is better treated as a controlled failure with bounded retry, explicit refusal and incomplete-response branches, and validation telemetry. Zod provides machine-readable issues, while structured-output APIs expose interruption and refusal states before domain execution. A model contract also benefits from explicit versioning. Schema changes such as renamed enum values, newly required fields, or tighter bounds can invalidate cached outputs and replayed events even when current generation is correct. Recording a schema identifier or application contract version beside generated data makes compatibility decisions explicit and prevents historical payloads from being interpreted under a newer contract. JSON Schema supports identifiers and dialect declarations for machine-readable schema metadata. The central engineering rule is simple: TypeScript types describe what trusted code may assume, not what an LLM actually produced. Untrusted AI output should enter the system as unknown, cross an executable runtime schema, and become a domain type only after successful validation. Provider-side structured output can reduce formatting failures, but it cannot replace local validation or domain checks. A pipeline built around that boundary preserves TypeScript’s strongest benefit without confusing compile-time confidence for runtime truth, and it converts probabilistic model output into data that deterministic application code can safely reason about. More
Replacing JSON With Protobuf in Your Microservice Mesh: A Zero-Downtime Migration Blueprint
Replacing JSON With Protobuf in Your Microservice Mesh: A Zero-Downtime Migration Blueprint
By Bansidhar kadiya
Angular Apps Don’t Need Another Chatbot: Building Agentic UI Workflows With TypeScript
Angular Apps Don’t Need Another Chatbot: Building Agentic UI Workflows With TypeScript
By Bhanu Sekhar Guttikonda DZone Core CORE
How to Test GET API Requests With Playwright TypeScript
How to Test GET API Requests With Playwright TypeScript
By Faisal Khatri DZone Core CORE
Fetching Information Randomly From JSON Using Node, Nuxt, Express
Fetching Information Randomly From JSON Using Node, Nuxt, Express

Nuxt.js is a popular framework for Vue.js, and it is widely used for websites that require server-side rendering. It is similar to the Next.js framework for React.js. In this article, I’m going to share how you can fetch values randomly from a static JSON file with a Node and Express server. To make this example more realistic, we will store some words with their meanings in the words.json file in a static folder at the root. The necessary frameworks and libraries need to be installed on your machine, and basic knowledge is required: Node.js/ expressVue.js/Nuxt.js CLIJSONnpm (Source) #static/words.json JSON [ { "word": "lysis", "type": "noun", "meaning": "The resolution or favorable termination of a disease, coming on gradually and not marked by abrupt change." }, { "word": "outwit", "type": "verb", "meaning": "To surpass in wisdom, esp. in cunning; to defeat or overreach by superior craft." }, { "word": "completive", "type": "adjective", "meaning": "Making complete." } ] The words.json file above contains a few words, each with its type and meaning. Next, we need an Express server that listens for API calls from the front Nuxt/Vue page. #server.js JavaScript const path = require('path'); const express = require('express'); const cors = require('cors'); const fs = require('fs'); const app = express(); const PORT = 3001; app.use(cors()); let words = JSON.parse(fs.readFileSync('static/words.json', 'utf-8')); words = words.map(w => ({ ...w, type: w.type ? w.type.trim().toLowerCase() : '' })); app.get('/api/types', (req, res) => { const uniqueTypes = [...new Set(words.map(w => w.type))].sort(); res.json(uniqueTypes); }); //Random word generator with filters app.get('/api/random', (req, res) => { let filtered = [...words]; const { type, start, end, op, len, count } = req.query; const requestedType = type ? type.trim().toLowerCase() : ''; const startLetter = start ? start.trim().toLowerCase() : ''; const endLetter = end ? end.trim().toLowerCase() : ''; const wordLength = len ? parseInt(len) : null; const limit = parseInt(count) || 5; if (startLetter) { filtered = filtered.filter(w => w.word?.toLowerCase().startsWith(startLetter)); } if (endLetter) { filtered = filtered.filter(w => w.word?.toLowerCase().endsWith(endLetter)); } if (requestedType && requestedType !== 'all') { filtered = filtered.filter(w => w.type === requestedType); } if (op && wordLength) { if (op === '=') filtered = filtered.filter(w => w.word.length === wordLength); else if (op === '<') filtered = filtered.filter(w => w.word.length < wordLength); else if (op === '>') filtered = filtered.filter(w => w.word.length > wordLength); } const result = []; const available = [...filtered]; while (result.length < limit && available.length > 0) { const index = Math.floor(Math.random() * available.length); result.push(available.splice(index, 1)[0]); } res.json(result); }); app.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}`); }); As the Nuxt.js server runs on port 3000 by default, we have specified port number 3001. Next up is the Vue/nuxt.js code. With an input selection form and a “generate words” button. #pages/index.vue Vue.js Component

Random Words List

Your generated words will appear here.

  • {{ word.word } ({{ word.type })

    {{ word.meaning }

This is the normal HTML form that will be placed inside