클라이언트
createClient로 Nodaro SDK 클라이언트를 만들고 기본 URL, 인증, 타임아웃, 워크스페이스를 설정하는 방법과 클라이언트가 제공하는 모든 리소스를 안내합니다.
클라이언트는 createClient()가 반환하는 객체입니다. NodaroClient는 내 기본 URL, 인증 공급자, 설정을 보관하고, client.workflows나 client.nodes처럼 Nodaro API의 모든 부분을 리소스로 제공합니다. 클라이언트는 한 번 만들고 모든 호출에서 다시 사용하세요.
클라이언트 만들기
import { createClient, StaticTokenAuth } from "@nodaro/sdk"
const client = createClient({
baseUrl: "https://app.nodaro.ai",
auth: new StaticTokenAuth(process.env.NODARO_TOKEN!),
timeoutMs: 120_000,
})createClient(options: ClientOptions): NodaroClient속성
타입
NodaroClient는 클래스로도 내보내지므로, 클라이언트를 받는 함수에 타입을 지정할 수 있습니다.
import type { NodaroClient } from "@nodaro/sdk"
async function countWorkflows(client: NodaroClient, projectId: string) {
const { data } = await client.workflows.list({ projectId })
return data.length
}클라이언트의 리소스
모든 리소스는 createClient가 만들며, client.<resource>로 접근합니다.
| 리소스 | 다루는 내용 | 참조 |
|---|---|---|
client.workflows | 워크플로: 만들기, 업데이트, 공유, 내보내기, 가져오기, 실행 | 워크플로와 프로젝트 |
client.projects | 워크플로를 담는 프로젝트 | 워크플로와 프로젝트 |
client.sessions | 프로젝트 안의 생성 세션 | 워크플로와 프로젝트 |
client.executions | 워크플로 전체의 실행 | 작업과 실행 |
client.jobs | 개별 생성 작업과 그 평가, 필터 | 작업과 실행 |
client.webhooks | 작업이 끝날 때 Nodaro가 호출하는 엔드포인트 | 작업과 실행 |
client.videoPro | 동영상 생성 Pro 실행 중지 또는 이어 가기 | 작업과 실행 |
client.nodes | 노드 카탈로그와 단일 노드 실행 | 노드 실행 |
client.apps | 게시된 앱과 그 실행 | 앱과 템플릿 |
client.templates | 템플릿 마켓플레이스 | 앱과 템플릿 |
client.tutorials | 튜토리얼 동영상과 튜토리얼 워크플로 | 앱과 템플릿 |
client.llm | 언어 모델의 구조화된 출력 | LLM과 Reduce |
client.reduce | 여러 결과 중 가장 좋은 것을 고르거나 하나로 합치기 | LLM과 Reduce |
client.uploads | 파일 업로드 | 미디어와 업로드 |
client.library | 내가 저장한 미디어 | 미디어와 업로드 |
client.gallery | 공개 갤러리와 내 즐겨찾기 | 미디어와 업로드 |
client.media | 미디어 다운로드, 자르기, 자막, 오버레이, 콜라주 | 미디어와 업로드 |
client.voices | 보이스, 보이스 체인저, 보이스 디자인, 더빙 | 보이스와 오디오 |
client.audio | 오디오 분리, 추출, 믹스, 자르기, 텍스트 변환 | 보이스와 오디오 |
client.edit | 무음 감지, 오디오 동기화, 편집 계획, EDL 렌더링 | 편집 |
client.scene3d | 편집 가능한 3D 장면과 3D 렌더링 Pro | 3D 장면 |
client.characters | 캐릭터 | 캐릭터 |
client.locations | 장소 | 장소 |
client.objects | 오브젝트와 소품 | 오브젝트와 크리처 |
client.creatures | 동물과 크리처 | 오브젝트와 크리처 |
client.community | 함께 쓰는 커뮤니티 에셋 라이브러리 | 커뮤니티 라이브러리 |
client.studio | 스튜디오 프로덕션 | 스튜디오 프로덕션 |
client.shots | 공유 링크 뒤에 있는 공유 샷 기록 | 스튜디오 프로덕션 |
client.recast | Recast 실행과 작성된 스크립트 | Recast |
client.pipelines | 스토리를 동영상으로 만드는 파이프라인 | 파이프라인 |
client.copilot | Copilot 스레드, Nodaro 앱 안에서만 사용 | Copilot |
client.models | 모델 카탈로그 | 모델과 크레딧 |
client.credits | 내 잔액과 모델 가격 | 모델과 크레딧 |
client.billing | 요금제, 가격, 내 구독 | 모델과 크레딧 |
client.pickerCatalogs | 각 피커에서 쓸 수 있는 옵션 | 피커, 프리셋, 프롬프트 |
client.catalogs | 모든 피커 카탈로그를 한 번의 호출로 | 피커, 프리셋, 프롬프트 |
client.presets | 저장된 노드 프리셋과 기본 제공 노드 프리셋 | 피커, 프리셋, 프롬프트 |
client.palettes | 내 컬러 팔레트 | 피커, 프리셋, 프롬프트 |
client.moodboards | 내 무드보드 | 피커, 프리셋, 프롬프트 |
client.promptHelper | 프롬프트 마법사 | 피커, 프리셋, 프롬프트 |
client.organizations | 조직, 멤버, 초대 | 조직과 워크스페이스 |
client.workspaces | 워크스페이스, 멤버, 참여 코드 | 조직과 워크스페이스 |
client.developerApps | 내가 소유한 OAuth 앱 | OAuth와 개발자 앱 |
client.oauth | 코드 교환, 토큰 폐기, 동의 화면 데이터 | OAuth와 개발자 앱 |
클라이언트 자체에는 메서드가 5개 더 있습니다. me(), connectedApps(), revokeConnectedApp(), withWorkspace(), request()입니다.
메서드가 반환하는 값
- 엔벨로프는 그대로 유지됩니다. 엔드포인트가
{ "data": ... }로 응답하면 메서드는 그 엔벨로프로 이행하므로,const { data } = await client.workflows.get(id)처럼 작성합니다. 페이지 단위 목록에는nextCursor같은 커서가data옆에 추가됩니다. - 일부 리소스는 페이로드를 반환합니다. 몇몇 메서드는 응답을 풀어서 반환합니다. 예를 들어
client.characters.list()는{ characters, nextCursor }로,client.credits.balance()는 잔액 자체로 이행합니다. 정확한 반환 타입은 각 참조 페이지에 나와 있습니다. - 삭제와 취소는 보통
{ success: true }로 이행합니다. - 필드 이름은 전송 형식을 따릅니다.
Job은 API가 그렇게 보내므로output_data나created_at같은 snake_case 필드를 사용합니다.Workflow와WorkflowExecution은 camelCase를 사용합니다.
모든 응답 타입과 입력 타입이 내보내지므로 import type으로 가져올 수 있습니다. 타입을 참조하세요.
me()
me(): Promise<UserIdentity & MeOrganizations>현재 토큰 뒤에 있는 사용자 정보를 반환합니다(GET /v1/me). 유효한 토큰이면 API 토큰이든 OAuth 액세스 토큰이든 브라우저 세션이든 그 소유자로 이행합니다. 토큰이 없거나 유효하지 않으면 UnauthorizedError가 발생합니다.
const me = await client.me()
console.log(me.email, me.tier)| 필드 | 타입 | 설명 |
|---|---|---|
id | string | Nodaro 사용자 ID입니다. |
email | string | 사용자의 이메일 주소입니다. |
displayName | string | null | 표시 이름이며, 설정되지 않았으면 null입니다. |
avatarUrl | string | null | 아바타 URL이며, 설정되지 않았으면 null입니다. |
tier | string | 저장된 구독 등급이며, "free"나 "pro" 같은 값입니다. 종량제를 포함해 실제로 적용되는 등급은 client.credits.balance()의 effectiveTier에서 읽으세요. |
isAdmin | boolean | 사용자가 관리자인지 여부입니다. 무엇을 보여 줄지 정할 때만 사용하세요. 모든 권한은 서버가 직접 확인합니다. |
조직이 있는 Nodaro Cloud 인스턴스에서는 결과에 organizations, workspaces, lastWorkspaceId, organizationsUnavailable도 포함됩니다. 세 가지 상태를 각각 다르게 처리하세요.
| 보이는 것 | 의미 | 할 일 |
|---|---|---|
| 필드가 없음 | 인스턴스에 조직이 없음 | 워크스페이스 전환기를 표시하지 마세요. |
| 필드가 있지만 비어 있음 | 계정이 어떤 조직에도 속하지 않음 | 조직을 만들거나 참여하도록 안내하세요. |
organizationsUnavailable: true | 조회에 실패함 | 이미 선택된 항목을 유지하세요. 사용자에게 접근 권한을 잃었다고 알리지 마세요. |
connectedApps()
connectedApps(): Promise<ConnectedApp[]>로그인한 사용자의 계정에 접근할 수 있는 모든 앱을 최근 권한 부여 순으로 반환합니다(GET /v1/me/connected-apps). 목록에는 개발자 앱, AI 어시스턴트의 MCP 커넥터, 셀프 호스팅 설치가 포함됩니다. 연결된 앱 설정 화면의 데이터입니다. 권한 부여가 무엇인지는 OAuth를 참조하세요.
이 메서드에는 로그인한 브라우저 세션이 필요합니다. supabaseAuth()나 createSharedSupabaseClient의 세션 토큰을 받습니다. API 토큰(ndr_…)이나 앱 토큰(ndr_app_…)은 UnauthorizedError로 거부하므로, 앱은 자신이 쓰는 권한 부여를 나열하거나 취소할 수 없습니다. 서버 측 통합에서는 호출할 수 없습니다.
const apps = await client.connectedApps()
// [{ authorizationId, name, kind, homepageUrl, scopes, connectedAt, lastUsedAt }]| 필드 | 타입 | 설명 |
|---|---|---|
authorizationId | string | 권한 부여입니다. revokeConnectedApp()에 전달하세요. |
name | string | null | 앱의 이름입니다. 스스로 등록한 MCP 클라이언트는 이름을 직접 정했습니다. |
kind | DeveloperAppKind | user(개발자 앱), first_party_mcp, dynamic_mcp(스스로 등록한 MCP 클라이언트), community_instance(셀프 호스팅 설치) 중 하나입니다. |
homepageUrl | string | null | 앱이 제공한 경우 앱의 홈페이지입니다. |
scopes | DeveloperAppScope[] | 권한 부여가 가진 스코프입니다. |
connectedAt | string | 사용자가 접근을 허용한 시각으로, ISO 8601 형식입니다. |
lastUsedAt | string | null | 권한 부여로 발급된 토큰이 가장 최근에 쓰인 시각이며, 한 번도 쓰이지 않았으면 null입니다. |
revokeConnectedApp(authorizationId)
revokeConnectedApp(authorizationId: string): Promise<void>앱의 접근을 끝냅니다(POST /v1/me/connected-apps/:authorizationId/revoke). 권한 부여와 그로 발급된 모든 토큰을 즉시 취소하므로, 앱의 다음 호출은 401로 실패합니다. connectedApps()처럼 로그인한 브라우저 세션이 필요합니다. 권한 부여가 사용자의 것이 아니거나 이미 취소되었으면 NotFoundError를 발생시킵니다.
await client.revokeConnectedApp(app.authorizationId)withWorkspace(workspaceId)
withWorkspace(workspaceId: string | null): NodaroClientworkspaceId에서 동작하는 새 클라이언트를 반환합니다. 새 클라이언트는 원래 클라이언트의 인증, 기본 URL, 타임아웃, fetch를 공유합니다. 내 개인 공간을 쓰려면 null을 전달하세요.
const classroom = client.withWorkspace(workspaceId)
await classroom.workflows.run(workflowId) // runs in the workspace
await client.workflows.run(workflowId) // runs in the personal space이 메서드는 현재 클라이언트를 바꾸지 않고 새 클라이언트를 반환합니다. 따라서 한 클라이언트에서 동시에 실행되는 두 작업의 워크스페이스가 서로 섞일 일이 없습니다.
워크스페이스는 범위를 정할 뿐, 접근 권한은 정하지 않습니다. 목록을 어느 워크스페이스에서 읽을지, 새 항목을 어디에 만들지를 정합니다. ID로 지정한 항목을 읽고, 바꾸고, 삭제하고, 실행할 수 있는지는 그 항목이 속한 워크스페이스에 따라 정해집니다. 워크스페이스를 빠뜨려도 내 작업이 숨겨지지 않고, 잘못 지정해도 다른 사람의 작업에 닿지 않습니다.
워크스페이스는 Nodaro Cloud의 조직에 속합니다. 조직과 워크스페이스와 워크스페이스를 참조하세요.
request(method, path, options)
request<T>(method: string, path: string, options?: {
body?: unknown
query?: Record<string, string | number | boolean | undefined>
headers?: Record<string, string>
signal?: AbortSignal
}): Promise<T>모든 엔드포인트에 요청을 보냅니다. 아직 리소스 메서드가 없는 몇몇 엔드포인트에 사용하세요. 인증 헤더와 워크스페이스를 추가하고, body를 JSON으로 보내고, timeoutMs를 적용하며, 리소스 메서드와 같은 타입 지정 오류를 발생시킵니다.
// The same request that client.jobs.list() sends
const page = await client.request<{ data: unknown[]; next: string | null }>("GET", "/v1/jobs", {
query: { type: "llm-structured", limit: 20 },
})FormData 본문은 멀티파트 업로드로 전송됩니다. 값이 undefined인 쿼리 항목은 빠집니다. 모든 엔드포인트와 필드는 REST API 참조에 나와 있습니다.
타임아웃과 사용자 지정 fetch
timeoutMs는 제한 시간보다 오래 걸리는 요청을 중단하며, 기본값은 60초입니다. 대부분의 생성은 적당한 HTTP 타임아웃보다 오래 걸리므로, 생성을 시작한 다음 작업을 폴링하세요. client.nodes.runAndWait()가 두 가지를 모두 처리합니다. 스트리밍 메서드인 client.copilot.stream()과 client.media.downloadVideoProgress()는 몇 분 동안 열려 있도록 만들어졌으므로 타임아웃을 적용하지 않습니다.
요청이 전달되는 방식을 바꾸려면 직접 만든 fetch를 전달하세요.
- 테스트. 목에서 미리 준비한
Response객체를 반환합니다. - 재시도. 전역
fetch를 5xx 응답에서 재시도하는 헬퍼로 감쌉니다. - 추적. 사용하는 추적 또는 모니터링 라이브러리로 감쌉니다.
const client = createClient({
baseUrl: "https://app.nodaro.ai",
auth: new StaticTokenAuth(process.env.NODARO_TOKEN!),
fetch: (input, init) => tracedFetch(input, init),
})런타임과 브라우저
SDK는 fetch와 URL만 사용합니다. 둘 다 Node.js 20 이상, 최신 브라우저, React Native, Cloudflare Workers, Deno, Bun에서 전역으로 제공됩니다. 폴리필은 필요하지 않습니다.
- OAuth 토큰과 CORS. OAuth 액세스 토큰으로 Nodaro를 호출하는 브라우저 앱은 개발자 앱의
allowedOrigins에 등록된 출처에서 실행되어야 합니다. OAuth와 개발자 앱을 참조하세요. - 세션과 CORS.
supabaseAuth를 사용하는 브라우저 앱은 이 목록으로 확인하지 않습니다. - 클라이언트 레이블. 서버에서는 SDK가
X-Nodaro-Client: sdk/<version>을 보내고, Nodaro는 이 값을 각 작업의 출처로 기록합니다. 브라우저에서는 브라우저의Origin헤더가 이미 내 앱을 알려 주므로 기본 레이블을 보내지 않습니다. 직접 설정한clientLabel은 항상 전송됩니다.
자주 묻는 질문
Nodaro SDK에서는 어떤 기본 URL을 사용해야 하나요?
Nodaro Cloud에서는 https://app.nodaro.ai를 사용하고, 셀프 호스팅 설치에서는 내 인스턴스의 주소를 사용하세요. Nodaro와 같은 출처에서 제공되는 브라우저 앱에서는 빈 문자열을 사용하세요.
기본 요청 타임아웃은 얼마인가요?
60초입니다. timeoutMs 옵션으로 바꿀 수 있습니다. 생성은 어떤 요청보다도 오래 걸리므로, 타임아웃을 늘리는 대신 생성을 시작한 다음 작업을 폴링하세요.
워크스페이스에서 요청을 보내려면 어떻게 하나요?
client.withWorkspace(workspaceId)를 호출하세요. 모든 요청을 그 워크스페이스에서 보내는 새 클라이언트가 반환되며, 원래 클라이언트는 계속 내 개인 공간에서 동작합니다.
SDK 메서드가 없는 엔드포인트는 어떻게 호출하나요?
client.request(method, path, options)를 사용하세요. 다른 모든 리소스 메서드와 같은 인증 헤더를 보내고, 같은 타임아웃을 적용하며, 같은 타입 지정 오류를 발생시킵니다.
관련 문서
TypeScript SDK
인증
오류
조직과 워크스페이스
타입
최종 업데이트