Last reviewed: 2026-08-01

Direct answer

A practical CometAPI image generation Node.js workflow has four parts: send the image request from server-side code, ask for base64 output, decode that output into bytes, and write those bytes to durable storage. Requesting b64_json avoids making your application depend on a temporary image URL. If a model returns a URL instead, download the file promptly and check that download’s HTTP status before saving it.

Keep both the endpoint and authorization value in runtime configuration. Set COMETAPI_IMAGE_ENDPOINT to the full secure URL ending in /v1/images/generations documented by CometAPI. Set COMETAPI_AUTH to the complete authorization value supplied for the runtime. Never print that value. In a ticket, screenshot, or example configuration, represent it only as [REDACTED].

The following script assumes fetch is available in the selected Node.js runtime. It requests one PNG, rejects non-success HTTP responses, checks the expected response field, decodes the base64 string with Buffer.from(), creates an output directory, and writes a new file without overwriting an existing one.

import { Buffer } from 'node:buffer';
import { mkdir, writeFile } from 'node:fs/promises';

const startedAt = Date.now();
let stage = 'configuration';

async function main() {
  const endpoint = process.env.COMETAPI_IMAGE_ENDPOINT;
  const authValue = process.env.COMETAPI_AUTH;
  const model = 'gpt-image-1-mini';

  if (!endpoint || !authValue) {
    console.error(JSON.stringify({
      event: 'image_generation_config_error',
      endpoint_present: Boolean(endpoint),
      auth_present: Boolean(authValue)
    }));
    process.exitCode = 1;
    return;
  }

  stage = 'request';
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: authValue
    },
    body: JSON.stringify({
      model,
      prompt: 'A clean product photograph of a cobalt ceramic mug on a white table',
      n: 1,
      size: '1024x1024',
      output_format: 'png',
      response_format: 'b64_json'
    })
  });

  stage = 'response';
  if (!response.ok) {
    console.error(JSON.stringify({
      event: 'cometapi_image_http_error',
      status: response.status,
      model,
      elapsed_ms: Date.now() - startedAt
    }));
    process.exitCode = 1;
    return;
  }

  const payload = await response.json();
  const encoded = payload?.data?.[0]?.b64_json;

  if (typeof encoded !== 'string' || encoded.length === 0) {
    throw new TypeError('Expected nonempty base64 image data');
  }

  stage = 'decode';
  const bytes = Buffer.from(encoded, 'base64');
  if (bytes.length === 0) {
    throw new TypeError('Decoded image is empty');
  }

  stage = 'write';
  await mkdir('generated', { recursive: true });
  const outputPath = `generated/image-${Date.now()}.png`;
  await writeFile(outputPath, bytes, { flag: 'wx' });

  stage = 'complete';
  console.info(JSON.stringify({
    event: 'cometapi_image_saved',
    status: response.status,
    model,
    output_format: 'png',
    output_path: outputPath,
    byte_count: bytes.length,
    elapsed_ms: Date.now() - startedAt
  }));
}

main().catch((error) => {
  console.error(JSON.stringify({
    event: 'cometapi_image_failed',
    stage,
    error_class: error instanceof Error ? error.name : 'UnknownError',
    elapsed_ms: Date.now() - startedAt
  }));
  process.exitCode = 1;
});

The log events are intentionally small. Safe operational fields include event, stage, HTTP status, model, output_format, relative output_path, byte_count, elapsed_ms, and booleans that report whether required configuration exists. Do not log the authorization value, prompt, full response body, base64 image data, temporary image URL, or raw upstream error body.

For the happy path, an operator should:

  1. Confirm the two runtime configuration values are present without displaying their contents.
  2. Run one request with a non-sensitive test prompt and n set to 1.
  3. Expect one cometapi_image_saved event with a success status and a positive byte_count.
  4. Confirm the named file exists under generated/ and opens as the requested format.
  5. Retain only the sanitized event and application correlation data needed for the deployment record.

For the error path, an operator should:

  1. Stop when the script emits image_generation_config_error, cometapi_image_http_error, or cometapi_image_failed.
  2. Use stage, status, model, and error_class to separate configuration, HTTP, decoding, and file-system failures.
  3. Verify the current model and parameter contract before changing the request.
  4. Avoid writing a file when the response is non-successful or the expected image field is absent.
  5. Retry only after classifying the failure; do not turn every non-success response into an automatic retry loop.

Who this is for

This guide is for developers who already have a Node.js service or script and need to persist generated images rather than display an upstream URL once. It is especially useful for thumbnail jobs, product mockups, content tools, and backend prototypes where the output must survive beyond the request that created it.

The request belongs on the server side. CometAPI’s refetched integration guide places the API call in a Node.js backend so the sensitive authorization value is not exposed to browser code. For a deeper repository boundary, follow the same principles described in keeping CometAPI keys out of tutorial repositories .

This is not a model-comparison guide, a browser gallery tutorial, or an image-editing walkthrough. It focuses narrowly on the generation-to-file boundary: request validation, response validation, byte decoding, file persistence, and useful failure evidence.

Key takeaways

  • Use the documented image-generation endpoint from server-side Node.js code.
  • Request b64_json when the selected model contract supports it, then decode it with Buffer.from(encoded, 'base64').
  • Treat response.ok and the expected JSON field as separate checks. A fulfilled fetch promise does not prove the server returned a successful HTTP status.
  • Write bytes with the Node.js promise-based file-system API and choose a no-overwrite mode for generated filenames.
  • Keep logs diagnostic but sanitized. Status, stage, model, elapsed time, and byte count are useful; prompts, authorization values, response bodies, and image payloads are not.
  • Verify the model-specific request contract before rollout because supported sizes, formats, and response fields can vary.

Sources checked

The CometAPI web-app image generation guide provides the first-party endpoint path, a server-side Node.js request pattern, the b64_json option, and the warning that generated URLs may be temporary.

The CometAPI gpt-image-1-mini API page confirms that gpt-image-1-mini is an image model exposed through the image-generation endpoint and documents format and quality controls at a high level.

The Node.js Buffer documentation establishes Buffer as Node.js’s byte representation and documents creating a buffer from a string with a specified encoding. That is the basis for decoding b64_json with the base64 encoding.

The Node.js file system documentation documents fsPromises.mkdir() and fsPromises.writeFile(), which the example uses to create the destination directory and persist the decoded bytes.

The MDN Fetch guide explains that fetch() can fulfill its promise even when the server returns an HTTP error status. Its explicit response.ok check supports the example’s separation of HTTP failures from network and parsing failures.

Contract details to verify

Treat the example as a small integration boundary, not as proof that every image model accepts the same payload. Before shipping, verify these details against the current CometAPI documentation and catalog:

  1. The complete API host and /v1/images/generations path.
  2. The exact model identifier available to the account and environment.
  3. Whether that model accepts response_format, output_format, size, and n with the values shown.
  4. Whether successful output appears in data[0].b64_json, data[0].url, or another documented field.
  5. Which image formats and dimensions the chosen model supports.
  6. The documented prompt limits and any model-specific editing or reference-image rules.
  7. The authentication setup, content type, and current error response contract.
  8. The storage policy for generated files, including retention, access control, and cleanup.

Do this verification before treating a missing field as an outage. A model or parameter mismatch is different from a network failure. The site’s model-catalog prepublish checks provide a useful companion process when examples will be maintained over time.

Failure modes

Missing runtime configuration. The script exits before a request and logs only endpoint_present and auth_present. This is a deployment configuration failure. Do not print either value while investigating it.

A non-success HTTP response. Because fetch() does not reject solely because a server returned an HTTP error status, the response.ok check is mandatory. Record the numeric status, model, and elapsed time. Then verify authentication and the current request contract through approved account and documentation channels. Do not copy the raw response into a public issue.

A successful status with an unexpected JSON shape. A response can be valid JSON yet lack data[0].b64_json. The selected model may return a URL, the requested response format may not be supported, or the contract may have changed. The safe behavior is to stop before writing and compare the actual documented response shape with the parser.

A temporary URL instead of base64. If the documented response contains data[0].url, fetch that value promptly, check the download response’s ok property, convert the returned ArrayBuffer to a Buffer, and write it through the same file path. Do not store the temporary URL as if it were the durable asset, and do not include it in routine logs.

Empty or unusable decoded bytes. Buffer.from() performs the string-to-byte conversion, but the application still owns validation. At minimum, reject zero-length output. For a production storage pipeline, validate the expected media type and file signature before publishing or serving the file.

File-system failure. The destination may be unwritable, storage may be full, or a generated name may already exist. The example uses mkdir() with recursive: true and writeFile() with flag: 'wx', so an existing path is not silently overwritten. Keep the generated directory outside source control and give the runtime only the storage permissions it needs.

Network interruption or an excessively long request. These failures reach the catch handler and include the current stage and error class. Add a bounded request policy that matches the workload before production. The pattern in client timeout handling for CometAPI requests can be adapted without changing the image response checks shown here.

Over-logging during diagnosis. Logging the complete payload may capture base64 image content, prompts, or upstream details. Log field presence and byte counts instead. If deeper inspection is necessary, use a restricted, short-lived diagnostic path with explicit access and deletion rules rather than broad application logs.

FAQ

Why request base64 instead of using the returned URL?

The refetched CometAPI guide describes generated image URLs as temporary and recommends downloading them promptly or requesting b64_json. Base64 increases the JSON response size, but it gives the application the image data in the same authenticated workflow and removes a second remote download from the happy path.

Can I assume every CometAPI image model supports these parameters?

No. The sources establish an image endpoint and document capabilities for specific examples, but they do not establish one immutable parameter set for every model. Verify the selected model’s current size, format, quality, count, and response-format rules before deployment.

Why check both response.ok and b64_json?

They protect different boundaries. response.ok identifies the HTTP success class. The b64_json check validates the application-level response shape. A successful HTTP status does not guarantee that the field your storage code expects is present and nonempty.

Should the script log the prompt for troubleshooting?

Not by default. Prompts can contain user or business data, and they are unnecessary for most transport and storage diagnosis. Use a local correlation identifier in the surrounding application if requests must be traced. Keep routine logs to the sanitized fields shown in the example.

What should happen when the API returns a URL?

Treat it as a separate download step. Check that the documented field is a nonempty string, fetch it promptly, require a successful download status, convert the resulting bytes to a Buffer, and write them to controlled storage. If any step fails, do not create a partial success record.

Is a positive byte count enough to publish the image?

It is enough for a minimal smoke check, not a complete media validation policy. Before serving generated files, confirm that the bytes match the expected format and that the file satisfies the application’s moderation, retention, and access rules.

Reader next step

Start with one non-sensitive prompt in a disposable environment. Configure the documented endpoint and runtime authorization value outside the source file, run the script, and require a single cometapi_image_saved event plus an image that opens locally. Then deliberately test three error paths: omit one configuration value, supply an unsupported request option in a test environment, and make the output directory unwritable. Confirm that each test stops cleanly without exposing the prompt, authorization value, payload, or returned image data.

Once those checks pass, add the request timeout policy, move the generated/ directory to the application’s managed storage layer, and document the selected model’s supported parameters beside the integration test. That leaves the production path with an explicit contract, a durable output, and logs that help operators without becoming a second copy of sensitive request data.