Creating routes from a file

I’m trying to migrate a website to Phoenix. I have a JSON file with routes definitions.

How can I route traffic base on my file definitions?

I fond this post but it only adds more questions.

What exactly does your file define? Could you share a piece of it so we can see how it looks like?

It doesn’t really matter because I can adjust it if necessary. It is more like how can I use a file?
Here is a snippet of my route JSON config:

[
  {
    "name": "VERTEX CACHE",
    "description": "A demo of bringing Houdini FEM simulations to the browser by storing vertex data in textures.",
    "img": "img/projects/vertex-cache.jpg",
    "entry": "projects/vertex-texture-cache/public/html/index.html",
    "public": [
      "projects/vertex-texture-cache/public"
    ],
    "route": "vertex-cache",
    "published": true
  },
  ...
]

Considering you have content, meta-data, route and status, it looks we are talking about a CMS here, isn’t it?

If that’s not the case, if your your site is static then you may read the file from a one-time script (elixir or not) that simply organizes the content files in a directory structure that can then be served statically, by Plug.Static or even an external webserver like nginx.

If you really want to create Phoenix routes dynamically, you could read the JSON content from your config.exs file and have is as a compilation-time list that you can iterate over from your routes.ex file to dynamically create the routes. Of course if you change the JSON then you would have to re-compile your application.

Last, you could have a dynamic website with a single route with a parameter in the URL path that you use to match against a keyword (for instance, “vertex-cache”). With the keyword, you would fetch data from a database or from an in-memory structure with the contents of your JSON to serve the correct content.

2 Likes

@rogerweb hank for you detailed reply!

No, it is not CMS it is all static content. I also use this JSON to generate my frontend page. This way I can keep all content in one file.

Static solution is interesting but in my case every route host a project that have it’s own set of separate static files. When user navigate to let say /project1 then public files from /project1/public directory should be served under /project1. Here is my current full NodeJS server that utilize Express Sub-apps:

import express from "express";
import path from "path";
import fs from "fs";

let projects = JSON.parse(fs.readFileSync("./constants/projects.json"));

const codercat = express();
const port = 8081;
const __dirname = path.resolve();

// Serve main site static files first
codercat.use(express.static("out", { extensions: ["html"] }));

projects.forEach((app) => {
  const subApp = express();
  const appRoute = "/" + app.route;

  // Skip projects that do not have sub apps
  if (!app.public) return;

  app.public.forEach((path) =>
    subApp.use(express.static(path, { extensions: ["html"] }))
  );

  subApp.get("/", (_, res) => {
    res.sendFile(path.join(__dirname, app.entry));
  });

  // Each app will be server on sub-route
  // with its own configuration and set of public assets
  console.log("[+] Hosting", app.name, "under", appRoute);
  codercat.use(appRoute, subApp);
});

// Start server
codercat.listen(port, () => {
  console.log(`Listening on port ${port}`);
});

I think, compile solution would work for me. How do I create a route this way? Should I use the suggestion from Plug router compiling routes from file or there is a better way?

Do you think my NodeJS approach is similar to your third dynamic route suggestion?

What’s the goal of moving this arrangement to Phoenix? I’m confident you could rig up a Plug equivalent of your Express setup, but why?

First as a learning exercise. Second to get rid of node process on my server. Third to have less maintenance overhead by having unified setup.

I’m pretty sure if you create your own plug it would be more elegant, but you can read your json file from within your endpoint.ex, iterate over the routes and add a Plug.Static for each.

I did a simple POC just to show you what can be done:

  for route <- ["aaa", "bbb", "ccc"] do
    plug Plug.Static,
      at: "/" <> route,   # the path as in the browser
      from: "/home/roger/code/temp/hello/" <> route,  # the path in the filesystem
      gzip: false
  end

Provided you have an index.html file in /home/roger/code/temp/hello/aaa/, you can access it from http://localhost:4000/aaa/index.html. The path in the filesystem doesn’t need to be directly related to the path in the URL, it was just an example. I would take a look at the documentation of Plug.Static to check all the options.

3 Likes

I didn’t know that you can add plugs in a for loop like this! Also following your suggestion I’m trying to see if I can make my custom plug that I can modify static path dynamically. Still struggling but thanks for this direction! Need some time to process this information.

1 Like