Add dev:generate-client command to sdk (#21489)
## after `yarn twenty dev:generate-client` <img width="1149" height="246" alt="image" src="https://github.com/user-attachments/assets/1edcba03-2647-4bc8-8188-7ad69362ac52" /> <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21489?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -43,6 +43,33 @@ yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
This is different from `yarn twenty docker:logs`, which shows the Docker container logs. `yarn twenty dev:function:logs` shows your app's function execution logs from the Twenty server.
|
||||
</Note>
|
||||
|
||||
## Generating the typed client (`yarn twenty dev:generate-client`)
|
||||
|
||||
Regenerate the typed API client (`twenty-client-sdk`) from the active remote's schema, without building or syncing an app. Use it to get a typed client in any project — like a backend service living in a separate repository — that talks to your Twenty instance:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# In your project (no Twenty app definition required)
|
||||
yarn add twenty-sdk twenty-client-sdk
|
||||
|
||||
# Connect to the Twenty instance to generate the client from
|
||||
yarn twenty remote:add
|
||||
|
||||
# Generate the typed client into node_modules/twenty-client-sdk
|
||||
yarn twenty dev:generate-client
|
||||
```
|
||||
|
||||
Then import the client in your code:
|
||||
|
||||
```typescript
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
```
|
||||
|
||||
Re-run the command whenever your data model changes to refresh the generated types.
|
||||
|
||||
<Note>
|
||||
The client is generated inside `node_modules`, so it is not committed with your code. Run `yarn twenty dev:generate-client` after every install (for example in a `postinstall` script or in CI).
|
||||
</Note>
|
||||
|
||||
## Uninstalling an app (`yarn twenty app:uninstall`)
|
||||
|
||||
Remove your app from the active workspace:
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { ApiService } from '@/cli/utilities/api/api-service';
|
||||
import { ClientService } from '@/cli/utilities/client/client-service';
|
||||
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/utilities/config/current-execution-directory';
|
||||
import { serializeError } from '@/cli/utilities/error/serialize-error';
|
||||
import { pathExists } from '@/cli/utilities/file/fs-utils';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export type AppGenerateClientCommandOptions = {
|
||||
appPath?: string;
|
||||
};
|
||||
|
||||
export class AppGenerateClientCommand {
|
||||
async execute(options: AppGenerateClientCommandOptions): Promise<void> {
|
||||
const appPath = options.appPath ?? CURRENT_EXECUTION_DIRECTORY;
|
||||
const clientSdkPath = join(appPath, 'node_modules', 'twenty-client-sdk');
|
||||
|
||||
if (!(await pathExists(clientSdkPath))) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
`Cannot find twenty-client-sdk in ${appPath}.\n\n` +
|
||||
' Install it first:\n' +
|
||||
' yarn add twenty-client-sdk',
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const apiService = new ApiService({ disableInterceptors: true });
|
||||
const validateAuth = await apiService.validateAuth();
|
||||
|
||||
if (!validateAuth.serverUp) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
'Cannot reach Twenty server.\n\n' +
|
||||
' Check your remotes:\n' +
|
||||
' yarn twenty remote:status',
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!validateAuth.authValid) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
'Authentication failed. Run `yarn twenty remote:add` to authenticate.',
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(chalk.blue('Generating API client...'));
|
||||
|
||||
try {
|
||||
const clientService = new ClientService({ skipAuth: false });
|
||||
|
||||
await clientService.generateCoreClient({ appPath });
|
||||
} catch (error) {
|
||||
console.error(
|
||||
chalk.red(`Failed to generate API client: ${serializeError(error)}`),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(chalk.green('✓ API client generated'));
|
||||
console.log(
|
||||
chalk.gray(`Output: ${join(clientSdkPath, 'dist', 'core', 'generated')}`),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { EntityAddCommand } from './add';
|
||||
import { AppBuildCommand } from './build';
|
||||
import { AppDevCommand } from './dev';
|
||||
import { AppDevOnceCommand } from './dev-once';
|
||||
import { AppGenerateClientCommand } from './generate-client';
|
||||
import { AppTypecheckCommand } from './typecheck';
|
||||
import { registerDevFunctionCommands } from './function';
|
||||
|
||||
@@ -15,6 +16,7 @@ export const registerDevCommands = (program: Command): void => {
|
||||
const devOnceCommand = new AppDevOnceCommand();
|
||||
const typecheckCommand = new AppTypecheckCommand();
|
||||
const addCommand = new EntityAddCommand();
|
||||
const generateClientCommand = new AppGenerateClientCommand();
|
||||
|
||||
const devAction = async (
|
||||
appPath: string | undefined,
|
||||
@@ -110,5 +112,16 @@ export const registerDevCommands = (program: Command): void => {
|
||||
await cmd.execute({ remote: options.remote });
|
||||
});
|
||||
|
||||
program
|
||||
.command('dev:generate-client [appPath]')
|
||||
.description(
|
||||
'Generate the typed API client from the active remote (no app definition required)',
|
||||
)
|
||||
.action(async (appPath) => {
|
||||
await generateClientCommand.execute({
|
||||
appPath: formatPath(appPath),
|
||||
});
|
||||
});
|
||||
|
||||
registerDevFunctionCommands(program);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ export class ClientService {
|
||||
this.apiService = new ApiService({
|
||||
disableInterceptors: true,
|
||||
serverUrl: options?.serverUrl,
|
||||
skipAuth: true,
|
||||
skipAuth: options?.skipAuth ?? true,
|
||||
token: options?.token,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user