Skip to main content

TypeScript examples

fetch

const API_URL = "https://uthana.com/graphql";
const API_KEY = "YOUR_API_KEY";

async function getMotion(motionId: string) {
  // Encode API key for Basic auth
  const authString = btoa(`${API_KEY}:`);

  const res = await fetch(API_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Basic ${authString}`,
    },
    body: JSON.stringify({
      query: `
        query GetMotion($id: String!) {
          motion(id: $id) {
            id
            name
            created
          }
        }
      `,
      variables: { id: motionId },
    }),
  });

  const json = await res.json();
  return json.data.motion;
}

// Usage
getMotion("m3G3XSJrjEJH").then(console.log);

React Query + graphql-request

import { GraphQLClient, gql } from "graphql-request";
import { useQuery } from "@tanstack/react-query";

const API_URL = "https://uthana.com/graphql";
const API_KEY = "YOUR_API_KEY";

// Encode API key for Basic auth
const authString = btoa(`${API_KEY}:`);

const client = new GraphQLClient(API_URL, {
  headers: {
    Authorization: `Basic ${authString}`,
  },
});

const GetMotion = gql`
  query GetMotion($id: String!) {
    motion(id: $id) {
      id
      name
      created
    }
  }
`;

export function useMotion(motionId: string) {
  return useQuery({
    queryKey: ["motion", motionId],
    queryFn: async () => {
      const data = await client.request(GetMotion, { id: motionId });
      return data.motion;
    },
    enabled: !!motionId,
  });
}

// Usage in a component:
// const { data, isLoading } = useMotion('m3G3XSJrjEJH');

Apollo Client

import { ApolloClient, InMemoryCache, gql } from "@apollo/client";

// Encode API key for Basic auth
const authString = btoa(`YOUR_API_KEY:`);

const client = new ApolloClient({
  uri: "https://uthana.com/graphql",
  cache: new InMemoryCache(),
  headers: {
    Authorization: `Basic ${authString}`,
  },
});

const GET_MOTION = gql`
  query GetMotion($id: String!) {
    motion(id: $id) {
      id
      name
      created
    }
  }
`;

// Usage:
client
  .query({
    query: GET_MOTION,
    variables: { id: "m3G3XSJrjEJH" },
  })
  .then((result) => {
    console.log(result.data.motion);
  });

Downloading motions

Download a motion with character mesh

const API_KEY = "YOUR_API_KEY";
const CHARACTER_ID = "your-character-id";
const MOTION_ID = "your-motion-id";

// Encode API key for Basic auth
const authString = btoa(`${API_KEY}:`);

async function downloadMotion(characterId: string, motionId: string, format: "fbx" | "glb") {
  const url = `https://uthana.com/motion/file/motion_viewer/${characterId}/${motionId}/${format}/${characterId}-${motionId}.${format}`;

  const response = await fetch(url, {
    headers: {
      Authorization: `Basic ${authString}`,
    },
  });

  if (!response.ok) {
    throw new Error(`Failed to download motion: ${response.statusText}`);
  }

  const blob = await response.blob();
  return blob;
}

// Usage:
// const fbxBlob = await downloadMotion(CHARACTER_ID, MOTION_ID, 'fbx');
// const glbBlob = await downloadMotion(CHARACTER_ID, MOTION_ID, 'glb');

Download a motion-only GLB (without character mesh)

const API_KEY = "YOUR_API_KEY";
const CHARACTER_ID = "your-character-id";
const MOTION_ID = "your-motion-id";
const APP_ID = "motion_viewer"; // or 'training'

// Encode API key for Basic auth
const authString = btoa(`${API_KEY}:`);

async function downloadMotionOnlyGlb(characterId: string, motionId: string, appId: string = "motion_viewer") {
  const url = `https://uthana.com/motion/animation/${appId}/${characterId}/${motionId}/glb/${characterId}-${motionId}.glb`;

  const response = await fetch(url, {
    headers: {
      Authorization: `Basic ${authString}`,
    },
  });

  if (!response.ok) {
    throw new Error(`Failed to download motion-only GLB: ${response.statusText}`);
  }

  const blob = await response.blob();
  return blob;
}

// Usage:
// const motionOnlyBlob = await downloadMotionOnlyGlb(CHARACTER_ID, MOTION_ID, APP_ID);
// Motion-only GLB files contain animation data without the character mesh. The character ID is required to ensure the correct skeleton is used.