Showing posts with label Javascript. Show all posts
Showing posts with label Javascript. Show all posts

Wednesday, February 12, 2025

Local Translation API in Chrome-based Browsers

Client-side Translator API with a model built into Chrome (from version 131).

Possible use case: customer support chat which allows for users to type in their first language and receive real-time translation for the support agent. github.com/webmachinelearning/translation-api

The Translator API has two important methods:

  • canTranslate(): Checks if a translation model for your language pair is ready. Returns "readily" if the model is already available on device, "after-download" if the browser first needs to download the model, and "no" if translation is not possible.
  • createTranslator(): This sets up your Translator object asynchronously. If the model needs downloading, it'll wait until it's ready.

The Translator object has just one method:

  • translate(): Feed it the source text, and it outputs the translated version.

As this is experimental and Chrome-specific for now, be sure to wrap all your code in feature detection.

const supportsOnDevice = 'model' in window && 'createTranslator' in model;
if (!supportsOnDevice) {
  return;
}

const parameters = { sourceLanguage: 'en', targetLanguage: 'pt' };
const modelState = await model.canTranslate(parameters);
if (modelState === 'no') {
  return;
}
const onDeviceTranslator = await model.createTranslator(parameters);

const result = await onDeviceTranslator.translate(input);
if (!result) {
  throw new Error('Failed to translate');
}
return result;

The model needs time to become available to the user. You can approach this in two ways:

  • Wait to enable your translation-powered UI elements once the model is ready.
  • Start with server-side AI for translation, then switch to client-side once the model has downloaded.

Sign up for the Translator API origin trial to enable your translation features for all users on your origin, on Chrome. Opening an Issue on the Explainer.

Use the Translator API in Chrome to translate text in the browser, using local AI models.

Translation of content on the web has typically required using a cloud service. First, the source content is uploaded to a server, which runs the translation to a target language, then the resulting text is downloaded and returned to the user. By running translation on the client, you save the time required by server trips and the cost of hosting the translation service.

Join the Translator API origin trial, running in Chrome beginning with version 131 (Chrome and derivative branches like Dev, Canary).

While you always know the target language for translations, you may not always know the source language, such as in user-generated content. For such cases, the Translator API proposal includes both the Translator API and the Language Detector API, also available in an origin trial. Sign up for both origin trials to use these APIs together.

To start using the Translator API, follow these steps:

  1. Acknowledge Google's Generative AI Prohibited Uses Policy.
  2. Go to the Translator API origin trial.
  3. Click Register and fill out the form.
    • In the Web origin field, provide your origin or extension ID, chrome-extension://YOUR_EXTENSION_ID.
  4. To submit, click Register.
  5. Copy the token provided, and add it to every web page on your origin or file for your Extension, on which you want the trial to be enabled.
  6. Start using the Translator API.

Learn more about how to get started with origin trials.

To access the Translator API on localhost during the origin trial, you must update Chrome to the latest version. Then, follow these steps:

  1. Go to chrome://flags/#translation-api.
  2. Select Enabled.
    • To try more language pairs, select Enabled without language pack limit.
  3. Click Relaunch or restart Chrome.

To determine if the Translator API is supported, run the following feature detection snippet.

if ('ai' in self && 'translator' in self.ai) {
  // The Translator API is supported.
}

Translation is managed with language packs, downloaded on demand. A language pack is like a dictionary for a given language.

  • sourceLanguage: The current language for the text.
  • targetLanguage: The final language the text should be translated into.

Use BCP 47 language short codes as strings. For example, 'es' for Spanish or 'fr' for French.

 const translatorCapabilities = await self.ai.translator.capabilities();
 translatorCapabilities.languagePairAvailable('es', 'fr');
 // 'readily'

The languagePairAvailable() function can return any of the following results:

  • no: It's not possible for this browser to translate as requested.
  • readily: The browser can translate as requested.
  • after-download: The browser can perform the translation, but only after it downloads the relevant model or language packs.

You can listen for model download progress using the downloadprogress event:

const translator = await self.ai.translator.create({
  sourceLanguage: 'es',
  targetLanguage: 'fr',
  monitor(m) {
    m.addEventListener('downloadprogress', (e) => {
      console.log(`Downloaded ${e.loaded} of ${e.total} bytes.`);
    });
  },
});

If the download fails, then downloadprogress events stop being emitted and the ready promise is rejected.

To create a translator, call the asynchronous translation.createTranslator() function. Like canTranslate(), it requires an options parameter with two fields, one for the sourceLanguage and one for the targetLanguage.

// Create a translator that translates from English to French.
const translator = await self.ai.translator.create({
  sourceLanguage: 'en',
  targetLanguage: 'fr',
});

Once you have a translator, call the asynchronous translate() function to translate your text.

await translator.translate('Where is the next bus stop, please?');
// "Où est le prochain arrêt de bus, s'il vous plaît ?"

The following limitations apply during the origin trial.

At this time, up to three language packs can be downloaded for translation. We're committed to expand the range of supported languages in future releases, while maintaining high standards for user privacy. You can confirm if the language pair you need is supported with the languagePairAvailable() function.

It's possible that certain, less frequently used language pairs may be used for fingerprinting. For example, it's more common to translate between English and Spanish than between less common languages, such as Gaelic and Catalan. A less common language pair could be considered a data point for user identification.

During the origin trial, we're limiting the potential translatable language pairs to protect user privacy. Language pairs must meet the following criteria:

  • Both the source and the destination language are set as preferred languages in Chrome.
  • Or, one of he languages is set as a preferred language in Chrome, and the other is among the following popular languages:
    • English (en)
    • Mandarin Chinese (zh; simplified) or Taiwanese Mandarin (zh-Hant; traditional)
    • Japanese (ja)
    • Portuguese (pt)
    • Russian (ru)
    • Spanish (es)
    • Turkish (tr)
    • Hindi (hi)
    • Vietnamese (vi)
    • Bengali (bn)

For local prototyping, you can bypass these checks by running Chrome with the command line option --disable-features=TranslationAPIAcceptLanguagesCheck. Alternatively, set chrome://flags/#translation-api to Enable without language pack limit.

Visit chrome://on-device-translation-internals/ to manually install and uninstall language packs.

Translations are processed sequentially. If you send large amounts of text to be translated, subsequent translations are blocked until the earlier ones complete.

For the best responsiveness of your translation requests, chunk them together and consider displaying a loading interface, such as a spinner, to convey that a translation is ongoing.

During the origin trial, the Translator API is only supported from the main thread. We intend to support it in web workers once the API is widely available.

You can see the Translator API, used in combination with the Language Detector API, in the Translator and Language Detector API playground.

We're working to standardize the Translator API, to ensure cross-browser compatibility.

Our API proposal received community support and has moved to the W3C Web Incubator Community Group for further discussion. The Chrome team requested feedback from the W3C Technical Architecture Group and asked Mozilla and WebKit for their standards positions.

Start testing the Translator API now by joining the origin trial and share your feedback. Your input can directly impact how we build and implement future versions of this API, and all built-in AI APIs.

  • Install Translation Detection API on Chrome

  • Go to chrome://flags/#language-detection-api.
  • Select Enabled
  • Go to chrome://flags/#translation-api.
  • Select Enabled without language pack limit to try more language pairs.
  • Click Relaunch or restart Chrome.
  • Open a new tab, go to chrome://components.
  • Find Chrome TranslateKit
  • Click "Check for update" button to download the language model. The version number should update.
  • (Optional) Open a new tab, go to chrome://on-device-translation-internals/
  • (Optional) Install language pairs.

 

Wednesday, September 4, 2024

Boilerplate Mongoose MongoDB

MyApp.js

require('dotenv').config();
// const MongoClient = require("mongodb").MongoClient;
const mongoose = require('mongoose');
const uri = process.env['MONGO_URI'];
// mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
// mongoose.connection.on('open', function (ref) {
//     console.log('Connected to mongo server.');

// mongoose.connection.db.listCollections().toArray(function (err, names) {
//         console.log(names);
//     });
// })
/// create a connection to the DB    
mongoose.set('strictQuery', true);
mongoose.connect(uri, {useNewUrlParser: true, useUnifiedTopology: true});
const db = mongoose.connection;

// Check DB Connection
db.once('open', () => {
  (async () => {
    const data = await mongoose.connection.db.admin().command({
      listDatabases: 1,
    });
    console.log(data);
  })();
  console.log('Connected to MongoDB');
});

// Check for DB errors
db.on('error', (err) => {
  console.log('DB Connection errors', err);
});
// dbs = mongoose.connection.db.listCollections().toArray()
// console.log(uri, mongoose.connection.readyState, dbs);

// const mongo_username = process.env["MONGO_USERNAME"];
// const mongo_password = process.env["MONGO_PASSWORD"];
// const uri = `mongodb+srv://${mongo_username}:${mongo_password}@cluster0-zrtwi.gcp.mongodb.net/crmdb?retryWrites=true&w=majority`;
// const client = new MongoClient(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
// collection = client.db("sample_training").collection("people");
// console.log(collection)
// mongooseclient = mongoose.connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });

// const client = new MongoClient(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
// baza = mongooseclient.then((db) => db.db("sample_training"));
// const collections = client.db('sample_training').collection('people');
// const databases = db.listCollections().toArray();
// console.log('current db', baza)

// 2. Create a Model
const Schema = mongoose.Schema;

const personSchema = new Schema({
  name:  {type: String, required: true},
  age: Number,
  favoriteFoods: [String]
});

const Person = mongoose.model('Person', personSchema);

// let Person;

const createAndSavePerson = (done) => {
  done(null /*, data*/);
};

const createManyPeople = (arrayOfPeople, done) => {
  done(null /*, data*/);
};

const findPeopleByName = (personName, done) => {
  done(null /*, data*/);
};

const findOneByFood = (food, done) => {
  done(null /*, data*/);
};

const findPersonById = (personId, done) => {
  done(null /*, data*/);
};

const findEditThenSave = (personId, done) => {
  const foodToAdd = "hamburger";

  done(null /*, data*/);
};

const findAndUpdate = (personName, done) => {
  const ageToSet = 20;

  done(null /*, data*/);
};

const removeById = (personId, done) => {
  done(null /*, data*/);
};

const removeManyPeople = (done) => {
  const nameToRemove = "Mary";

  done(null /*, data*/);
};

const queryChain = (done) => {
  const foodToSearch = "burrito";

  done(null /*, data*/);
};

/** **Well Done !!**
/* You completed these challenges, let's go celebrate !
 */

//----- **DO NOT EDIT BELOW THIS LINE** ----------------------------------

exports.PersonModel = Person;
exports.createAndSavePerson = createAndSavePerson;
exports.findPeopleByName = findPeopleByName;
exports.findOneByFood = findOneByFood;
exports.findPersonById = findPersonById;
exports.findEditThenSave = findEditThenSave;
exports.findAndUpdate = findAndUpdate;
exports.createManyPeople = createManyPeople;
exports.removeById = removeById;
exports.removeManyPeople = removeManyPeople;
exports.queryChain = queryChain;

/*
require('dotenv').config();


// 1. Install and Set Up Mongoose
const mongoose = require('mongoose');

const uri = process.env['MONGO_URI'];

mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });


// 2. Create a Model
const Schema = mongoose.Schema;

const personSchema = new Schema({
  name:  {type: String, required: true},
  age: Number,
  favoriteFoods: [String]
});

const Person = mongoose.model('Person', personSchema);


// 3. Create and Save a Record of a Model
const createAndSavePerson = (done) => {
  var janeFonda = new Person({
    name: "Jane Fonda",
    age: 84,
    favoriteFoods: ["eggs", "fish", "fresh fruit"]
  });

  janeFonda.save(function(err, data) {
    if (err) return console.error(err);
    done(null, data)
  });
};


// 4. Create Many Records with model.create()
var arrayOfPeople = [
  {name: "Frankie", age: 74, favoriteFoods: ["Del Taco"]},
  {name: "Sol", age: 76, favoriteFoods: ["roast chicken"]},
  {name: "Robert", age: 78, favoriteFoods: ["wine"]}
];

const createManyPeople = (arrayOfPeople, done) => {
  Person.create(arrayOfPeople, function (err, people) {
    if (err) return console.log(err);
    done(null, people);
  });
};


// 5. Use model.find() to Search Your Database
const findPeopleByName = (personName, done) => {
  Person.find({name: personName}, function (err, personFound) {
    if (err) return console.log(err);
    done(null, personFound);
  });
};


// 6. Use model.findOne() to Return a Single Matching Document from Your Database
const findOneByFood = (food, done) => {
  Person.findOne({favoriteFoods: food}, function (err, data) {
    if (err) return console.log(err);
    done(null, data);
  });
};


// 7. Use model.findById() to Search Your Database By _id
const findPersonById = (personId, done) => {
  Person.findById(personId, function (err, data) {
    if (err) return console.log(err);
    done(null, data);
  });
};


// 8. Perform Classic Updates by Running Find, Edit, then Save
const findEditThenSave = (personId, done) => {
  const foodToAdd = 'hamburger';

  // .findById() method to find a person by _id with the parameter personId as search key.
  Person.findById(personId, (err, person) => {
    if(err) return console.log(err);

    // Array.push() method to add "hamburger" to the list of the person's favoriteFoods
    person.favoriteFoods.push(foodToAdd);

    // and inside the find callback - save() the updated Person.
    person.save((err, updatedPerson) => {
      if(err) return console.log(err);
      done(null, updatedPerson)
    })
  })
};


// 9. Perform New Updates on a Document Using model.findOneAndUpdate()
const findAndUpdate = (personName, done) => {
  const ageToSet = 20;

  Person.findOneAndUpdate({name: personName}, {age: ageToSet}, {new: true}, (err, updatedDoc) => {
    if(err) return console.log(err);
    done(null, updatedDoc);
  })
};


// 10. Delete One Document Using model.findByIdAndRemove
const removeById = (personId, done) => {
  Person.findByIdAndRemove(
    personId,
    (err, removedDoc) => {
      if(err) return console.log(err);
      done(null, removedDoc);
    }
  );
};


// 11. Delete Many Documents with model.remove()
const removeManyPeople = (done) => {
  const nameToRemove = "Mary";
  Person.remove({name: nameToRemove}, (err, response) => {
    if(err) return console.log(err);
    done(null, response);
  })
};


// 12. Chain Search Query Helpers to Narrow Search Results
const queryChain = (done) => {
  const foodToSearch = "burrito";

  Person.find({ favoriteFoods: foodToSearch })
  .sort({ name: 1 })
  .limit(2)
  .select({ age: 0 })
  .exec(function(err, data) {
    if(err) return console.log(err);
    done(null, data);
  });

};



/** **Well Done !!**
/* You completed these challenges, let's go celebrate !
 */

//----- **DO NOT EDIT BELOW THIS LINE** ----------------------------------

exports.PersonModel = Person;
exports.createAndSavePerson = createAndSavePerson;
exports.findPeopleByName = findPeopleByName;
exports.findOneByFood = findOneByFood;
exports.findPersonById = findPersonById;
exports.findEditThenSave = findEditThenSave;
exports.findAndUpdate = findAndUpdate;
exports.createManyPeople = createManyPeople;
exports.removeById = removeById;
exports.removeManyPeople = removeManyPeople;
exports.queryChain = queryChain;

*/

server.js

/********************************************
 * DO NOT EDIT THIS FILE
 * the verification process may break
 *******************************************/

const express = require("express");
const app = express();
let mongoose;
try {
  mongoose = require("mongoose");
} catch (e) {
  console.log(e);
}
const fs = require("fs");
const path = require("path");
const bodyParser = require("body-parser");
const router = express.Router();

const enableCORS = function (req, res, next) {
  if (!process.env.DISABLE_XORIGIN) {
    const allowedOrigins = ["https://www.freecodecamp.org"];
    const origin = req.headers.origin;
    if (!process.env.XORIGIN_RESTRICT || allowedOrigins.indexOf(origin) > -1) {
      console.log(req.method);
      res.set({
        "Access-Control-Allow-Origin": origin,
        "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
        "Access-Control-Allow-Headers":
          "Origin, X-Requested-With, Content-Type, Accept",
      });
    }
  }
  next();
};

// global setting for safety timeouts to handle possible
// wrong callbacks that will never be called
const TIMEOUT = 10000;

app.use(bodyParser.urlencoded({ extended: "false" }));
app.use(bodyParser.json());

app.get("/", function (req, res) {
  res.sendFile(path.join(__dirname, "views", "index.html"));
});

router.get("/file/*?", function (req, res, next) {
  if (req.params[0] === ".env") {
    return next({ status: 401, message: "ACCESS DENIED" });
  }
  fs.readFile(path.join(__dirname, req.params[0]), function (err, data) {
    if (err) {
      return next(err);
    }
    res.type("txt").send(data.toString());
  });
});

router.get("/is-mongoose-ok", function (req, res) {
  if (mongoose) {
    res.json({ isMongooseOk: !!mongoose.connection.readyState });
  } else {
    res.json({ isMongooseOk: false });
  }
});

const Person = require("./myApp.js").PersonModel;

router.use(function (req, res, next) {
  if (req.method !== "OPTIONS" && Person.modelName !== "Person") {
    return next({ message: "Person Model is not correct" });
  }
  next();
});

router.post("/mongoose-model", function (req, res, next) {
  // try to create a new instance based on their model
  // verify it's correctly defined in some way
  let p;
  p = new Person(req.body);
  res.json(p);
});

const createPerson = require("./myApp.js").createAndSavePerson;
router.get("/create-and-save-person", function (req, res, next) {
  // in case of incorrect function use wait timeout then respond
  let t = setTimeout(() => {
    next({ message: "timeout" });
  }, TIMEOUT);
  createPerson(function (err, data) {
    clearTimeout(t);
    if (err) {
      return next(err);
    }
    if (!data) {
      console.log("Missing `done()` argument");
      return next({ message: "Missing callback argument" });
    }
    Person.findById(data._id, function (err, pers) {
      if (err) {
        return next(err);
      }
      res.json(pers);
      pers.remove();
    });
  });
});

const createPeople = require("./myApp.js").createManyPeople;
router.post("/create-many-people", function (req, res, next) {
  Person.remove({}, function (err) {
    if (err) {
      return next(err);
    }
    // in case of incorrect function use wait timeout then respond
    let t = setTimeout(() => {
      next({ message: "timeout" });
    }, TIMEOUT);
    createPeople(req.body, function (err, data) {
      clearTimeout(t);
      if (err) {
        return next(err);
      }
      if (!data) {
        console.log("Missing `done()` argument");
        return next({ message: "Missing callback argument" });
      }
      Person.find({}, function (err, pers) {
        if (err) {
          return next(err);
        }
        res.json(pers);
        Person.remove().exec();
      });
    });
  });
});

const findByName = require("./myApp.js").findPeopleByName;
router.post("/find-all-by-name", function (req, res, next) {
  let t = setTimeout(() => {
    next({ message: "timeout" });
  }, TIMEOUT);
  Person.create(req.body, function (err, pers) {
    if (err) {
      return next(err);
    }
    findByName(pers.name, function (err, data) {
      clearTimeout(t);
      if (err) {
        return next(err);
      }
      if (!data) {
        console.log("Missing `done()` argument");
        return next({ message: "Missing callback argument" });
      }
      res.json(data);
      Person.remove().exec();
    });
  });
});

const findByFood = require("./myApp.js").findOneByFood;
router.post("/find-one-by-food", function (req, res, next) {
  let t = setTimeout(() => {
    next({ message: "timeout" });
  }, TIMEOUT);
  let p = new Person(req.body);
  p.save(function (err, pers) {
    if (err) {
      return next(err);
    }
    findByFood(pers.favoriteFoods[0], function (err, data) {
      clearTimeout(t);
      if (err) {
        return next(err);
      }
      if (!data) {
        console.log("Missing `done()` argument");
        return next({ message: "Missing callback argument" });
      }
      res.json(data);
      p.remove();
    });
  });
});

const findById = require("./myApp.js").findPersonById;
router.get("/find-by-id", function (req, res, next) {
  let t = setTimeout(() => {
    next({ message: "timeout" });
  }, TIMEOUT);
  let p = new Person({ name: "test", age: 0, favoriteFoods: ["none"] });
  p.save(function (err, pers) {
    if (err) {
      return next(err);
    }
    findById(pers._id, function (err, data) {
      clearTimeout(t);
      if (err) {
        return next(err);
      }
      if (!data) {
        console.log("Missing `done()` argument");
        return next({ message: "Missing callback argument" });
      }
      res.json(data);
      p.remove();
    });
  });
});

const findEdit = require("./myApp.js").findEditThenSave;
router.post("/find-edit-save", function (req, res, next) {
  let t = setTimeout(() => {
    next({ message: "timeout" });
  }, TIMEOUT);
  let p = new Person(req.body);
  p.save(function (err, pers) {
    if (err) {
      return next(err);
    }
    try {
      findEdit(pers._id, function (err, data) {
        clearTimeout(t);
        if (err) {
          return next(err);
        }
        if (!data) {
          console.log("Missing `done()` argument");
          return next({ message: "Missing callback argument" });
        }
        res.json(data);
        p.remove();
      });
    } catch (e) {
      console.log(e);
      return next(e);
    }
  });
});

const update = require("./myApp.js").findAndUpdate;
router.post("/find-one-update", function (req, res, next) {
  let t = setTimeout(() => {
    next({ message: "timeout" });
  }, TIMEOUT);
  let p = new Person(req.body);
  p.save(function (err, pers) {
    if (err) {
      return next(err);
    }
    try {
      update(pers.name, function (err, data) {
        clearTimeout(t);
        if (err) {
          return next(err);
        }
        if (!data) {
          console.log("Missing `done()` argument");
          return next({ message: "Missing callback argument" });
        }
        res.json(data);
        p.remove();
      });
    } catch (e) {
      console.log(e);
      return next(e);
    }
  });
});

const removeOne = require("./myApp.js").removeById;
router.post("/remove-one-person", function (req, res, next) {
  Person.remove({}, function (err) {
    if (err) {
      return next(err);
    }
    let t = setTimeout(() => {
      next({ message: "timeout" });
    }, TIMEOUT);
    let p = new Person(req.body);
    p.save(function (err, pers) {
      if (err) {
        return next(err);
      }
      try {
        removeOne(pers._id, function (err, data) {
          clearTimeout(t);
          if (err) {
            return next(err);
          }
          if (!data) {
            console.log("Missing `done()` argument");
            return next({ message: "Missing callback argument" });
          }
          console.log(data);
          Person.count(function (err, cnt) {
            if (err) {
              return next(err);
            }
            data = data.toObject();
            data.count = cnt;
            console.log(data);
            res.json(data);
          });
        });
      } catch (e) {
        console.log(e);
        return next(e);
      }
    });
  });
});

const removeMany = require("./myApp.js").removeManyPeople;
router.post("/remove-many-people", function (req, res, next) {
  Person.remove({}, function (err) {
    if (err) {
      return next(err);
    }
    let t = setTimeout(() => {
      next({ message: "timeout" });
    }, TIMEOUT);
    Person.create(req.body, function (err, pers) {
      if (err) {
        return next(err);
      }
      try {
        removeMany(function (err, data) {
          clearTimeout(t);
          if (err) {
            return next(err);
          }
          if (!data) {
            console.log("Missing `done()` argument");
            return next({ message: "Missing callback argument" });
          }
          Person.count(function (err, cnt) {
            if (err) {
              return next(err);
            }
            if (data.ok === undefined) {
              // for mongoose v4
              try {
                data = JSON.parse(data);
              } catch (e) {
                console.log(e);
                return next(e);
              }
            }
            res.json({
              n: data.n,
              count: cnt,
              ok: data.ok,
            });
          });
        });
      } catch (e) {
        console.log(e);
        return next(e);
      }
    });
  });
});

const chain = require("./myApp.js").queryChain;
router.post("/query-tools", function (req, res, next) {
  let t = setTimeout(() => {
    next({ message: "timeout" });
  }, TIMEOUT);
  Person.remove({}, function (err) {
    if (err) {
      return next(err);
    }
    Person.create(req.body, function (err, pers) {
      if (err) {
        return next(err);
      }
      try {
        chain(function (err, data) {
          clearTimeout(t);
          if (err) {
            return next(err);
          }
          if (!data) {
            console.log("Missing `done()` argument");
            return next({ message: "Missing callback argument" });
          }
          res.json(data);
        });
      } catch (e) {
        console.log(e);
        return next(e);
      }
    });
  });
});

app.use("/_api", enableCORS, router);

// Error handler
app.use(function (err, req, res, next) {
  if (err) {
    res
      .status(err.status || 500)
      .type("txt")
      .send(err.message || "SERVER ERROR");
  }
});

// Unmatched routes handler
app.use(function (req, res) {
  if (req.method.toLowerCase() === "options") {
    res.end();
  } else {
    res.status(404).type("txt").send("Not Found");
  }
});

const listener = app.listen(process.env.PORT || 3000, function () {
  console.log("Your app is listening on port " + listener.address().port);
});

/********************************************
 * DO NOT EDIT THIS FILE
 * the verification process may break
 *******************************************/

MongoDB and Mongoose Challenges

This is the boilerplate for the MongoDB and Mongoose lessons. Instructions for completing these lessons start at https://www.freecodecamp.org/learn/apis-and-microservices/mongodb-and-mongoose/

package.json

{
  "name": "fcc-mongo-mongoose-challenges",
  "version": "0.0.1",
  "description": "A boilerplate project",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "body-parser": "^1.15.2",
    "dotenv": "^8.2.0",
    "express": "^4.12.4",
    "mongodb": "^6.1.0",
    "mongoose": "^5.11.15"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/freeCodeCamp/boilerplate-mongomongoose.git"
  },
  "keywords": [
    "node",
    "mongoose",
    "express"
  ],
  "license": "MIT"
}
sample.env

MONGO_URI= 

views/index.html

<!-- This is a static file -->
<!-- served from your routes in server.js -->

<!-- You might want to try something fancier: -->
<!-- html/nunjucks docs: http://mozilla.github.io/nunjucks/ -->
<!-- jade: http://jade-lang.com/ -->
<!-- haml: http://haml.info/tutorial.html -->
<!-- hbs(handlebars): http://handlebarsjs.com/expressions.html -->

<!DOCTYPE html>
<html>
  <head>
    <title>MongoDB & Mongoose | freeCodeCamp.org</title>
    <link
      rel="shortcut icon"
      href="https://cdn.freecodecamp.org/universal/favicons/favicon-32x32.png"
      type="image/x-icon"
    />
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      body {
        background-color: #ddd;
        color: #333;
        font-family: sans-serif;
        text-align: center;
      }
    </style>
  </head>

  <body>
    <h1>MongoDB & Mongoose</h1>
  </body>
</html>

 

Boilerplate NodeJS Express File Metadata

index.js

var express = require('express');
var cors = require('cors');
require('dotenv').config();

var app = express();

//Import multer for getting file upload
const multer  = require('multer')
const upload = multer({ dest: 'uploads/' })

app.use(cors());
app.use('/public', express.static(process.cwd() + '/public'));

app.get('/', function (req, res) {
    res.sendFile(process.cwd() + '/views/index.html');
});

app.post('/api/fileanalyse', upload.single('upfile'), function (req, res, next) {
  res.json({name: req.file.originalname, type: req.file.mimetype, size: req.file.size});
});

const port = process.env.PORT || 3000;
app.listen(port, function () {
  console.log('Your app is listening on port: ' + port)
});

readme.md

File Metadata Microservice

This is the boilerplate for the File Metadata Microservice project. Instructions for building your project can be found at https://www.freecodecamp.org/learn/apis-and-microservices/apis-and-microservices-projects/file-metadata-microservice

sample.env

PORT=3000

package.json

{
  "name": "file_metadata",
  "version": "0.0.1",
  "description": "API project for freeCodeCamp",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^8.2.0",
    "express": "^4.16.4",
    "multer": "^1.4.5-lts.1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/freeCodeCamp/boilerplate-project-filemetadata"
  },
  "keywords": [
    "node",
    "express"
  ],
  "license": "MIT"
}
 

views/index.html

<!DOCTYPE html>

<html>
   <head>
      <title>File Metadata</title>
      <link rel="shortcut icon" href="https://cdn.freecodecamp.org/universal/favicons/favicon-32x32.png" type="image/x-icon"/>
      <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" type="text/css">
      <link href="/public/style.css" rel="stylesheet" type="text/css">
   </head>

   <body>
      <div class="container">
        <h2>API Project: File Metadata Microservice</h2>

        <h3>Usage:</h3>
        <p>
          Please Upload a File ...
        </p>
        <div class="view">
          <h4 id="output"></h4>
          <form enctype="multipart/form-data" method="POST" action="/api/fileanalyse">
            <input id="inputfield" type="file" name="upfile">
            <input id="button" type="submit" value="Upload">
          </form>
        </div>
      </div>
      <div class="footer">
        <p>
          by
          <a href="http://www.freecodecamp.com">freeCodeCamp</a>
        </p>
      </div>
   </body>
</html>

public/style.css

/****** Main Styling ******/

body {
  font-family: 'Roboto', sans-serif;
  font-size: 16px;
  color: #222;
  background-color: #ECF0F1;
  text-align: center;
}

.container {
  padding: 0;
  margin-top: 40px;
}

.footer {
  margin-top: 60px;
}

a {
  color: #2574A9;
}

input {
  display: block;
  position: relative;
  margin: 10px auto;
}

input#button {
  width: 230px;
}

.view {
  position:relative;
  margin: auto;
  margin-top: 40px;
  border: 1px  solid  #aaa;
  padding: 20px;
  width: 60%;
  min-width: 400px;
}
 

uploads/text.txt

This is a test.


Boilerplate NPM

 server.js

 /******************************************************
 * PLEASE DO NOT EDIT THIS FILE
 * the verification process may break
 * ***************************************************/

'use strict';

var fs = require('fs');
var express = require('express');
var app = express();

if (!process.env.DISABLE_XORIGIN) {
  app.use(function(req, res, next) {
    var allowedOrigins = ['https://narrow-plane.gomix.me', 'https://www.freecodecamp.com'];
    var origin = req.headers.origin || '*';
    if(!process.env.XORIG_RESTRICT || allowedOrigins.indexOf(origin) > -1){
         console.log(origin);
         res.setHeader('Access-Control-Allow-Origin', origin);
         res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    }
    next();
  });
}

app.use('/public', express.static(process.cwd() + '/public'));

app.route('/_api/package.json')
  .get(function(req, res, next) {
    console.log('requested');
    fs.readFile(__dirname + '/package.json', function(err, data) {
      if(err) return next(err);
      res.type('txt').send(data.toString());
    });
  });
 
app.route('/')
    .get(function(req, res) {
          res.sendFile(process.cwd() + '/views/index.html');
    })

// Respond not found to all the wrong routes
app.use(function(req, res, next){
  res.status(404);
  res.type('txt').send('Not found');
});

// Error Middleware
app.use(function(err, req, res, next) {
  if(err) {
    res.status(err.status || 500)
      .type('txt')
      .send(err.message || 'SERVER ERROR');
  }  
})

//Listen on port set in environment variable or default to 3000
const listener = app.listen(process.env.PORT || 3000, function () {
  console.log("Node.js listening on port " + listener.address().port);
});

package.json

{"author": "TCL",
    "description": "A boilerplate project",
    "keywords": [ "boilerplate", "nodejs", "test", "freecodecamp"],
    "license": "MIT",
    "version": "0.0.1",
    "name": "fcc-learn-npm-package-json",
    "dependencies": {"express": "^4.14.0"},
    "main": "server.js",
    "scripts": {
        "start": "node server.js"
    },
    "repository": {
        "type": "git",
        "url": "https://idontknow/todo.git"
    }
}

readme.md

# Backend Challenges boilerplate - package.json
[![Run on Repl.it](https://repl.it/badge/github/freeCodeCamp/boilerplate-npm)](https://repl.it/github/freeCodeCamp/boilerplate-npm)

views/index.html

<!DOCTYPE html>
<html>

   <head>
      <title>Backend Challenges | Free Code Camp</title>
      <link rel="shortcut icon" href="https://cdn.hyperdev.com/us-east-1%3A52a203ff-088b-420f-81be-45bf559d01b1%2Ffavicon.ico" type="image/x-icon"/>
      <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" type="text/css">
      <link href="/public/style.css" rel="stylesheet" type="text/css">
   </head>

   <body>
      <div class="container">
         <h1>Manage Node.js projects and npm packages using package.json</h1>
         <h3>Part 1 of Free Code Camp Backend Challenges</h3>
      </div>
   </body>

</html>

public/style.css

/****** Main Styling ******/

body {
    font-family: 'Roboto', sans-serif;
    font-size: 16px;
    color: #222;
    background-color: #ECF0F1;
    text-align: center;
}

.container {
    padding: 0;
    margin-top: 40px;
}

.footer {
    margin-top: 60px;
}

ol {
    list-style-position: inside;
}
ul {
    list-style-type: none;
}

a {
    color: #2574A9;
}
/****** Logo Div Styling ******/

img {
    margin: 20px auto 0 auto;
    display: block;
}


Monday, September 2, 2024

Boilerplate NPM NodeJS Express

MyApp.js

require('dotenv').config()
let bodyParser = require('body-parser')
let express = require('express');
let app = express();
let viewpath = __dirname + '/views/'
let indexpath = viewpath + 'index.html'
let publicpath = __dirname + '/public'
app.use('/public', express.static(publicpath));
app.use(logger);
app.use(bodyParser.urlencoded({extended: false}));

function logger (req, res, next) {
  var string = req.method + " " + req.path + " - " + req.ip;
    console.log(string);
  next();
}

function stringresponse (req, res) {
  res.send('Hello Express');
}

function jsonresponse (req, res) {
  let data = {"message": "Hello json"};
    // console.log(process.env.MESSAGE_STYLE == "uppercase")
    if (process.env.MESSAGE_STYLE == "uppercase") {
        data['message'] = data['message'].toUpperCase()
    }
    res.json(data);
}

function fileresponse (req, res) {
  // console.log(req.method, req.path, req.ip);
    res.sendFile(indexpath);
}

app.get('/', fileresponse)
app.get('/json', jsonresponse)
app.get('/now', function(req, res, next) {
  let timenow = new Date().toString()
    req.time = timenow;  // Hypothetical synchronous operation
  next();
}, function(req, res) {
  res.json({'time': req.time});
});
app.get('/:word/echo', function(req, res, next) {
    req.echo = req.params.word;  // Hypothetical synchronous operation
  next();
}, function(req, res) {
  res.json({'echo': req.echo});
});

app.route('/name').get(getnamehandler).post(postnamehandler)
function getnamehandler(req, res) {
    let reqquery = req.query;
    let first = reqquery['first']
    let last = reqquery['last']
    let fullname = first + ' ' + last
    console.log('get req.query', reqquery);
  res.json({'name': fullname});
};

function postnamehandler(req, res) {
    let reqbody = req.body;
    let first = reqbody['first']
    let last = reqbody['last']
    let fullname = first + ' ' + last
    console.log('post req.body', fullname);
  res.json({'name': fullname});
};

 module.exports = app;

server.js

/******************************************************
 * PLEASE DO NOT EDIT THIS FILE
 * the verification process may break
 * ***************************************************/
 
const bGround = require('fcc-express-bground');
const myApp = require('./myApp');
const express = require('express');
const app = express();

if (!process.env.DISABLE_XORIGIN) {
  app.use((req, res, next) => {
    const allowedOrigins = ['https://narrow-plane.gomix.me', 'https://www.freecodecamp.com'];
    const origin = req.headers.origin || '*';
    if(!process.env.XORIG_RESTRICT || allowedOrigins.indexOf(origin) > -1){
         console.log(origin);
         res.setHeader('Access-Control-Allow-Origin', origin);
         res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    }
    next();
  });
}

const port = process.env.PORT || 3000;
bGround.setupBackgroundApp(app, myApp, __dirname).listen(port, () => {
  bGround.log(`Node is listening on port ${port}...`);
});

/******************************************************
 * PLEASE DO NOT EDIT THIS FILE
 * the verification process may break
 * ***************************************************/
 views/index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>Hello HTML</title>
        <link rel="stylesheet" href="/public/style.css">
    </head>
    <body>
        <h1>Hello, HTML.</h1>
        <p>Do not use until Challenge #12</p>
     <form action="/name" method="post">
      <label>First Name :</label>
      <input type="text" name="first" value="John"><br>
      <label>Last Name :</label>
      <input type="text" name="last" value="Doe"><br><br>
      <input type="submit" value="Submit">
    </form>
    </body>

</html>

public/style.css

/* the 'body' selector is required for the tests to pass */
/* You can change the style attributes if you want */
body {
  background-color: #222;
  color: #ddd;
  text-align: center;
  font-family: sans-serif;
}

h1 {
  font-size: 4em;
}

input[type=text], label {
  margin-bottom: 8px;
}

package.json

{
  "name": "fcc-learn-node-with-express",
  "version": "0.1.0",
  "dependencies": {
    "body-parser": "^1.15.2",
    "cookie-parser": "^1.4.3",
    "dotenv": "^16.0.1",
    "express": "^4.14.0",
    "fcc-express-bground": "https://github.com/freeCodeCamp/fcc-express-bground-pkg.git"
  },
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  }
}

readme.json

Basic Node and Express

This is the boilerplate code for the Basic Node and Express Challenges. Instructions for working on these challenges start at https://www.freecodecamp.org/learn/apis-and-microservices/basic-node-and-express/

Boilerplate Project Header Parser NodeJS Express

// index.js
// where your node app starts

// init project
require('dotenv').config();
var express = require('express');
var app = express();

// enable CORS (https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
// so that your API is remotely testable by FCC
var cors = require('cors');
app.use(cors({ optionsSuccessStatus: 200 })); // some legacy browsers choke on 204

// http://expressjs.com/en/starter/static-files.html
app.use(express.static('public'));

// http://expressjs.com/en/starter/basic-routing.html
app.get('/', function (req, res) {
  res.sendFile(__dirname + '/views/index.html');
});

// your first API endpoint...
app.get('/api/whoami', function (req, res) {
    res.json({
            ipaddress: req.get('x-forwarded-for'),
            language: req.get('accept-language'),
    software: req.get('user-agent')
    });
});

app.get('/api/test', function (req, res) {
    res.json({
        userAgent: req.get('user-agent')
    });
});


// listen for requests :)
var listener = app.listen(process.env.PORT || 3000, function () {
  console.log('Your app is listening on port ' + listener.address().port);
});

views/index.html

<!DOCTYPE html>

<html>

   <head>
      <title>Request Header Parser</title>
      <link rel="shortcut icon" href="https://cdn.hyperdev.com/us-east-1%3A52a203ff-088b-420f-81be-45bf559d01b1%2Ffavicon.ico" type="image/x-icon"/>
      <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" type="text/css">
      <link href="style.css" rel="stylesheet" type="text/css">
   </head>

   <body>
      <div class="container">
        <h2>Request Header Parser Microservice</h2>

        <h3>Example Usage:</h3>
        <p>
          <a href="api/whoami">[base url]/api/whoami</a>
        </p>

        <h3>Example Output:</h3>
        <p>
          <code>{"ipaddress":"::ffff:159.20.14.100","language":"en-US,en;q=0.5",<br>"software":"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0"}</code>
        </p>
      </div>
      <div class="footer">
        <p>
          by <a href="https://www.freecodecamp.org">freeCodeCamp</a>
        </p>
      </div>
   </body>


</html>

public/style.css

body {
    font-family: 'Roboto', sans-serif;
    font-size: 16px;
    color: #222;
    background-color: #FaFaFa;
    text-align: center;
    line-height: 1.4em;
}

.container {
    padding: 0;
    margin-top: 40px;
}

h3 {
  margin-top: 30px;
}

.footer {
    margin-top: 40px;
}

code {
  font-family: monospace;
  padding: 2px;
  color: black;
  background-color: #fff;
}

a {
    color: #2574A9;
}

readme.md

# Request Header Parser Microservice

This is the boilerplate for the Request Header Parser Microservice project. Instructions for building your project can be found at https://www.freecodecamp.org/learn/apis-and-microservices/apis-and-microservices-projects/request-header-parser-microservice

You should provide your own project, not the example URL.
Waiting: A request to /api/whoami should return a JSON object with your IP address in the ipaddress key.
Waiting: A request to /api/whoami should return a JSON object with your preferred language in the language key.
Waiting: A request to /api/whoami should return a JSON object with your software in the software key.


API Project: Request Header Parser Microservice
Example Usage:
[base url]/api/whoami
Example Output:

{"ipaddress":"159.20.14.100","language":"en-US,en;q=0.5",
"software":"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0"}

Sunday, September 1, 2024

Boilerplate Project NodeJs Express Timestamp

// index.js
// where your node app starts

// init project
var express = require('express');
var app = express();

// enable CORS (https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
// so that your API is remotely testable by FCC
var cors = require('cors');
app.use(cors({optionsSuccessStatus: 200}));  // some legacy browsers choke on 204

// http://expressjs.com/en/starter/static-files.html
app.use(express.static('public'));

// http://expressjs.com/en/starter/basic-routing.html
app.get("/", function (req, res) {
  res.sendFile(__dirname + '/views/index.html');
});


app.get("/api/:time?", function (req, res) {
  let date;
  if (req.params.time) {
    // Check if it is a Unix timestamp
    if (!isNaN(req.params.time)) {
      date = new Date(parseInt(req.params.time));
    } else {
      date = new Date(req.params.time);
    }
  } else {
    // If no parameter, use the current date and time
    date = new Date();
  }

  if (!isNaN(date.getTime())) {
    // If date is valid, return the Unix timestamp and UTC string
    res.json({unix: date.getTime(), utc: date.toUTCString()});
  } else {
    // If date is not valid, return an error message
    res.json({error: "Invalid Date"});
  }
});

// listen for requests :)
var listener = app.listen(process.env.PORT, function () {
  console.log('Your app is listening on port ' + listener.address().port);
});

views/index.html

<!DOCTYPE html>

<html>
   <head>
      <title>Timestamp Microservice | freeCodeCamp.org</title>
      <link rel="shortcut icon" href="https://cdn.freecodecamp.org/universal/favicons/favicon-32x32.png" type="image/x-icon"/>
      <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" type="text/css">
      <link href="style.css" rel="stylesheet" type="text/css">
   </head>

   <body>
    <h1>Timestamp Microservice</h1>
    <hr />
    <div class="container">
      <h3>Example Usage:</h3>
      <ul>
        <li><a href="api/2015-12-25">[project url]/api/2015-12-25</a></li>
        <li><a href="api/1451001600000">[project url]/api/1451001600000</a></li>
      </ul>

      <h3>Example Output:</h3>
      <p>
        <code>{"unix":1451001600000, "utc":"Fri, 25 Dec 2015 00:00:00 GMT"}</code>
      </p>
    </div>
    <div class="footer">
      <p>
        By <a href="https://www.freecodecamp.org/">freeCodeCamp.org</a>
      </p>
    </div>
  </body>
</html>

public/style.css

/****** Main Styling ******/

body {
    font-family: 'Roboto', sans-serif;
    font-size: 16px;
    color: #222;
    background-color: #FaFaFa;
    text-align: center;
    line-height: 1.4em;
}a

.container {
    padding: 0;
    margin-top: 40px;
}

h3 {
  margin-top: 30px;
}

hr {
    margin: 25px;
}

.footer {
    margin-top: 40px;
}

code {
  font-family: monospace;
  padding: 2px;
  color: black;
  background-color: #fff;
}

ul {
    list-style-type: none;
}

li {
  margin-bottom: 0.5em;
}

a {
    color: #2574A9;
}

package.json

{
  "name": "fcc-api-projects-boilerplate",
  "version": "0.0.1",
  "description": "An FCC Backend Challenge",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.12.4",
    "cors": "^2.8.0"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/freeCodeCamp/boilerplate-project-timestamp.git"
  },
  "bugs": {
    "url": "https://github.com/freeCodeCamp/freeCodeCamp/issues"
  },
  "homepage": "https://github.com/freeCodeCamp/boilerplate-project-timestamp#readme",
  "author": "freeCodeCamp <team@freecodecamp.org>",
  "keywords": [
    "node",
    "express",
    "freeCodeCamp"
  ],
  "license": "MIT"
}

readme.md

Timestamp Microservice

This is the boilerplate code for the Timestamp Microservice project. Instructions for building your project can be found at https://www.freecodecamp.org/learn/apis-and-microservices/apis-and-microservices-projects/timestamp-microservice

Timestamp Microservice Example Usage:

[project url]/api/2015-12-25
[project url]/api/1451001600000

Example Output:

{"unix":1451001600000, "utc":"Fri, 25 Dec 2015 00:00:00 GMT"} app.get("/api/:time", function (req, res) { console.log(req.params.time); let unixtime; if (Date.parse(req.params.time) > 0) { unixtime = new Date(req.params.time).getTime() / 1000; } else { unixtime = req.params.time; } const utctime = new Date(req.params.time).toUTCString(); console.log(utctime); res.json({unix: unixtime, utc: utctime}); });

 

Tuesday, November 14, 2023

Server Send Events Stream SSE + Javascript

from quart import abort, make_response, request, Quart

app = Quart(__name__)
@app.get("/sse")
async def sse():
    if "text/event-stream" not in request.accept_mimetypes:
        abort(400)
    response = await make_response(
        send_events(),
        {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            'Transfer-Encoding': 'chunked',
        },
    )
    response.timeout = None  # Disable the timeout for this streaming response
    return response
 
<script>
        document.addEventListener('DOMContentLoaded', (event) => {
            const eventSource = new EventSource('/sse');
            eventSource.onmessage = function(event) {
                console.log('New message:', event.data);
                // You can update the DOM here
            };
            eventSource.onopen = function(event) {
                console.log('Connection to server opened.');
            };
            eventSource.onerror = function(event) {
                console.error('EventSource failed.');
            };
            // To close the connection when the window is closed
            window.onbeforeunload = () => {
                eventSource.close();
            };
        });
    </script>
 
With parameters:
from quart import Quart, request, abort, make_response
from dataclasses import dataclass

app = Quart(__name__)

@dataclass
class ServerSentEvent:
data: str
event: str | None = None
id: int | None = None
retry: int | None = None

def encode(self) -> bytes:
message = f"data: {self.data}"
if self.event is not None:
message = f"{message}\nevent: {self.event}"
if self.id is not None:
message = f"{message}\nid: {self.id}"
if self.retry is not None:
message = f"{message}\nretry: {self.retry}"
message = f"{message}\n\n"
return message.encode('utf-8')

@app.get("/chat-updates")
async def chat_updates():
if "text/event-stream" not in request.accept_mimetypes:
abort(400)

friends = request.args.get('friends', default='false') == 'true'
format = request.args.get('format', default='simple')

async def send_events():
while True:
# Your logic to get updates, possibly filtering based on the query parameters
data = ... # Replace with your data retrieval logic
event = ServerSentEvent(data)
yield event.encode()

response = await make_response(
send_events(),
{
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Transfer-Encoding': 'chunked',
},
)
response.timeout = None
return response

# Run the app
if __name__ == "__main__":
app.run()
Javascript:
<script>
const params = new URLSearchParams({
friends: 'true',
format: 'detailed'
});

const url = `https://my-server/chat-updates?${params}`;
const eventSource = new EventSource(url);

eventSource.onmessage = function(event) {
// Handle incoming messages
console.log(event.data);
};

eventSource.onerror = function(error) {
// Handle any errors that occur
console.error("EventSource failed:", error);
}; 
</script>
 
// server.js
const http = require('http');
const es = require('event-stream');
// Create a HTTP server
const server = http.createServer((req, res) => {
// Check if the request path is /stream
if (req.url === '/stream') {
// Set the response headers
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// Create a counter variable
let counter = 0;
// Create an interval function that sends an event every second
const interval = setInterval(() => {
// Increment the counter
counter++;
// Create an event object with name, data, and id properties
const event = {
name: 'message',
data: `Hello, this is message number ${counter}`,
id: counter
};
// Convert the event object to a string
const eventString = `event: ${event.name}\ndata: ${event.data}\nid: ${event.id}\n\n`;
// Write the event string to the response stream
res.write(eventString);
// End the response stream after 10 events
if (counter === 10) {
clearInterval(interval);
res.end();
}
}, 1000);
} else {
// Handle other requests
res.writeHead(404);
res.end('Not found');
}
});
// Listen on port 3000
server.listen(3000, () => {
console.log('Server listening on port 3000');
});
 
// client.js
// Fetch the event stream from the server
fetch('/stream')
.then(response => {
// Get the readable stream from the response body
const stream = response.body;
// Get the reader from the stream
const reader = stream.getReader();
// Define a function to read each chunk
const readChunk = () => {
// Read a chunk from the reader
reader.read()
.then(({
value,
done
}
) =>
{
// Check if the stream is done
if (done) {
// Log a message
console.log('Stream finished');
// Return from the function
return;
}
// Convert the chunk value to a string
const chunkString = new TextDecoder().decode(value);
// Log the chunk string
console.log(chunkString);
// Read the next chunk
readChunk();
})
.catch(error => {
// Log the error
console.error(error);
});
};
// Start reading the first chunk
readChunk();
})
.catch(error => {
// Log the error
console.error(error);
});
  
 
from flask import Flask, Response, render_template
import itertools
import time

app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html')

@app.route('/connect')
def publish_hello():
def stream():
for idx in itertools.count():
msg = f"data: <p>This is {idx}.</p>\n\n"
yield msg
time.sleep(1)
return Response(stream(), mimetype="text/event-stream")

Htmx

<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/htmx.org@1.8.6"></script>
<script src="https://unpkg.com/htmx.org/dist/ext/sse.js"></script>
</head>
<body>
<div hx-ext="sse" sse-connect="/connect" sse-swap="message">
Contents of this box will be updated in real time with every SSE message received from the server.
</div>
</body>
</html>
 
In this example, the Flask app sends a message to the HTMX client every second. The message is wrapped in an HTML paragraph tag and sent as an SSE event. The HTMX client listens to the SSE endpoint and updates the contents of the HTML element with the sse-swap attribute with the message received from the server.
Streaming data from Flask to HTMX using Server-Side Events (SSE) | mathspp 
 // Client-side Javascript in the HTML 
var targetContainer = document.getElementById("this-div");
var eventSource = new EventSource("/stream");
eventSource.onmessage = function(e) {
targetContainer.innerHTML = e.data;
};

Generator + SSE: So why are Python generators good with SSE? It’s simply because they can keeping looping and yielding data and handing it to the client very seamlessly. Here is a simple Python implementation of SSE in Flask:

@route("/stream")
def stream():
def eventStream():
while True:
# Poll data from the database
# and see if there's a new message
if len(messages) > len(previous_messages):
yield "data:
{}\n\n".format(messages[len(messages)-1)])"

return Response(eventStream(), mimetype="text/event-stream")

This is a simple hypothetical event source that checks if there’s a new inbox message and yield the new message. For the browser to acknowledge a server-sent message, you’ll have to comply to this format:

"data: <any_data>\n\n"

You have the option to also send with the data the event and id.

"id: <any_id>\nevent: <any_message>\ndata: <any_data>\n\n"

Note that the fields do not have to be in any order as long as there is a newline (\n) for each field and two (\n\n) at the end of them. With additional event field, you can have more control how you push data to the browser.

// Client-side Javascript in the HTMLvar targetContainer = document.getElementById("this-div");
var eventSource = new EventSource("/stream");
eventSource.addEventListener = (<any_message>, function(e) {
targetContainer.innerHTML = e.data;

if (e.data > 20) {
targetContainer.style.color = "red";
}
};

This will basically render the DOM with the latest data on the specified event message and change the color to “red” when it exceeds 20.

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#receiving_events_from_the_server

Warning: When not used over HTTP/2, SSE suffers from a limitation to the maximum number of open connections, which can be especially painful when opening multiple tabs, as the limit is per browser and is set to a very low number (6).
The issue has been marked as "Won't fix" in Chrome and Firefox.
This limit is per browser + domain, which means that you can open 6 SSE connections
across all of the tabs to www.example1.com and another 6 SSE connections to www.example2.com (per Stackoverflow). When using HTTP/2, the maximum number of simultaneous HTTP streams is negotiated between the server and the client (defaults to 100). 

Fields
Each message received has some combination of the following fields, one per line:

event
    A string identifying the type of event described. If this is specified, an event will be dispatched on the browser to the listener for the specified event name; the website source code should use addEventListener() to listen for named events. The onmessage handler is called if no event name is specified for a message.

data
    The data field for the message. When the EventSource receives multiple consecutive lines that begin with data:, it concatenates them, inserting a newline character between each one. Trailing newlines are removed.

id
    The event ID to set the EventSource object's last event ID value.

retry
    The reconnection time. If the connection to the server is lost, the browser will wait for the specified time before attempting to reconnect. This must be an integer, specifying the reconnection time in milliseconds. If a non-integer value is specified, the field is ignored.

All other field names are ignored.