Guia completo para carregar arquivos para o Blue usando mutações GraphQL ou API REST


Visão Geral

Este guia demonstra como carregar arquivos para o Blue usando duas abordagens diferentes:

  1. Carregamento Direto via GraphQL (Recomendado) - Carregamento simples em uma etapa com limite de tamanho de arquivo de 256MB
  2. Carregamento via API REST - Processo em três etapas que suporta arquivos maiores de até 4.8GB

Esta é uma comparação dos dois métodos:

Recurso Carregamento GraphQL Carregamento via API REST
Complexity Simple (one request) Complex (three steps)
File Size Limit 256MB per file 4.8GB per file
Batch Upload Up to 10 files Single file only
Implementation Direct mutation Multi-step process
Best For Most use cases Large files only

Carregamento de Arquivos via GraphQL

O método de carregamento via GraphQL fornece uma maneira simples e direta de carregar arquivos com uma única solicitação.

uploadFile

Carrega um único arquivo para o sistema de armazenamento de arquivos e cria uma referência de arquivo no banco de dados.

Entrada:

  • file: Upload! - O arquivo a ser carregado (usando multipart/form-data)
  • projectId: String! - ID ou slug do projeto onde o arquivo será armazenado
  • companyId: String! - ID ou slug da empresa onde o arquivo será armazenado

Retorna: File! - O objeto de arquivo criado

Exemplo:

mutation UploadFile($input: UploadFileInput!) {
  uploadFile(input: $input) {
    id
    uid
    name
    size
    type
    extension
    shared
    createdAt
    project {
      id
      name
    }
    folder {
      id
      title
    }
  }
}

uploadFiles

Carrega vários arquivos para o sistema de armazenamento de arquivos e cria referências de arquivos no banco de dados.

Entrada:

  • files: [Upload!]! - Array de arquivos a serem carregados (máx. 10)
  • projectId: String! - ID ou slug do projeto onde os arquivos serão armazenados
  • companyId: String! - ID ou slug da empresa onde os arquivos serão armazenados

Retorna: [File!]! - Array de objetos de arquivo criados

Exemplo:

mutation UploadFiles($input: UploadFilesInput!) {
  uploadFiles(input: $input) {
    id
    uid
    name
    size
    type
    extension
    shared
    createdAt
  }
}

Implementação do Cliente

Apollo Client (JavaScript)

Carregamento de Um Único Arquivo:

import { gql } from '@apollo/client';

const UPLOAD_FILE = gql`
  mutation UploadFile($input: UploadFileInput!) {
    uploadFile(input: $input) {
      id
      uid
      name
      size
      type
      extension
      shared
      createdAt
    }
  }
`;

// Using file input
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

const { data } = await uploadFile({
  variables: {
    input: {
      file: file,
      projectId: "project_123", // or "my-project-slug"
      companyId: "company_456"  // or "my-company-slug"
    }
  }
});
@@CB##f959ab83-76e1-4e18-9eb3-ef26b9744731##CB@@javascript
const UPLOAD_FILES = gql`
  mutation UploadFiles($input: UploadFilesInput!) {
    uploadFiles(input: $input) {
      id
      uid
      name
      size
      type
      extension
      shared
      createdAt
    }
  }
`;

// Using multiple file inputs
const fileInputs = document.querySelectorAll('input[type="file"]');
const files = Array.from(fileInputs).map(input => input.files[0]).filter(Boolean);

const { data } = await uploadFiles({
  variables: {
    input: {
      files: files,
      projectId: "project_123", // or "my-project-slug"
      companyId: "company_456"  // or "my-company-slug"
    }
  }
});

Vanilla JavaScript

Single File Upload:

<!-- HTML -->
<input type="file" id="fileInput" />
<button onclick="uploadFile()">Upload File</button>
async function uploadFile() {
  const fileInput = document.getElementById('fileInput');
  const file = fileInput.files[0];

  if (!file) {
    alert('Please select a file');
    return;
  }

  // Create GraphQL mutation
  const query = `
    mutation UploadFile($input: UploadFileInput!) {
      uploadFile(input: $input) {
        id
        name
        size
        type
        extension
        createdAt
      }
    }
  `;

  // Prepare form data
  const formData = new FormData();
  formData.append('operations', JSON.stringify({
    query: query,
    variables: {
      input: {
        file: null, // Will be replaced by file
        projectId: "your_project_id", // or "your-project-slug"
        companyId: "your_company_id"  // or "your-company-slug"
      }
    }
  }));

  formData.append('map', JSON.stringify({
    "0": ["variables.input.file"]
  }));

  formData.append('0', file);

  try {
    const response = await fetch('/graphql', {
      method: 'POST',
      body: formData,
      headers: {
        // Don't set Content-Type - let browser set it with boundary
        'Authorization': 'Bearer your_auth_token'
      }
    });

    const result = await response.json();

    if (result.errors) {
      console.error('Upload failed:', result.errors);
      alert('Upload failed: ' + result.errors[0].message);
    } else {
      console.log('Upload successful:', result.data.uploadFile);
      alert('File uploaded successfully!');
    }
  } catch (error) {
    console.error('Network error:', error);
    alert('Network error during upload');
  }
}
@@CB##862c4d7b-2b18-466b-ad00-7431489b6d2a##CB@@html
<!-- HTML -->
<input type="file" id="filesInput" multiple />
<button onclick="uploadFiles()">Upload Files</button>
@@CB##0976926a-0c5c-4268-9bf5-091dbc1a97ee##CB@@javascript
async function uploadFiles() {
  const filesInput = document.getElementById('filesInput');
  const files = Array.from(filesInput.files);

  if (files.length === 0) {
    alert('Please select files');
    return;
  }

  if (files.length > 10) {
    alert('Maximum 10 files allowed');
    return;
  }

  const query = `
    mutation UploadFiles($input: UploadFilesInput!) {
      uploadFiles(input: $input) {
        id
        name
        size
        type
        extension
        createdAt
      }
    }
  `;

  const formData = new FormData();

  // Create file placeholders for variables
  const fileVariables = files.map((_, index) => null);

  formData.append('operations', JSON.stringify({
    query: query,
    variables: {
      input: {
        files: fileVariables,
        projectId: "your_project_id", // or "your-project-slug"
        companyId: "your_company_id"  // or "your-company-slug"
      }
    }
  }));

  // Create map for file replacements
  const map = {};
  files.forEach((_, index) => {
    map[index.toString()] = [`variables.input.files.${index}`];
  });
  formData.append('map', JSON.stringify(map));

  // Append actual files
  files.forEach((file, index) => {
    formData.append(index.toString(), file);
  });

  try {
    const response = await fetch('/graphql', {
      method: 'POST',
      body: formData,
      headers: {
        'Authorization': 'Bearer your_auth_token'
      }
    });

    const result = await response.json();

    if (result.errors) {
      console.error('Upload failed:', result.errors);
      alert('Upload failed: ' + result.errors[0].message);
    } else {
      console.log('Upload successful:', result.data.uploadFiles);
      alert(`${result.data.uploadFiles.length} arquivos carregados com sucesso!`);
    }
  } catch (error) {
    console.error('Network error:', error);
    alert('Network error during upload');
  }
}
@@CB##3dd202b7-b5c9-4948-8df4-92f5f3bf7a80##CB@@bash
# Single file upload with cURL
curl -X POST \
  -H "Authorization: Bearer your_auth_token" \
  -F 'operations={"query":"mutation UploadFile($input: UploadFileInput!) { uploadFile(input: $input) { id name size type extension createdAt } }","variables":{"input":{"file":null,"projectId":"your_project_id","companyId":"your_company_id"}}}' \
  -F 'map={"0":["variables.input.file"]}' \
  -F '0=@/path/to/your/file.jpg' \
  https://your-api.com/graphql

REST API Upload

Use this method for files larger than 256MB (up to 4.8GB). This approach uses a three-step process: request upload credentials, upload to storage, then register the file in the database.

Prerequisites:

  • Python 3.x installed
  • requests library installed: pip install requests
  • A valid X-Bloo-Token-ID and X-Bloo-Token-Secret for Blue API authentication
  • The file to upload (e.g., test.jpg) in the same directory as the script

This method covers two scenarios:

  1. Uploading to the "File Tab"
  2. Uploading to the "Todo File Custom Field"

Configuration

Define these constants at the top of your script:

@@CB##1851e2df-bb57-4d5c-9ba0-ad4caa91f13d##CB@@

This is diagram that shows the flow of the upload process:

Upload Process

Uploading to File Tab

::code-group

@@CB##d0cd9749-1703-4a34-9755-972e8b2cdf17##CB@@
::

Steps Explained

Step 1: Request Upload Credentials

  • Send a GET request to https://api.blue.cc/uploads?filename=test.jpg@@CODE##dc4387ab-bedf-4178-b1e8-ae0f6ccad80a##CODE@@files parameter in requests.post to send a multipart/form-data request to the S3 URL
  • Ensures all fields match the policy exactly, avoiding curl's formatting issues

Step 3: Register File Metadata

  • Sends a GraphQL mutation to https://api.blue.cc/graphql
  • Dynamically calculates file size with os.path.getsize

Sample Response:
@@CB##fa0b6d2d-0de4-415c-a80a-b51c40a04a7c##CB@@

Uploading to Custom Field

::code-group

@@CB##66775eb5-690b-46a7-a742-42401c8a8ca5##CB@@
::

Steps 1-3 are the same as the File Tab process (fetch credentials, upload to S3, and register file metadata).

For step 4, you need to associate the file with a todo custom field.

  • Sends a GraphQL mutation to link the file to a todo custom field
  • Uses todoId@@CODE##e88d1c12-6e3e-4c77-8e0e-2143c7a9fb41##CODE@@customFieldId specific to your setup

Sample Response:

{
  "data": {
    "createTodoCustomFieldFile": true
  }
}

Assistente de IA

As respostas são geradas usando IA e podem conter erros.

Como posso ajudá-lo?

Pergunte-me qualquer coisa sobre o Blue ou esta documentação.

Digite para enviar • Shift+Enter para nova linha • ⌘I para abrir