Skip to content
BasiX Media

Upload from the browser without exposing your key

Your server trades its API key for a short, limited upload token. The browser uploads straight to BasiX with that token. The key never reaches the page.

  1. 1.Keep the key on your server

    Create an upload-only key in Dashboard → API keys and store it as an environment variable on your server, never in front-end code.

    bash
    BASIX_API_KEY=bx_live_…
  2. 2.Add a route that mints an upload token

    POST https://upload.basix.media/api/upload-token with your key. Optional JSON fields: folder (where files land), maxSizeMb (per-file ceiling) and expiresIn (seconds, 60 to 31536000, default 3600). Check who is asking before answering: this route hands out the right to write to your storage.

    javascript
    app.post('/upload-token', requireSignedIn, async (req, res) => {
      const response = await fetch('https://upload.basix.media/api/upload-token', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${process.env.BASIX_API_KEY}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ folder: 'user-uploads', maxSizeMb: 10, expiresIn: 600 }),
      })
      res.status(response.status).json(await response.json())
    })
  3. 3.Upload from the browser

    The browser fetches a token from your route, then sends the file as multipart form data, field "file", with the token as a Bearer. Cross-origin requests are allowed for these tokens.

    javascript
    async function uploadToBasix(file) {
      const tokenResponse = await fetch('/upload-token', { method: 'POST' })
      if (!tokenResponse.ok) throw new Error('Could not get an upload token')
      const { token } = await tokenResponse.json()
    
      const form = new FormData()
      form.append('file', file)
    
      const response = await fetch('https://upload.basix.media/api/v1/upload', {
        method: 'POST',
        headers: { Authorization: `Bearer ${token}` },
        body: form,
      })
      const body = await response.json()
      if (!response.ok) throw new Error(body.error.message)
      return body.url
    }
    
    document.querySelector('input[type=file]').addEventListener('change', async (event) => {
      const url = await uploadToBasix(event.target.files[0])
      document.querySelector('img').src = url
    })
  4. 4.Read the response

    A successful upload answers 201 with the public address of the file.

    json
    {
      "id": "k3j9x2m1p0qa",
      "path": "user-uploads/k3j9x2m1p0qa.png",
      "url": "https://cdn.basix.media/a7k2m9x4/user-uploads/k3j9x2m1p0qa.png",
      "size": 482113,
      "mime": "image/png",
      "name": "photo.png",
      "replaced": false
    }

Worth knowing

  • A token can only upload. It cannot list, read or delete anything.
  • Files are stored as <folder>/<id>.<ext>. A token never overwrites an existing file, and any "path" field sent by the browser is ignored.
  • Files larger than maxSizeMb are refused with 413 FILE_TOO_LARGE.
  • Tokens are tied to the key that minted them: revoke the key and every token it issued stops working at once. The key’s rate limits apply to its tokens.
  • Need a URL that works exactly once instead? Use a presigned upload URL, described in the API reference.

More recipes

← Standalone documentation

Upload from the browser without exposing your key — BasiX