Skip to content

Commit df4b13e

Browse files
[2026-03-18] Release: Update Connect API Starter Kit (#31)
Release: Update Connect API Starter Kit Generated on: Wed Mar 18 23:11:29 UTC 2026 Source commit: fc3cd0f0ab34b7a500ea117f78f8704b664daeee Co-authored-by: canva-sdk-releases[bot] <227329455+canva-sdk-releases[bot]@users.noreply.github.com>
1 parent 56653dc commit df4b13e

45 files changed

Lines changed: 8835 additions & 4780 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## 2026-03-19
4+
5+
### 🔧 Changed
6+
7+
- Upgraded `@hey-api/openapi-ts` to `0.85.2` and regenerated the Connect SDK. The demos now import from `@canva/connect-api-ts/client` instead of `@hey-api/client-fetch`.
8+
- Dependencies audit bringing modules up to date:
9+
10+
```text
11+
@types/jest 29.5.14 -> 30.0.0
12+
axios 1.12.2 -> 1.13.6
13+
jest 29.7.0 -> 30.2.0
14+
jest-environment-jsdom 29.7.0 -> 30.2.0
15+
multer 2.0.2 -> 2.1.1
16+
terser-webpack-plugin 5.3.14 -> 5.4.0
17+
```
18+
319
## 2026-02-27
420

521
### 🔧 Changed

client/openapi-ts.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ import { defineConfig } from "@hey-api/openapi-ts";
22

33
export default defineConfig({
44
input: "../openapi/spec.yml",
5-
client: "@hey-api/client-fetch",
65
experimentalParser: true,
76
output: {
87
path: "./ts",
98
format: "prettier",
109
lint: "eslint",
1110
},
1211
plugins: [
12+
{
13+
name: "@hey-api/client-fetch",
14+
},
1315
{
1416
name: "@hey-api/sdk",
1517
asClass: true,
18+
classNameBuilder: (name) => `${name}Service`,
1619
},
1720
{
1821
name: "@hey-api/typescript",

client/package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,13 @@
88
"scripts": {
99
"generate": "rm -rf ./ts && openapi-ts"
1010
},
11-
"dependencies": {
12-
"@hey-api/client-fetch": "0.5.0"
13-
},
11+
"dependencies": {},
1412
"engines": {
1513
"node": ">=20.14.0"
1614
},
1715
"engineStrict": true,
1816
"devDependencies": {
19-
"@hey-api/openapi-ts": "0.57.1",
17+
"@hey-api/openapi-ts": "0.85.2",
2018
"typescript": "5.9.2"
2119
}
2220
}

client/ts/client.gen.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// This file is auto-generated by @hey-api/openapi-ts
2+
3+
import {
4+
type ClientOptions,
5+
type Config,
6+
createClient,
7+
createConfig,
8+
} from "./client";
9+
import type { ClientOptions as ClientOptions2 } from "./types.gen";
10+
11+
/**
12+
* The `createClientConfig()` function will be called on client initialization
13+
* and the returned object will become the client's initial configuration.
14+
*
15+
* You may want to initialize your client this way instead of calling
16+
* `setConfig()`. This is useful for example if you're using Next.js
17+
* to ensure your client always has the correct values.
18+
*/
19+
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (
20+
override?: Config<ClientOptions & T>,
21+
) => Config<Required<ClientOptions> & T>;
22+
23+
export const client = createClient(
24+
createConfig<ClientOptions2>({
25+
baseUrl: "https://api.canva.com/rest",
26+
}),
27+
);

client/ts/client/client.gen.ts

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
// This file is auto-generated by @hey-api/openapi-ts
2+
3+
import { createSseClient } from "../core/serverSentEvents.gen";
4+
import type { HttpMethod } from "../core/types.gen";
5+
import { getValidRequestBody } from "../core/utils.gen";
6+
import type {
7+
Client,
8+
Config,
9+
RequestOptions,
10+
ResolvedRequestOptions,
11+
} from "./types.gen";
12+
import {
13+
buildUrl,
14+
createConfig,
15+
createInterceptors,
16+
getParseAs,
17+
mergeConfigs,
18+
mergeHeaders,
19+
setAuthParams,
20+
} from "./utils.gen";
21+
22+
type ReqInit = Omit<RequestInit, "body" | "headers"> & {
23+
body?: any;
24+
headers: ReturnType<typeof mergeHeaders>;
25+
};
26+
27+
export const createClient = (config: Config = {}): Client => {
28+
let _config = mergeConfigs(createConfig(), config);
29+
30+
const getConfig = (): Config => ({ ..._config });
31+
32+
const setConfig = (config: Config): Config => {
33+
_config = mergeConfigs(_config, config);
34+
return getConfig();
35+
};
36+
37+
const interceptors = createInterceptors<
38+
Request,
39+
Response,
40+
unknown,
41+
ResolvedRequestOptions
42+
>();
43+
44+
const beforeRequest = async (options: RequestOptions) => {
45+
const opts = {
46+
..._config,
47+
...options,
48+
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
49+
headers: mergeHeaders(_config.headers, options.headers),
50+
serializedBody: undefined,
51+
};
52+
53+
if (opts.security) {
54+
await setAuthParams({
55+
...opts,
56+
security: opts.security,
57+
});
58+
}
59+
60+
if (opts.requestValidator) {
61+
await opts.requestValidator(opts);
62+
}
63+
64+
if (opts.body !== undefined && opts.bodySerializer) {
65+
opts.serializedBody = opts.bodySerializer(opts.body);
66+
}
67+
68+
// remove Content-Type header if body is empty to avoid sending invalid requests
69+
if (opts.body === undefined || opts.serializedBody === "") {
70+
opts.headers.delete("Content-Type");
71+
}
72+
73+
const url = buildUrl(opts);
74+
75+
return { opts, url };
76+
};
77+
78+
const request: Client["request"] = async (options) => {
79+
// @ts-expect-error
80+
const { opts, url } = await beforeRequest(options);
81+
const requestInit: ReqInit = {
82+
redirect: "follow",
83+
...opts,
84+
body: getValidRequestBody(opts),
85+
};
86+
87+
let request = new Request(url, requestInit);
88+
89+
for (const fn of interceptors.request.fns) {
90+
if (fn) {
91+
request = await fn(request, opts);
92+
}
93+
}
94+
95+
// fetch must be assigned here, otherwise it would throw the error:
96+
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
97+
const _fetch = opts.fetch!;
98+
let response = await _fetch(request);
99+
100+
for (const fn of interceptors.response.fns) {
101+
if (fn) {
102+
response = await fn(response, request, opts);
103+
}
104+
}
105+
106+
const result = {
107+
request,
108+
response,
109+
};
110+
111+
if (response.ok) {
112+
const parseAs =
113+
(opts.parseAs === "auto"
114+
? getParseAs(response.headers.get("Content-Type"))
115+
: opts.parseAs) ?? "json";
116+
117+
if (
118+
response.status === 204 ||
119+
response.headers.get("Content-Length") === "0"
120+
) {
121+
let emptyData: any;
122+
switch (parseAs) {
123+
case "arrayBuffer":
124+
case "blob":
125+
case "text":
126+
emptyData = await response[parseAs]();
127+
break;
128+
case "formData":
129+
emptyData = new FormData();
130+
break;
131+
case "stream":
132+
emptyData = response.body;
133+
break;
134+
case "json":
135+
default:
136+
emptyData = {};
137+
break;
138+
}
139+
return opts.responseStyle === "data"
140+
? emptyData
141+
: {
142+
data: emptyData,
143+
...result,
144+
};
145+
}
146+
147+
let data: any;
148+
switch (parseAs) {
149+
case "arrayBuffer":
150+
case "blob":
151+
case "formData":
152+
case "json":
153+
case "text":
154+
data = await response[parseAs]();
155+
break;
156+
case "stream":
157+
return opts.responseStyle === "data"
158+
? response.body
159+
: {
160+
data: response.body,
161+
...result,
162+
};
163+
}
164+
165+
if (parseAs === "json") {
166+
if (opts.responseValidator) {
167+
await opts.responseValidator(data);
168+
}
169+
170+
if (opts.responseTransformer) {
171+
data = await opts.responseTransformer(data);
172+
}
173+
}
174+
175+
return opts.responseStyle === "data"
176+
? data
177+
: {
178+
data,
179+
...result,
180+
};
181+
}
182+
183+
const textError = await response.text();
184+
let jsonError: unknown;
185+
186+
try {
187+
jsonError = JSON.parse(textError);
188+
} catch {
189+
// noop
190+
}
191+
192+
const error = jsonError ?? textError;
193+
let finalError = error;
194+
195+
for (const fn of interceptors.error.fns) {
196+
if (fn) {
197+
finalError = (await fn(error, response, request, opts)) as string;
198+
}
199+
}
200+
201+
finalError = finalError || ({} as string);
202+
203+
if (opts.throwOnError) {
204+
throw finalError;
205+
}
206+
207+
// TODO: we probably want to return error and improve types
208+
return opts.responseStyle === "data"
209+
? undefined
210+
: {
211+
error: finalError,
212+
...result,
213+
};
214+
};
215+
216+
const makeMethodFn =
217+
(method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
218+
request({ ...options, method });
219+
220+
const makeSseFn =
221+
(method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
222+
const { opts, url } = await beforeRequest(options);
223+
return createSseClient({
224+
...opts,
225+
body: opts.body as BodyInit | null | undefined,
226+
headers: opts.headers as unknown as Record<string, string>,
227+
method,
228+
onRequest: async (url, init) => {
229+
let request = new Request(url, init);
230+
for (const fn of interceptors.request.fns) {
231+
if (fn) {
232+
request = await fn(request, opts);
233+
}
234+
}
235+
return request;
236+
},
237+
url,
238+
});
239+
};
240+
241+
return {
242+
buildUrl,
243+
connect: makeMethodFn("CONNECT"),
244+
delete: makeMethodFn("DELETE"),
245+
get: makeMethodFn("GET"),
246+
getConfig,
247+
head: makeMethodFn("HEAD"),
248+
interceptors,
249+
options: makeMethodFn("OPTIONS"),
250+
patch: makeMethodFn("PATCH"),
251+
post: makeMethodFn("POST"),
252+
put: makeMethodFn("PUT"),
253+
request,
254+
setConfig,
255+
sse: {
256+
connect: makeSseFn("CONNECT"),
257+
delete: makeSseFn("DELETE"),
258+
get: makeSseFn("GET"),
259+
head: makeSseFn("HEAD"),
260+
options: makeSseFn("OPTIONS"),
261+
patch: makeSseFn("PATCH"),
262+
post: makeSseFn("POST"),
263+
put: makeSseFn("PUT"),
264+
trace: makeSseFn("TRACE"),
265+
},
266+
trace: makeMethodFn("TRACE"),
267+
} as Client;
268+
};

client/ts/client/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This file is auto-generated by @hey-api/openapi-ts
2+
3+
export type { Auth } from "../core/auth.gen";
4+
export type { QuerySerializerOptions } from "../core/bodySerializer.gen";
5+
export {
6+
formDataBodySerializer,
7+
jsonBodySerializer,
8+
urlSearchParamsBodySerializer,
9+
} from "../core/bodySerializer.gen";
10+
export { buildClientParams } from "../core/params.gen";
11+
export { serializeQueryKeyValue } from "../core/queryKeySerializer.gen";
12+
export { createClient } from "./client.gen";
13+
export type {
14+
Client,
15+
ClientOptions,
16+
Config,
17+
CreateClientConfig,
18+
Options,
19+
OptionsLegacyParser,
20+
RequestOptions,
21+
RequestResult,
22+
ResolvedRequestOptions,
23+
ResponseStyle,
24+
TDataShape,
25+
} from "./types.gen";
26+
export { createConfig, mergeHeaders } from "./utils.gen";

0 commit comments

Comments
 (0)