Skip to content

Using the API from TypeScript ​

This example uses the standard fetch function and hand-written types for the two responses, so it needs no GraphQL client library. It runs on Node.js 18 or later and 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.ts. For the Node.js types, install typescript and @types/node (npm install --save-dev typescript @types/node). Node.js 24 runs the file directly (node jobs.ts, or node jobs.ts create to create a job); with older versions, compile it first (npx tsc --target es2022 --module nodenext --types node jobs.ts) and run node jobs.js.

typescript
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 }
  }
}`;

interface GraphQLResponse<T> {
  data?: T;
  errors?: { message: string }[];
}

interface Job {
  id: string; // a JobID such as "job_01M0SYJHB7FX6BKYVZZ7EAG4Y8"
  name: string;
  quantity: number;
  status: string; // a JobStatus value such as "DRAFT" or "CONFIRMED"
  dueAt: string | null; // ISO 8601
}

interface ListJobsData {
  listJobs: {
    totalCount: number | null;
    pageInfo: { hasNextPage: boolean; endCursor: string | null };
    edges: { node: Job }[];
  };
}

interface CreateJobData {
  createJob: {
    job: Pick<Job, 'id' | 'name' | 'status'> | null;
    errors: { path: string; message: string }[];
  };
}

// POST one operation and return its data. Throws on any top-level error.
async function graphql<T>(query: string, variables: Record<string, unknown> = {}): Promise<T> {
  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()) as GraphQLResponse<T>;
  // Authentication, permission and query errors arrive here with HTTP 200.
  if (result.errors?.length || !result.data) {
    throw new Error(result.errors?.map((e) => e.message).join('; ') ?? 'No data in response');
  }
  return result.data;
}

// Yield every job, 100 per request.
async function* listJobs(): AsyncGenerator<Job> {
  let after: string | null = null;
  while (true) {
    const { listJobs: page }: ListJobsData = await graphql<ListJobsData>(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: { name: string; quantity: number; externalId?: string }) {
  const { createJob: payload } = await graphql<CreateJobData>(CREATE_JOB, { input });
  // Validation problems come back in the payload, not as top-level errors.
  if (payload.errors.length || !payload.job) {
    throw new Error(payload.errors.map((e) => `${e.path}: ${e.message}`).join('; '));
  }
  return payload.job;
}

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

main().catch((error: unknown) => {
  console.error(error);
  process.exitCode = 1;
});

Notes ​

  • The types describe only the fields the two operations select. To generate them from the schema instead, download the SDL with curl -o schema.graphql https://<your-tenant-host>/graphql/schema and give that file to a code generator such as GraphQL Code Generator.
  • status is typed as string. See JobStatus for the values.
  • graphql() throws for both kinds of failure: an HTTP error status, and a response whose errors array is not empty.
  • externalId must be unique in your tenant. Running create twice with the same value returns a validation error in createJob.errors.

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