Skip to navigation

TypeScript quickstart

Run a server-side Skin Analysis with the published TypeScript SDK.

This guide uploads one image, submits a Skin Analysis job, and reads its result with @morphiaone/sdk. It runs on Node.js 20 or newer.

Release status. API 1.0.0-rc.1 is a release candidate: request and response shapes may still change before the stable release. SDK 0.2.2 is published as a Developer Preview (@morphiaone/sdk on npm and morphiaone on PyPI).

You need an API key first; Get access takes you through it. Use a pk_test_ key while you build.

Install the SDK

npm install --save-exact @morphiaone/sdk@0.2.2

To run the TypeScript file directly on Node.js 20, add tsx to your project and run the file with it:

npm install --save-dev tsx
npx tsx quickstart.mts

Keep both values in your server environment. Never put an API key in browser code, mobile app bundles, source control, or a chatbot client.

export MORPHIAONE_BASE_URL="https://api.morphiaone.ai"
export MORPHIAONE_API_KEY="pk_test_..." # the key you created in the Platform

Upload, submit, and wait

Save this as quickstart.mts in your server-side Node.js project. Replace face.jpg with an image you are allowed to process.

import { readFile } from "node:fs/promises";
import { MorphiaOne } from "@morphiaone/sdk";
const { MORPHIAONE_API_KEY: apiKey, MORPHIAONE_BASE_URL: baseUrl } = process.env;
if (!apiKey || !baseUrl) {
throw new Error("Set MORPHIAONE_API_KEY and MORPHIAONE_BASE_URL");
}
const client = new MorphiaOne({ apiKey, baseUrl });
const source = await client.assets.upload(await readFile("face.jpg"));
const accepted = await client.skin.analyze({
assetId: source.id,
concerns: ["acne", "redness"],
idempotencyKey: "order-48213-skin",
});
const completed = await client.jobs.wait(accepted.id);
if (!completed.output) throw new Error("Completed job has no output");
if (completed.output.known && completed.output.type === "skin.analysis") {
for (const concern of completed.output.concerns) {
console.log(concern.concern, concern.status, concern.score);
}
} else if (!completed.output.known) {
console.log("Unrecognized output type:", completed.output.type);
}

jobs.wait polls using the server’s Retry-After and resolves with a succeeded Job. If processing fails, it throws ProcessingError. A timeout stops local waiting; it does not cancel work already accepted by the server. Store the Job id if your application needs to resume later. See Jobs and outputs.

Make retries safe

The idempotencyKey above comes from the order that owns the work. Keep that key for every retry of the same logical request. Use a new key for a different request. If you omit it, the SDK creates a key for that call and reuses it across its own transport retries, but that key cannot survive an application restart.

See Idempotency for replay behavior and Retries and backoff for retryable failures.

Handle errors by type and code

import { ApiError, MorphiaOneError, ProcessingError } from "@morphiaone/sdk";
try {
await client.jobs.wait(accepted.id);
} catch (error) {
if (error instanceof ProcessingError) {
console.error("Job failed", error.code, error.jobId);
} else if (error instanceof ApiError) {
console.error("Request failed", error.status, error.code, error.requestId);
} else if (error instanceof MorphiaOneError) {
console.error("SDK failure", error.code, error.requestId);
} else {
throw error;
}
}

Branch on code, not the human-readable message. An HTTP success for the submit means the Job was accepted; a processing failure is reported by the Job and is distinct from an HTTP error. See Error handling.

Continue with another product or language