CGI - это соглашения о том, как должна считывать и выдавать данные программа, работающая под управлением веб-сервера. Это достаточно низкоуровневые соглашения. Есть более высокий уровень, напримр WSGI/ASGI в экосистеме Python. Или сервлеты в экосистеме Java.
  Для node.js разговор о CGI неактуален, т.к. приложение на node само является сервером и получает данные непосредственно от модулей, реализующих стек протоколов интернета.
  Веб-сервер обычно помещает такие значения, как строку GET-запроса, в переменные окружения, доступные программе с помощью кода типа getenv("QUERY_STRING"). 
  Данные в теле POST-запроса считываются из стандартного потока ввода с помощью кода типа fread(data, 1, num_bytes, stdin)
  

Программа может обработать данные и поместить результат (заголовки и контент) в стандартный поток вывода (stdout) с помощью кода типа printf("Content-type: text/html\n\n");

Скомпилированная программа выполняется очень быстро, а возможности таких языков, как C, очень широки. Поэтому и сложилась практика поручать таким программам осуществлять вычисления, забирая полученный результат и отправляя веб-клиенту. 

В настоящий момент практически повсеместно используется более быстрый и безопасный вариант интерфейса FastCGI. FastCGI взаимодействует с клиентом через сокеты или TCP/IP-соединение. На основе FastCGI работает PHP-FPM. FPM считают лучшим способом взаимодействовать с PHP при использовании веб-сервера NGINX.  PHP-FPM выполняет запросы веб-сервера на исполнение PHP-сценариев.

При использовании PHP-FPM появляется возможность масштабировать веб-сервер и приложение раздельно. В связке Apache+mod_php PHP суть процесс в составе веб-сервера. И деплоятся они вместе. В случае PHP-FPM они могут жить раздельно, как веб-сервер и сервер баз данных. Всё это не говоря уже о том, что Apache запускает отдельный дочерний процесс для каждого запроса...
 
  


  Проведём эксперимент.
  Возьмём файл script.c (curl) https://github.com/GossJS/php_starters1/blob/cgi/script.c и скомпилируем его на компьютере под управлением Ubuntu. 
    

gcc ./script.c -o 'script.cgi'
    

ls -la script.cgi
  -rwxr-xr-x 1 root root 8904 Dec 16 12:43 script.cgi 
whoami
  root
    
Компилятор проставил биты исполнимости во всех трёх разделах прав. Для исполнения из веба это избыточно - выясните, какое условие здесь необходимо и достаточно при использовании сервера NGINX. Чтобы сценарий CGI выполнился обычным образом, он должен быть вызван веб-сервером. Обычно исполнимый файл помещается в папку типа cgi-bin с соответствующими настройками. Задача здесь в том, чтобы не допустить выдачи клиенту самого файла - в нём может содержаться чувствительная информация, хотя он и скомпилирован. https://kodaktor.ru/cgi-bin/script.cgi При использовании таких технологий как Node мы не можем просто указать этот путь и ожидать, что каким-то образом файл выполнится и его стандартный вывод уйдёт в браузер. Мы вообще не знаем в общем случае, что означают эти знаки после доменного имени. Но если на сервере kodaktor.ru работает на линуксе веб-сервер типа NGINX и в папке типа /var/www/cgi-bin находится исполнимый файл script.cgi, то при таком обращении веб-сервер позволит файлу выполниться и отдаст клиенту результат работы. А в случае Node.js шаги будут другими.

     app.get('/cgi-bin/script.cgi', async r=>{
       const execFile = require('util').promisify(require('child_process').execFile);
       const { stdout } = await execFile( `${__dirname}/script.cgi`);
       r.res.send(stdout);
     });
    
Если исполнимый файл существует, то он совершенно не обязан находится в папке cgi-bin. Но при обращении к маршруту /cgi-bin/script.cgi node передаст ему управление, а затем выдаст в Response результат. Пример непосредственной работы с потоком ввода: // PHP – нет встроенного массива $_PUT, считываем из stdin $method = $_SERVER['REQUEST_METHOD']; if ('PUT' === $method) { parse_str(file_get_contents('php://input'), $_PUT); var_dump($_PUT); //$_PUT contains put fields } // node.js http – аналогично require('http') .Server((req, res) => req.on('data', d => console.log(String(d))) .on('end', () => res.end('Hello!'))) .listen(4321, () => console.log(process.pid));