> ## Documentation Index
> Fetch the complete documentation index at: https://veryfront.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Project search

> Search the caller's projects by name or slug.

export const GraphQLOperationPlayground = ({operationName, query, variables, operationType = 'query'}) => {
  const endpoint = 'https://api.veryfront.com/graphql';
  const tokenStorageKey = 'veryfront.docs.mcpBearerToken';
  const readStoredToken = () => {
    if (typeof window === 'undefined') return '';
    try {
      return window.sessionStorage.getItem(tokenStorageKey) || '';
    } catch {
      return '';
    }
  };
  const [token, setToken] = useState(readStoredToken);
  const [queryText, setQueryText] = useState(query);
  const [variablesText, setVariablesText] = useState(variables);
  const [running, setRunning] = useState(false);
  const [status, setStatus] = useState('');
  const [response, setResponse] = useState(null);
  const [copied, setCopied] = useState(false);
  const updateToken = value => {
    setToken(value);
    try {
      if (value) window.sessionStorage.setItem(tokenStorageKey, value); else window.sessionStorage.removeItem(tokenStorageKey);
    } catch {}
  };
  const runOperation = async () => {
    let parsedVariables;
    try {
      parsedVariables = variablesText.trim() ? JSON.parse(variablesText) : {};
    } catch {
      setStatus('Variables must be valid JSON.');
      return;
    }
    if (parsedVariables === null || Array.isArray(parsedVariables) || typeof parsedVariables !== 'object') {
      setStatus('Variables must be a JSON object.');
      return;
    }
    setRunning(true);
    setStatus('Sending request…');
    setCopied(false);
    setResponse(null);
    try {
      const result = await fetch(endpoint, {
        method: 'POST',
        headers: {
          accept: 'application/json',
          'content-type': 'application/json',
          ...token.trim() ? {
            authorization: `Bearer ${token.trim()}`
          } : {},
          'x-veryfront-origin': 'docs-graphql-operation-page'
        },
        body: JSON.stringify({
          operationName,
          query: queryText,
          variables: parsedVariables
        })
      });
      const text = await result.text();
      let body;
      try {
        body = JSON.parse(text);
      } catch {}
      setResponse(body === undefined ? text || 'No response body.' : JSON.stringify(body, null, 2));
      setStatus(!result.ok ? `Request failed with HTTP ${result.status}.` : body?.errors?.length ? 'The response contains GraphQL errors.' : 'Response received.');
    } catch (error) {
      setStatus('Request failed.');
      setResponse(error instanceof Error ? error.message : String(error));
    } finally {
      setRunning(false);
    }
  };
  const copyResponse = async () => {
    try {
      await navigator.clipboard.writeText(response || '');
      setCopied(true);
    } catch {
      setStatus('Could not copy the response.');
    }
  };
  return <details className="api-try-operation not-prose">
    <summary>Try this {operationType}</summary>
    <p>Calls use <code>{endpoint}</code>. Replace sample values with resources you can access. See <a href="/docs/cloud/authentication">Authentication</a> for credentials and permissions.</p>
    <div className="api-request-playground not-prose">
      <label htmlFor={`${operationName}-token`}>Bearer token or API key</label>
      <input id={`${operationName}-token`} type="password" value={token} onChange={event => updateToken(event.target.value)} placeholder="Paste a credential to authenticate" autoComplete="off" />
      <div className="api-request-help">Stored in this tab's session. <button type="button" onClick={() => updateToken('')}>Clear credential</button></div>

      <label htmlFor={`${operationName}-query`}>{operationType === 'mutation' ? 'Mutation' : 'Query'}</label>
      <textarea id={`${operationName}-query`} value={queryText} onChange={event => setQueryText(event.target.value)} rows={12} spellCheck={false} />
      <label htmlFor={`${operationName}-variables`}>Variables (JSON)</label>
      <textarea id={`${operationName}-variables`} value={variablesText} onChange={event => setVariablesText(event.target.value)} rows={3} spellCheck={false} />
      <button className="api-request-submit" type="button" disabled={running} onClick={runOperation}>
        {running ? 'Running…' : operationType === 'mutation' ? 'Run mutation' : 'Run query'}
      </button>
      <p className="api-request-status" role="status" aria-live="polite">{status}</p>
      {response !== null && <div className="api-request-response">
        <div className="api-request-response-heading"><span>Response</span><button type="button" onClick={copyResponse}>{copied ? 'Copied' : 'Copy'}</button></div>
        <pre><code>{response}</code></pre>
      </div>}
    </div>
    </details>;
};

<div className="api-operation-signature">
  `projectSearch(input: SearchProjectsInput): ProjectsConnection`
</div>

Returns 10 records by default, up to 100.

hasNextPage is true only when another matching record exists; use endCursor for the next page.

## Arguments

| Argument | Type | Required |
| - | - | - |
| `input` | [`SearchProjectsInput`](/docs/cloud/graphql/types/searchprojectsinput) | No |

## Input fields

### input

[`SearchProjectsInput`](/docs/cloud/graphql/types/searchprojectsinput)

| Field | Type | Required | Description |
| - | - | - | - |
| `search` | [`String`](/docs/cloud/graphql/types/string) | No | Text to match against project names and slugs. Omit to list the caller's projects. |
| `limit` | [`Int`](/docs/cloud/graphql/types/int) | No | Page size from 1 to 100. Defaults to 10. |
| `cursor` | [`String`](/docs/cloud/graphql/types/string) | No | The previous response's pageInfo.endCursor. |
| `filter` | [`SearchProjectsFilterInput`](/docs/cloud/graphql/types/searchprojectsfilterinput) | No | |

## Response

Returns [`ProjectsConnection`](/docs/cloud/graphql/types/projectsconnection). Open the type for its fields and nested types.

| Field | Type |
| - | - |
| `data` | [`[Project!]!`](/docs/cloud/graphql/types/project) |
| `pageInfo` | [`PageInfo!`](/docs/cloud/graphql/types/pageinfo) |

Types marked with `!` cannot be null. Lists use `[Type]` notation. The [type index](/docs/cloud/graphql/schema) lists all published input and response types.

## Example

<CodeGroup>
  ```graphql title="query.graphql" theme={null}
  query ProjectSearch($input: SearchProjectsInput) {
    projectSearch(input: $input) {
      data {
        id
        name
        slug
        createdAt
        updatedAt
      }
      pageInfo {
        hasNextPage
        endCursor
        hasPreviousPage
        startCursor
      }
    }
  }
  ```

  ```json title="variables.json" theme={null}
  {}
  ```
</CodeGroup>

<GraphQLOperationPlayground operationName="ProjectSearch" operationType="query" query={"query ProjectSearch($input: SearchProjectsInput) {\n  projectSearch(input: $input) {\n    data {\n      id\n      name\n      slug\n      createdAt\n      updatedAt\n    }\n    pageInfo {\n      hasNextPage\n      endCursor\n      hasPreviousPage\n      startCursor\n    }\n  }\n}"} variables={"{}"} />


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.