Skip to content

Using the API from Node.js ​

This example uses the fetch function built into Node.js 18 and later, so there is nothing to install. It needs an API token and the COCOCO_URL and COCOCO_TOKEN environment variables described in Calling the CoCoCo API.

The operations ​

graphql
query ListJobs($first: Int, $after: String) {
  listJobs(first: $first, after: $after) {
    totalCount
    pageInfo { hasNextPage endCursor }
    edges { node { id name quantity status dueAt } }
  }
}
graphql
mutation CreateJob($input: CreateJobInput!) {
  createJob(input: $input) {
    job { id name status }
    errors { path message }
  }
}

The program ​

Save this as jobs.mjs. node jobs.mjs lists every job; node jobs.mjs create creates one.

javascript
const ENDPOINT = `${process.env.COCOCO_URL.replace(/\/$/, '')}/graphql`;
const TOKEN = process.env.COCOCO_TOKEN;

const LIST_JOBS = `query ListJobs($first: Int, $after: String) {
  listJobs(first: $first, after: $after) {
    totalCount
    pageInfo { hasNextPage endCursor }
    edges { node { id name quantity status dueAt } }
  }
}`;

const CREATE_JOB = `mutation CreateJob($input: CreateJobInput!) {
  createJob(input: $input) {
    job { id name status }
    errors { path message }
  }
}`;

// POST one operation and return its data. Throws on any top-level error.
async function graphql(query, variables = {}) {
  const response = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ query, variables }),
  });
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  }
  const result = await response.json();
  // Authentication, permission and query errors arrive here with HTTP 200.
  if (result.errors?.length) {
    throw new Error(result.errors.map((e) => e.message).join('; '));
  }
  return result.data;
}

// Yield every job, 100 per request.
async function* listJobs() {
  let after = null;
  while (true) {
    const { listJobs: page } = await graphql(LIST_JOBS, { first: 100, after });
    for (const edge of page.edges) yield edge.node;
    if (!page.pageInfo.hasNextPage) return;
    after = page.pageInfo.endCursor;
  }
}

async function createJob(input) {
  const { createJob: payload } = await graphql(CREATE_JOB, { input });
  // Validation problems come back in the payload, not as top-level errors.
  if (payload.errors.length) {
    throw new Error(payload.errors.map((e) => `${e.path}: ${e.message}`).join('; '));
  }
  return payload.job;
}

if (process.argv[2] === 'create') {
  const job = await createJob({ name: 'Business cards', quantity: 500, externalId: 'ERP-10042' });
  console.log(`Created ${job.id} (${job.status})`);
} else {
  for await (const job of listJobs()) {
    console.log(job.id, job.status, job.quantity, job.name);
  }
}

Notes ​

  • graphql() throws for both kinds of failure: an HTTP error status, and a response whose errors array is not empty.
  • listJobs() follows pageInfo.endCursor until hasNextPage is false. The API returns at most 100 items per page.
  • externalId must be unique in your tenant. Running create twice with the same value returns a validation error in createJob.errors.
  • The file uses top-level await, which is why it has the .mjs extension.

Connect machines, automate workflows, and build custom apps for print production.