title: Streaming audio from S3 via Express server takes too long
body: <p>I am trying to stream audio files from S3 to my React client by making a request to my Node/Express server. I managed to implement something that works, but I am not sure if I am actually <em>streaming</em> the file here, or simply downloading it. I suspect I might be downloading the file, because my requests to the server take a long time to come back:</p>
  
  <pre><code>Established database connection.
  Server listening on port 9000!
  ::1 - - [18/Apr/2017:21:13:43 +0000] "GET / HTTP/1.1" 200 424 6.933 ms
  ::1 - - [18/Apr/2017:21:13:43 +0000] "GET /static/js/main.700ba5c4.js HTTP/1.1" 200 217574 1.730 ms
  ::1 - - [18/Apr/2017:21:13:43 +0000] "GET /index.css HTTP/1.1" 200 - 8.722 ms
  Server received a request: GET /tracks
  ::1 - - [18/Apr/2017:21:13:43 +0000] "GET /tracks HTTP/1.1" 304 - 41.468 ms
  Server received a request: GET /tracks/1/stream
  ::1 - - [18/Apr/2017:21:14:13 +0000] "GET /tracks/1/stream HTTP/1.1" 200 - 636.249 ms
  Server received a request: GET /tracks/2/stream
  Database query threw an error: ETIMEDOUT
  </code></pre>
  
  <p><strong>Note the 636.249 ms!</strong></p>
  
  <p>Can you guys tell if I am doing anything wrong here? I pasted the code from my current approach below; It does the following:</p>
  
  <ol>
  <li>Client makes a fetch call to <code>/tracks/id/stream</code></li>
  <li>Server queries database to get the track</li>
  <li>Server uses <code>downloadStream</code> (from the <a href="https://www.npmjs.com/package/s3" rel="nofollow noreferrer">s3 package</a>) to get the file</li>
  <li>Server pipes the data to the client</li>
  <li>Client receives the data as an ArrayBuffer</li>
  <li>Client decodes the buffer and passes it to an AudioBufferSourceNode</li>
  <li>AudioBufferSourceNode plays the audio</li>
  </ol>
  
  <p>The server-side:</p>
  
  <pre><code>app.get('/tracks/:id/stream', (req, res) =&gt; {
  
    const id = req.params.id
  
    // Query the database for all tracks
    database.query(`SELECT * FROM Tracks WHERE id = ${id}`, (error, results, fields) =&gt; {
  
      // Upon failure...
      if (error) {
        res.sendStatus(500)
      }
  
      // Upon success...
      const params = {
        Bucket: bucketName, // use bucketName defined elsewhere
        Key: results[0].key // use the key from the track object
      };
  
      // Download stream and pipe to client
      const stream = client.downloadStream(params)
      stream.pipe(res)
    })
  });
  </code></pre>
  
  <p>The client-side fetch call:</p>
  
  <pre><code>const URL = `/tracks/${id}/stream`
  const options = { method: 'GET' }
  
  fetch(URL, options)
  .then(response =&gt; response.arrayBuffer())
  .then(data =&gt; {
  
    // do some stuff
  
    AudioPlayer.play(data)
  })
  </code></pre>
  
  <p>The client-side AudioPlayer, responsible for handling the actual AudioBufferSourceNode:</p>
  
  <pre><code>const AudioPlayer = {
    play: function(data) {
  
      // Decode the audio data
      context.decodeAudioData(data, buffer =&gt; {
  
        // Create a new buffer source
        source = context.createBufferSource()
        source.buffer = buffer
        source.connect(context.destination)
  
        // Start playing immediately
        source.start(context.currentTime)
      })    
    },
  ...
  </code></pre>