# Generate a client

> Typed clients from the OpenAPI document, and a Postman collection.

There is no SDK to install yet. The API's OpenAPI document gives you a typed client in the language
you use, and a Postman collection lets you try every call by hand.

## Which document

| Document | `config` | Use it for |
|---|---|---|
| [`/openapi.json`](/vod-express/openapi.json) on this site | Typed, field by field, for the build the [Config reference](/vod-express/configuration/reference) documents | Clients: a config with a typo does not compile |
| `GET /v1/openapi.json` on the API | An object, validated by the API against the project's build | Checking what the API serves right now |

Both list the same operations. Download the first:

```bash
curl -s https://docs.syncwords.com/vod-express/openapi.json -o vod-express.openapi.json
```

A client typed for one build still works with projects on another: the API validates `config`
against the project's own build. Regenerate after a build changes the fields you use.

## TypeScript

**openapi-typescript**

Types only, for use with `fetch` through [`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/):

```bash
npx openapi-typescript@7 vod-express.openapi.json --default-non-nullable false -o vod-express.d.ts
npm install openapi-fetch
```

`--default-non-nullable false` matters: without it, every config field that has a default
becomes required in the types.

```typescript
import createClient from 'openapi-fetch';
import type { paths } from './vod-express';

const api = createClient<paths>({
  baseUrl: process.env.SW_VOD_API ?? 'https://api-vod-express.syncwords.com/v1',
  headers: { Authorization: `Bearer ${token}` },
});

const { data, error } = await api.GET('/projects/{id}/files/{name}/meta', {
  params: { path: { id: projectId, name: 'season 2/episode 01.mp4' } },
});
if (error) throw new Error(`${error.code}: ${error.message}`);
```

**openapi-generator**

A client class per tag, with [openapi-generator](https://openapi-generator.tech) in Docker:

```bash
docker run --rm -v "$PWD:/local" openapitools/openapi-generator-cli:v7.16.0 generate \
  -i /local/vod-express.openapi.json -g typescript-fetch -o /local/vod-express-client \
  --additional-properties=supportsES6=true
```

```typescript
import { Configuration, FilesApi, ProjectsApi } from './vod-express-client';

const config = new Configuration({
  basePath: process.env.SW_VOD_API ?? 'https://api-vod-express.syncwords.com/v1',
  accessToken: token,
});
const page = await new ProjectsApi(config).listProjects({ perPage: 10 });
const files = await new FilesApi(config).listFiles({ id: page.data![0]!.id! });
```

## Python

```bash
docker run --rm -v "$PWD:/local" openapitools/openapi-generator-cli:v7.16.0 generate \
  -i /local/vod-express.openapi.json -g python -o /local/vod-express-python --package-name vod_express
pip install ./vod-express-python
```

```python
import os

import vod_express

config = vod_express.Configuration(
    host=os.environ.get("SW_VOD_API", "https://api-vod-express.syncwords.com/v1"),
    access_token=token,
)
with vod_express.ApiClient(config) as client:
    print(vod_express.AccountApi(client).get_whoami().data.vod_express)
    files = vod_express.FilesApi(client).list_files(project_id).data
    print([(f.name, f.status) for f in files])
```

Method names come from the operations: `createProject` / `create_project`, `listFiles` /
`list_files`, `startUpload` / `start_upload`, and so on; the [API reference](/vod-express/api-reference/introduction)
lists them all.

## What a generated client does not do for you

- **Tokens.** It sends the token you give it. Create one with your key pair and replace it when
  a call answers `401`; see [Authentication](/vod-express/authentication#token-lifetime).
- **Upload bytes.** The part `PUT`s go to S3's presigned URLs, not to the API: send them with any
  HTTP client, as in [Upload through the API](/vod-express/uploads/api-uploads#scripts).
- **Downloads.** A download call answers a presigned URL; fetch it without the `Authorization`
  header.

File names with slashes need nothing special: both generators encode a path parameter as one
segment (`season 2/episode 01.mp4` becomes `season%202%2Fepisode%2001.mp4`).

## Postman

- **[VOD Express API — Postman collection](/vod-express/downloads/vod-express.postman_collection.json)**: Every call of the API reference, grouped as in it, with example bodies, for this site's base URLs.

1. In Postman, **Import** the downloaded file.
2. In the collection's **Variables**, set `apiKey` and `apiSecret` to your key pair.
3. Send **Create token**: it stores the token in `token`, which every other request sends.
4. **Create a project**, **Create a webhook** and **Start an upload** store the ids they answer
   (`projectId`, `webhookId`, `uploadId`, `uploadKey`) for the requests that follow. Set `fileName`
   yourself, with a `/` written as `%2F`: Postman does not encode path variables.
5. `Idempotency-Key` is listed, switched off, on **Create a project**. Switched on, it sends a
   new UUID every time; put a fixed value in it to see a repeat answered without a second project.

`baseUrl` names the environment; see [Environments](/vod-express/environments).
