Skip to content

chore(backend): Add machines Backend API resource and methods #6335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eleven-cats-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/backend": minor
---

Add machines Backend API resource and methods
65 changes: 65 additions & 0 deletions packages/backend/src/api/endpoints/MachineApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { joinPaths } from '../../util/path';
import type { PaginatedResourceResponse } from '../resources/Deserializer';
import type { Machine } from '../resources/Machine';
import { AbstractAPI } from './AbstractApi';

const basePath = '/machines';

type CreateMachineParams = {
name: string;
};

type UpdateMachineParams = {
machineId: string;
name: string;
};

type GetMachineListParams = {
limit?: number;
offset?: number;
query?: string;
};

export class MachineApi extends AbstractAPI {
async get(machineId: string) {
this.requireId(machineId);
return this.request<Machine>({
method: 'GET',
path: joinPaths(basePath, machineId),
});
}

async list(queryParams: GetMachineListParams = {}) {
return this.request<PaginatedResourceResponse<Machine[]>>({
method: 'GET',
path: basePath,
queryParams,
});
}

async create(bodyParams: CreateMachineParams) {
return this.request<Machine>({
method: 'POST',
path: basePath,
bodyParams,
});
}

async update(params: UpdateMachineParams) {
const { machineId, ...bodyParams } = params;
this.requireId(machineId);
return this.request<Machine>({
method: 'PATCH',
path: joinPaths(basePath, machineId),
bodyParams,
});
}

async delete(machineId: string) {
this.requireId(machineId);
return this.request<Machine>({
method: 'DELETE',
path: joinPaths(basePath, machineId),
});
}
}
Comment on lines +1 to +65
Copy link
Contributor

@coderabbitai coderabbitai bot Jul 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add test coverage for the new Machine API.

No tests were added for the new MachineApi class. Please add comprehensive test coverage for all CRUD operations to ensure the API works correctly and to prevent regressions.

Consider adding tests for:

  • All CRUD operations (get, list, create, update, delete)
  • Error handling scenarios
  • Parameter validation
  • Response deserialization

Would you like me to help generate test cases for the MachineApi class?

🤖 Prompt for AI Agents
In packages/backend/src/api/endpoints/MachineApi.ts lines 1 to 65, the
MachineApi class lacks test coverage. Add comprehensive tests covering all CRUD
methods: get, list, create, update, and delete. Include tests for error
handling, parameter validation (e.g., requireId checks), and verify correct
response deserialization. Use mocks for the request method to simulate API
responses and errors, ensuring all code paths are tested.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is part of an upcoming project (machine-to-machine tokens). The tests will be added later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wobsoriano Thanks for the clarification! That makes sense - I understand this is part of the upcoming machine-to-machine tokens project. I'll note that test coverage can be addressed in a later phase of the project rollout.


🧠 Learnings used
Learnt from: wobsoriano
PR: clerk/javascript#6229
File: packages/backend/src/api/endpoints/MachineTokensApi.ts:47-89
Timestamp: 2025-07-01T15:20:41.834Z
Learning: In the Clerk JavaScript repository, for the MachineTokensApi class (packages/backend/src/api/endpoints/MachineTokensApi.ts), the maintainers prefer to rely on TypeScript types and readable property names for API documentation rather than JSDoc comments.

Learnt from: CR
PR: clerk/javascript#0
File: .cursor/rules/nextjs.mdc:0-0
Timestamp: 2025-06-30T10:31:43.578Z
Learning: Applies to app/api/**/*.ts : Document API endpoints with TypeScript interfaces

Learnt from: CR
PR: clerk/javascript#0
File: .cursor/rules/nextjs.mdc:0-0
Timestamp: 2025-06-30T10:31:43.578Z
Learning: Applies to app/api/**/*.ts : Use TypeScript interfaces for API response types

Learnt from: wobsoriano
PR: clerk/javascript#6229
File: packages/backend/src/api/endpoints/MachineTokensApi.ts:32-45
Timestamp: 2025-07-01T15:20:59.456Z
Learning: In the MachineTokensApi class (packages/backend/src/api/endpoints/MachineTokensApi.ts), the #withMachineTokenSecretHeader method intentionally overwrites the entire headerParams object with only the Authorization header when machineTokenSecret is provided, rather than merging with existing headers. This is by design to ensure only the Authorization header is present in requests.

Learnt from: CR
PR: clerk/javascript#0
File: .cursor/rules/nextjs.mdc:0-0
Timestamp: 2025-06-30T10:31:43.578Z
Learning: Applies to app/**/route.ts : Use route.ts for API endpoints (GET, POST, etc.) in the App Router

Learnt from: CR
PR: clerk/javascript#0
File: .cursor/rules/development.mdc:0-0
Timestamp: 2025-06-30T10:29:42.997Z
Learning: Include tests for all new features

Learnt from: CR
PR: clerk/javascript#0
File: .cursor/rules/typescript.mdc:0-0
Timestamp: 2025-06-30T10:33:45.961Z
Learning: Applies to **/__tests__/**/*.{ts,tsx} : Implement proper mock types that match interfaces in tests

Learnt from: CR
PR: clerk/javascript#0
File: .cursor/rules/development.mdc:0-0
Timestamp: 2025-06-30T10:29:42.997Z
Learning: Unit tests are required for all new functionality

Learnt from: CR
PR: clerk/javascript#0
File: .cursor/rules/development.mdc:0-0
Timestamp: 2025-06-30T10:29:42.997Z
Learning: Update documentation for API changes

1 change: 1 addition & 0 deletions packages/backend/src/api/endpoints/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from './EmailAddressApi';
export * from './IdPOAuthAccessTokenApi';
export * from './InstanceApi';
export * from './InvitationApi';
export * from './MachineApi';
export * from './MachineTokensApi';
export * from './JwksApi';
export * from './JwtTemplatesApi';
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/api/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
InvitationAPI,
JwksAPI,
JwtTemplatesApi,
MachineApi,
MachineTokensApi,
OAuthApplicationsApi,
OrganizationAPI,
Expand Down Expand Up @@ -64,6 +65,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) {
invitations: new InvitationAPI(request),
jwks: new JwksAPI(request),
jwtTemplates: new JwtTemplatesApi(request),
machines: new MachineApi(request),
machineTokens: new MachineTokensApi(
buildRequest({
...options,
Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/api/resources/Deserializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
InstanceSettings,
Invitation,
JwtTemplate,
Machine,
MachineToken,
OauthAccessToken,
OAuthApplication,
Expand Down Expand Up @@ -132,6 +133,8 @@ function jsonToObject(item: any): any {
return Invitation.fromJSON(item);
case ObjectType.JwtTemplate:
return JwtTemplate.fromJSON(item);
case ObjectType.Machine:
return Machine.fromJSON(item);
case ObjectType.MachineToken:
return MachineToken.fromJSON(item);
case ObjectType.OauthAccessToken:
Expand Down
10 changes: 10 additions & 0 deletions packages/backend/src/api/resources/JSON.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const ObjectType = {
InstanceRestrictions: 'instance_restrictions',
InstanceSettings: 'instance_settings',
Invitation: 'invitation',
Machine: 'machine',
MachineToken: 'machine_to_machine_token',
JwtTemplate: 'jwt_template',
OauthAccessToken: 'oauth_access_token',
Expand Down Expand Up @@ -698,6 +699,15 @@ export interface SamlAccountConnectionJSON extends ClerkResourceJSON {
updated_at: number;
}

export interface MachineJSON extends ClerkResourceJSON {
object: typeof ObjectType.Machine;
id: string;
name: string;
instance_id: string;
created_at: number;
updated_at: number;
}

export interface MachineTokenJSON extends ClerkResourceJSON {
object: typeof ObjectType.MachineToken;
name: string;
Expand Down
15 changes: 15 additions & 0 deletions packages/backend/src/api/resources/Machine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { MachineJSON } from './JSON';

export class Machine {
constructor(
readonly id: string,
readonly name: string,
readonly instanceId: string,
readonly createdAt: number,
readonly updatedAt: number,
) {}

static fromJSON(data: MachineJSON): Machine {
return new Machine(data.id, data.name, data.instance_id, data.created_at, data.updated_at);
}
}
1 change: 1 addition & 0 deletions packages/backend/src/api/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export * from './InstanceRestrictions';
export * from './InstanceSettings';
export * from './Invitation';
export * from './JSON';
export * from './Machine';
export * from './MachineToken';
export * from './JwtTemplate';
export * from './OauthAccessToken';
Expand Down
Loading