diff --git a/public/functions/api/packages_url_resources.json.js b/public/functions/api/packages_url_resources.json.js
new file mode 100644
index 000000000..e466a9005
--- /dev/null
+++ b/public/functions/api/packages_url_resources.json.js
@@ -0,0 +1,105 @@
+let urlResources = [
+  {
+    "name": "ReScript Test Framework",
+    "description": "The most minimalistic testing library you will find for testing ReScript code",
+    "keywords": ["testing", "minimal", "experimental"],
+    "urlHref": "https://github.com/rescript-lang/rescript-project-template/blob/test/tests/Tests.res",
+    "official": true
+  },
+  {
+    "name": "genType",
+    "description": "Better interop with JS & TS in ReScript",
+    "keywords": ["rescript", "typescript"],
+    "urlHref": "https://github.com/reason-association/genType",
+    "official": true
+  }
+]
+
+export async function onRequestGET(context) {
+  const packages = await fetchNpmPackages()
+  return Response.json({
+    ...packages,
+    urlResources,
+  })
+}
+
+async function fetchNpmPackages() {
+  let baseUrl = "https://registry.npmjs.org/-/v1/search?text=keywords:rescript&size=250&maintenance=1.0&popularity=0.5&quality=0.9"
+
+  let [data1, data2, data3] = await Promise.all3([
+    fetch(baseUrl)
+      .then(res => res.json())
+      .then(data => parsePkgs(data)),
+    fetch(baseUrl + "&from=250")
+      .then(res => res.json())
+      .then(data => parsePkgs(data)),
+    fetch(baseUrl + "&from=500")
+      .then(res => res.json())
+      .then(data => parsePkgs(data)),
+  ])
+
+  let unmaintained = []
+
+  function shouldAllow(pkg) {
+    // These are packages that we do not want to filter out when loading searching from NPM.
+    let packageAllowList = []
+
+    if (packageAllowList.includes(pkg)) {
+      return true
+    }
+
+    if (pkg.name.includes("reason")) {
+      return false
+    }
+
+    if (pkg.maintenanceScore < 0.3) {
+      unmaintained.push(pkg)
+      return false
+    }
+
+    return true
+  }
+
+  let packages = []
+  for (let pkg of data1) if (shouldAllow(pkg)) packages.push(pkg)
+  for (let pkg of data2) if (shouldAllow(pkg)) packages.push(pkg)
+  for (let pkg of data3) if (shouldAllow(pkg)) packages.push(pkg)
+
+  return {
+    packages,
+    unmaintained,
+  }
+}
+
+function parsePkgs(data) {
+  return data["objects"].map(item => {
+    let pkg = item["package"]
+    return {
+      name: pkg["name"],
+      version: pkg["version"],
+      keywords: uniqueKeywords(filterKeywords(pkg["keywords"])),
+      description: pkg["description"] ?? "",
+      repositoryHref: pkg["links"]?.["repository"] ?? null,
+      npmHref: pkg["links"]["npm"],
+      searchScore: item["searchScore"],
+      maintenanceScore: item["score"]["detail"]["maintenance"],
+    }
+  })
+}
+
+function filterKeywords(keywords) {
+  return keywords.filter(kw => {
+    let k = kw.toLowerCase()
+    return !(
+      k === "reasonml" ||
+      k === "reason" ||
+      k === "ocaml" ||
+      k === "bucklescript" ||
+      k === "rescript"
+    )
+  })
+}
+
+function uniqueKeywords(keywords) {
+  return [...new Set(keywords)]
+}
diff --git a/public/functions/api/resources.json.js b/public/functions/api/resources.json.js
new file mode 100644
index 000000000..0942199f8
--- /dev/null
+++ b/public/functions/api/resources.json.js
@@ -0,0 +1,96 @@
+/** This is the list of community content we want to generate. */
+/** If you  have content you would like to add, please open up a PR adding the link to this list and then run `npm run generate-resources` */
+let urls = [
+  // 2025
+  "https://dev.to/dzakh/javascript-schema-library-from-the-future-5420",
+  "https://www.youtube.com/watch?v=yKl2fSdnw7w",
+  "https://github.com/rescript-lang/awesome-rescript", // regardless of age this seems like it should always be near the top
+  // 2024
+  "https://www.youtube.com/watch?v=MC-dbM-GEuw",
+  "https://dev.to/jderochervlk/rescript-has-come-a-long-way-maybe-its-time-to-switch-from-typescript-29he",
+  "https://www.youtube.com/watch?v=f0gDMjuaCZo",
+  "https://www.geldata.com/blog/rescript-and-edgedb",
+  "https://www.youtube.com/watch?v=37FY6a-zY20",
+  // 2023
+  "https://dev.to/cometkim/when-and-where-to-use-rescript-the-rescript-happy-path-47ni",
+  // 2022
+  "https://www.greyblake.com/blog/from-typescript-to-rescript/",
+  "https://dev.to/zth/getting-rid-of-your-dead-code-in-rescript-3mba",
+  "https://www.youtube.com/watch?v=KDL-kRgilkQ",
+  "https://dev.to/srikanthkyatham/rescript-react-error-boundary-usage-3b05",
+  // "https://www.daggala.com/belt_vs_js_array_in_rescript/" I think we should exclude this one since it's related to API we are deprecating
+  // 2021
+  "https://fullsteak.dev/posts/fullstack-rescript-architecture-overview",
+  "https://scalac.io/blog/rescript-for-react-development/",
+  "https://yangdanny97.github.io/blog/2021/07/09/Migrating-to-Rescript",
+  "https://alexfedoseev.com/blog/post/responsive-images-and-cumulative-layout-shift",
+  "https://dev.to/ryyppy/rescript-records-nextjs-undefined-and-getstaticprops-4890",
+]
+
+export async function onRequestGET(context) {
+  const resources = [];
+
+  for (let url of urls) {
+    let resource = await fetchUrlResource(url)
+    if (resource) {
+      resources.push(resource)
+    }
+  }
+
+  return Response.json(resources)
+}
+
+function makeCollector() {
+  let state = {
+    url: null,
+    title: null,
+    description: null,
+    image: null,
+  }
+  return {
+    get state() {
+      return { ...state };
+    },
+    element(element) {
+      let property = element.getAttribute('property')
+      let content = element.getAttribute('content')
+      let [_og, namespace, key] = property.split(':')
+      switch (namespace) {
+        case 'url':
+          state.url = content
+          return
+        case 'title':
+          state.title = content
+          return
+        case 'description':
+          state.description = content
+          return
+        case 'image': {
+          if (!key || key === 'url') {
+            state.image = content
+          }
+        }
+      }
+      // FIXME: should flush
+    }
+  }
+}
+
+async function fetchUrlResource(url) {
+  let response = await fetch(url)
+  if (!response.ok) {
+    return null
+  }
+
+  let collector = makeCollector()
+  let stream = new HTMLRewriter()
+    .on('meta[property^=og][content]', collector)
+    .transform(response)
+
+  for await (let _chunk of stream) {
+    // noop
+    // FIXME: no need to iterate full content, just the first few hundreds killobytes enough
+  }
+
+  return collector.state
+}
diff --git a/wrangler.jsonc b/wrangler.jsonc
new file mode 100644
index 000000000..2c294da8c
--- /dev/null
+++ b/wrangler.jsonc
@@ -0,0 +1,4 @@
+{
+  "name": "rescript-lang-org",
+  "pages_build_output_dir": "out"
+}