Skip to content

Commit

Permalink
Merge pull request #38 from mondaycom/feature/romka/new-request-types
Browse files Browse the repository at this point in the history
breaking change version - new request types
  • Loading branch information
RomKadria authored Oct 31, 2024
2 parents 4d44cd9 + 8341bce commit 151ea57
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 30 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 92 additions & 7 deletions packages/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ npm install @mondaydotcomorg/api

All exported types correspond to the current version of the API that existed when the NPM package was released

For the conviniecne of monday app developers, this CLI is included in the [@mondaydotcomorg/apps-cli](https://www.npmjs.com/package/@mondaycom/apps-cli).
For the convenience of monday app developers, this CLI is included in the [@mondaydotcomorg/apps-cli](https://www.npmjs.com/package/@mondaycom/apps-cli).
If you want to use it on it’s own, you can install [@mondaydotcomorg/setup-api](https://www.npmjs.com/package/@mondaydotcomorg/setup-api).
(you can find more about app development here [monday-apps-sdk](https://developer.monday.com/apps/docs/introduction-to-the-sdk))

Expand All @@ -34,7 +34,7 @@ The package exports the class `ApiClient` which is the main entry point to the S
```typescript
import { ApiClient } from '@mondaydotcomorg/api';

const client = new ApiClient('<API-TOKEN>');
const client = new ApiClient({token: '<API-TOKEN>'});

// Or use the operations provided by the SDK
const me = await client.operations.getMeOp();
Expand All @@ -56,10 +56,10 @@ const changeStatusColumn = await client.operations.changeColumnValueOp({
});

// Use the client to query monday's API freestyle WITHOUT TYPES -> Use @mondaydotcomorg/setup-api to setup typed project!
const boards = await client.query<{boards: [{ name: string }]}>(`query { boards(ids: some_id) { name } }`);
const boards = await client.request<{boards: [{ name: string }]}>(`query { boards(ids: some_id) { name } }`);

// You can also use the types provided by the sdk
const { boards } = await client.query<{
const { boards } = await client.request<{
boards: [Board];
}>(`query { boards(ids: some_id) { name } }`);
```
Expand All @@ -78,17 +78,102 @@ const user: User = {
}
```

### Configuration

#### ErrorPolicy

By default GraphQLClient will throw when an error is received. However, sometimes you still want to resolve the (partial) data you received. You can define errorPolicy in the GraphQLClient constructor.

```typescript
const client = new ApiClient({token: '<API-TOKEN>', requestConfig: { errorPolicy: 'all' }});
```

**None (default)**
Allow no errors at all. If you receive a GraphQL error the client will throw.

**Ignore**
Ignore incoming errors and resolve like no errors occurred

**All**
Return both the errors and data, only works with the client's rawRequest call option.

### Handling errors

The errors are returned from the `ClientError` type.
**The example below is leveraging types using [@mondaydotcomorg/setup-api](https://www.npmjs.com/package/@mondaydotcomorg/setup-api).**

```typescript
import { ApiClient, ClientError } from "@mondaydotcomorg/api";
import { GetBoardsQuery, GetBoardsQueryVariables } from "./generated/graphql";
import { exampleQuery } from "./queries.graphql";

async function getBoardDetails(): Promise<void> {
try {
const token = "<API_TOKEN>";
const client = new ApiClient({ token });
const queryVariables: GetBoardsQueryVariables = { ids: ["5901934630"] };
const queryData = await client.request<GetBoardsQuery>(
exampleQuery,
queryVariables
);

console.log(queryData.boards);
} catch (error) {
if (error instanceof ClientError) {
console.error(error.response.errors);
} else {
console.error(error);
}
}
}

getBoardDetails();
```

### Other request options

If you prefer the 'old' style of response (data, errors, extensions) you can call the api using the rawRequest option
**The example below is leveraging types using [@mondaydotcomorg/setup-api](https://www.npmjs.com/package/@mondaydotcomorg/setup-api).**

```typescript
import { ApiClient, ClientError } from "@mondaydotcomorg/api";
import { GetBoardsQuery, GetBoardsQueryVariables } from "./generated/graphql";
import { exampleQuery } from "./queries.graphql";

async function getBoardDetails(): Promise<void> {
try {
const token = "<API_TOKEN>";
const client = new ApiClient({ token });
const queryVariables: GetBoardsQueryVariables = { ids: ["5901934630"] };
const queryData = await client.rawRequest<GetBoardsQuery>(
exampleQuery,
queryVariables
);

console.log(queryData.data.boards);
} catch (error) {
if (error instanceof ClientError) {
console.error(error.response.errors);
} else {
console.error(error);
}
}
}

getBoardDetails();
```

## SeamlessApiClient

The SeamlessApiClient class is a tool designed for making seamless API requests to Monday.com, tailored for use within the client side of applications deployed on the platform.
Basically, when you are making an api call from the client side of an app deployed on Monday.com, you dont need to specify the users token.
Basically, when you are making an api call from the client side of an app deployed on Monday.com, you don't need to specify the users token.

```typescript
import {
Board,
} from "@mondaycom/api";

const { boards } = await SeamlessApiClient.query<{boards: [Board];}>(`query { boards(ids: some_id) { id name } }`);
const { boards } = await SeamlessApiClient.request<{boards: [Board];}>(`query { boards(ids: some_id) { id name } }`);

// or using your own types after integrating with @mondaycom/setup-api
import { GetBoardsQueryVariables, GetBoardsQuery } from "./generated/graphql";
Expand All @@ -103,7 +188,7 @@ export const getBoards = gql`
}
`;

const data = await SeamlessApiClient.query<GetBoardsQuery>(getBoards, variables);
const data = await SeamlessApiClient.request<GetBoardsQuery>(getBoards, variables);
```

### Type support
Expand Down
60 changes: 52 additions & 8 deletions packages/api/lib/api-client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { GraphQLClient } from 'graphql-request';
import { GraphQLClient, ClientError } from 'graphql-request';
import { ApiVersionType, DEFAULT_VERSION, QueryVariables } from './constants/index';
import { Sdk, getSdk } from './generated/sdk';
import pkg from '../package.json';
import { getApiEndpoint } from './shared/get-api-endpoint';
import { GraphQLClientResponse, RequestConfig } from 'graphql-request/build/esm/types';

export { ClientError };

export interface ApiClientConfig {
token: string;
apiVersion?: string;
requestConfig?: Omit<RequestConfig, 'headers'>;
}

/**
* The `ApiClient` class provides a structured way to interact with the Monday.com API,
Expand All @@ -18,17 +27,22 @@ export class ApiClient {

/**
* Constructs a new `ApiClient` instance, initializing the GraphQL client with
* the specified authentication token and API version.
* the specified configuration object.
*
* @param {string} token - The authentication token required for making API requests to Monday.com.
* @param {string} [apiVersion=DEFAULT_VERSION] - The API version to use for requests.
* Defaults to the version corresponding to the package version release (which will be the current), but can be specified with custom string.
* @param {ApiClientConfig} config - Configuration for the API client.
* Requires `token`, and optionally includes `apiVersion` and `requestConfig`.
*/

constructor(token: string, apiVersion: string = DEFAULT_VERSION) {
constructor(config: ApiClientConfig) {
const { token, apiVersion = DEFAULT_VERSION, requestConfig = {} } = config;
if (!this.isValidApiVersion(apiVersion)) {
throw new Error(
"Invalid API version format. Expected format is 'yyyy-mm' with month as one of '01', '04', '07', or '10'.",
);
}
this.apiVersion = apiVersion;
const endpoint = getApiEndpoint();
this.client = new GraphQLClient(endpoint, {
...requestConfig,
headers: {
'Content-Type': 'application/json',
Authorization: token,
Expand All @@ -53,8 +67,38 @@ export class ApiClient {
* @returns {Promise<T>} A promise that resolves with the result of the query or mutation.
* @template T The expected type of the query or mutation result.
*/
public query = async <T>(query: string, variables?: QueryVariables): Promise<T> => {
public request = async <T>(query: string, variables?: QueryVariables): Promise<T> => {
const res = await this.client.request<T>(query, variables);
return res;
};

/**
* Performs a raw GraphQL query or mutation to the Monday.com API using the configured
* GraphQL client. This method is asynchronous and returns a promise that resolves
* with the query result.
*
* The result will be in the raw format: data, errors, extensions.
*
* @param {string} query - The GraphQL query or mutation string.
* @param {QueryVariables} [variables] - An optional object containing variables for the query.
* `QueryVariables` is a type alias for `Record<string, any>`, allowing specification
* of key-value pairs where the value can be any type. This parameter is used to provide
* dynamic values in the query or mutation.
* @returns {Promise<T>} A promise that resolves with the result of the query or mutation.
* @template T The expected type of the query or mutation result.
*/
public rawRequest = async <T>(query: string, variables?: QueryVariables): Promise<GraphQLClientResponse<T>> => {
const res = await this.client.rawRequest<T>(query, variables);
return res;
};

/**
* Validates the API version format (yyyy-mm), restricting mm to 01, 04, 07, or 10.
*
* @param {string} version - The API version string to validate.
* @returns {boolean} - Returns true if the version matches yyyy-mm format with allowed months.
*/
private isValidApiVersion(version: string): boolean {
return /^\d{4}-(01|04|07|10)$/.test(version);
}
}
2 changes: 1 addition & 1 deletion packages/api/lib/seamless-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class SeamlessApiClient {
* @template T The expected type of the query or mutation result.
* @throws {Error} Throws an error if called from within the monday.com platform and the request failed, or if the request timed out.
*/
public query<T>(
public request<T>(
query: string,
variables?: QueryVariables,
version?: ApiVersionType,
Expand Down
3 changes: 2 additions & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mondaydotcomorg/api",
"version": "5.0.1",
"version": "6.0.0",
"description": "monday.com API client",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down Expand Up @@ -33,6 +33,7 @@
"url": "https://github.com/mondaycom/monday-graphql-api/tree/main/packages/api"
},
"dependencies": {
"graphql": "16.8.2",
"graphql-request": "^6.1.0",
"graphql-tag": "^2.12.6"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/api/tests/api-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jest.mock('graphql-request', () => {
describe('ApiClient', () => {
it('should correctly initialize with default parameters', () => {
const token = 'test-token';
const apiClient = new ApiClient(token);
const apiClient = new ApiClient({ token });

expect(GraphQLClient).toHaveBeenCalledWith('https://api.monday.com/v2', {
headers: {
Expand Down
8 changes: 4 additions & 4 deletions packages/setup-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,17 @@ When using this package, you will automatically get an example query and an exam
Here is how to use them!

```typescript
const client = new ApiClient("your_api_token"); // From the monday api sdk @mondaydotcomorg/api
const client = new ApiClient({token: '<API-TOKEN>'}); // From the monday api sdk @mondaydotcomorg/api

const queryVariables: QueryBoardsArgs = { ids: ["your_board_id"] }; // replace with your board id
const queryData = await client.query<GetBoardsQuery>(exampleQuery, queryVariables);
const queryData = await client.request<GetBoardsQuery>(exampleQuery, queryVariables);

const mutationVariables: CreateItemMutationVariables = {
boardId: "your_board_id", // replace with your board id
groupId: "your_groyup_id", // replace with your group id
groupId: "your_group_id", // replace with your group id
itemName: "Im using my own queries!",
};
const mutationData = await client.query<CreateItemMutation>(exampleMutation, mutationVariables);
const mutationData = await client.request<CreateItemMutation>(exampleMutation, mutationVariables);
```

## Further configuration
Expand Down
2 changes: 1 addition & 1 deletion packages/setup-api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mondaydotcomorg/setup-api",
"version": "1.2.1",
"version": "1.2.2",
"description": "monday.com setup api cli",
"main": "dist/cjs/index.js",
"types": "dist/cjs/index.d.ts",
Expand Down

0 comments on commit 151ea57

Please sign in to comment.