Dynamic grql api wrapper on application sync (#15791)
# Introduction Important note: for the moment testing this locally will require some hack due to latest twenty-sdk not being published. You will need to build twenty-cli and `cd packages/twenty-cli && yarn link` To finally sync the app in your app folder as `cd app-folder && twenty app sync` close https://github.com/twentyhq/core-team-issues/issues/1863 In this PR is introduced the generate sdk programmatic call to [genql](https://genql.dev/) exposed in a `client` barrel of `twenty-sdk` located in this package as there's high chances that will add a codegen layer above it at some point ? The cli calls this method after a sync application and writes a client in a generated folder. It will make a graql introspection query on the whole workspace. We should later improve that and only filter by current applicationId and its dependencies ( when twenty-standard application is introduced ) Fully typesafe ( input, output, filters etc ) auto-completed client ## Hello-world app serverless refactor <img width="2480" height="1326" alt="image" src="https://github.com/user-attachments/assets/b18ea372-b21d-4560-8fbc-1dc348427a95" /> --------- Co-authored-by: martmull <martmull@hotmail.fr>
This commit is contained in:
@@ -6,6 +6,11 @@ import {
|
||||
type PackageJson,
|
||||
} from '../types/config.types';
|
||||
import { ConfigService } from './config.service';
|
||||
import {
|
||||
buildClientSchema,
|
||||
getIntrospectionQuery,
|
||||
printSchema,
|
||||
} from 'graphql/index';
|
||||
|
||||
export class ApiService {
|
||||
private client: AxiosInstance;
|
||||
@@ -188,4 +193,48 @@ export class ApiService {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getSchema(): Promise<ApiResponse<string>> {
|
||||
try {
|
||||
const introspectionQuery = getIntrospectionQuery();
|
||||
|
||||
const response = await this.client.post(
|
||||
'/graphql',
|
||||
{
|
||||
query: introspectionQuery,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: '*/*',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.data.errors) {
|
||||
return {
|
||||
success: false,
|
||||
error: `GraphQL introspection errors: ${JSON.stringify(response.data.errors)}`,
|
||||
};
|
||||
}
|
||||
|
||||
const schema = buildClientSchema(response.data.data);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: printSchema(schema),
|
||||
message: 'Successfully load schema',
|
||||
};
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response) {
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
error.response.data.errors[0]?.message ||
|
||||
'Failed to load graphql Schema',
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import chalk from 'chalk';
|
||||
import { generate } from '@genql/cli';
|
||||
import { join, resolve } from 'path';
|
||||
import { ConfigService } from './config.service';
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
export const GENERATED_FOLDER_NAME = 'generated';
|
||||
|
||||
export class GenerateService {
|
||||
private configService: ConfigService;
|
||||
private apiService: ApiService;
|
||||
|
||||
constructor() {
|
||||
this.configService = new ConfigService();
|
||||
this.apiService = new ApiService();
|
||||
}
|
||||
|
||||
async generateClient(appPath: string): Promise<void> {
|
||||
const outputPath = join(appPath, GENERATED_FOLDER_NAME);
|
||||
|
||||
console.log(chalk.blue('📦 Generating Twenty client...'));
|
||||
console.log(chalk.gray(`📁 Output Path: ${outputPath}`));
|
||||
console.log('');
|
||||
const config = await this.configService.getConfig();
|
||||
|
||||
const url = config.apiUrl;
|
||||
const token = config.apiKey;
|
||||
|
||||
if (!url || !token) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'⚠️ Skipping Client generation: API URL or token not configured',
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(chalk.gray(`API URL: ${url}`));
|
||||
console.log(chalk.gray(`Output: ${outputPath}`));
|
||||
|
||||
const { data: schema } = await this.apiService.getSchema();
|
||||
|
||||
await generate({
|
||||
schema,
|
||||
output: resolve(outputPath),
|
||||
scalarTypes: {
|
||||
DateTime: 'string',
|
||||
JSON: 'Record<string, unknown>',
|
||||
UUID: 'string',
|
||||
},
|
||||
verbose: true,
|
||||
});
|
||||
|
||||
console.log(chalk.green('✓ Client generated successfully!'));
|
||||
console.log(chalk.gray(`Generated files at: ${outputPath}`));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user