oak on Deno - How do I build a URL to a route?

685 Views Asked by At

I come from a land of ASP.NET Core. Having fun learning a completely new stack.

I'm used to being able to:

  1. name a route "orders"
  2. give it a path like /customer-orders/{id}
  3. register it
  4. use the routing system to build a URL for my named route

An example of (4) might be to pass a routeName and then routeValues which is an object like { id = 193, x = "y" } and the routing system can figure out the URL /customer-orders/193?x=y - notice how it just appends extraneous key-vals as params.

Can I do something like this in oak on Deno?? Thanks.

Update: I am looking into some functions on the underlying regexp tool the routing system uses. It doesn't seem right that this often used feature should be so hard/undiscoverable/inaccessible.

https://github.com/pillarjs/path-to-regexp#compile-reverse-path-to-regexp

2

There are 2 best solutions below

2
jsejcksn On

I'm not exactly sure what you mean by "building" a URL, but the URL associated to the incoming request is defined by the requesting client, and is available in each middleware callback function's context parameter at context.request.url as an instance of the URL class.

The documentation provides some examples of using a router and the middleware callback functions that are associated to routes in Oak.

Here's an example module which demonstrates accessing the URL-related data in a request:

so-74635313.ts:

import { Application, Router } from "https://deno.land/x/[email protected]/mod.ts";

const router = new Router({ prefix: "/customer-orders" });

router.get("/:id", async (ctx, next) => {
  // An instance of the URL class:
  const { url } = ctx.request;

  // An instance of the URLSearchParams class:
  const { searchParams } = url;

  // A string:
  const { id } = ctx.params;

  const serializableObject = {
    id,
    // Iterate all the [key, value] entries and collect into an array:
    searchParams: [...searchParams.entries()],
    // A string representation of the full request URL:
    url: url.href,
  };

  // Respond with the object as JSON data:
  ctx.response.body = serializableObject;
  ctx.response.type = "application/json";

  // Log the object to the console:
  console.log(serializableObject);

  await next();
});

const app = new Application();

app.use(router.routes());
app.use(router.allowedMethods());

function printStartupMessage({ hostname, port, secure }: {
  hostname: string;
  port: number;
  secure?: boolean;
}): void {
  if (!hostname || hostname === "0.0.0.0") hostname = "localhost";
  const address =
    new URL(`http${secure ? "s" : ""}://${hostname}:${port}/`).href;
  console.log(`Listening at ${address}`);
  console.log("Use ctrl+c to stop");
}

app.addEventListener("listen", printStartupMessage);

await app.listen({ port: 8000 });

In a terminal shell (I'll call it shell A), the program is started:

% deno run --allow-net so-74635313.ts
Listening at http://localhost:8000/
Use ctrl+c to stop

Then, in another shell (I'll call it shell B), a network request is sent to the server at the route described in your question — and the response body (JSON text) is printed below the command:

% curl 'http://localhost:8000/customer-orders/193?x=y'
{"id":"193","searchParams":[["x","y"]],"url":"http://localhost:8000/customer-orders/193?x=y"}

Back in shell A, the output of the console.log statement can be seen:

{
  id: "193",
  searchParams: [ [ "x", "y" ] ],
  url: "http://localhost:8000/customer-orders/193?x=y"
}

ctrl + c is used to send an interrupt signal (SIGINT) to the deno process and stop the server.

0
Luke Puplett On

I am fortunately working with a React developer today!

Between us, we've found the .url(routeName, ...) method on the Router instance and that does exactly what I need!

Here's the help for it:

  /** Generate a URL pathname for a named route, interpolating the optional
   * params provided.  Also accepts an optional set of options. */

Here's it in use in context:

export const routes = new Router()
  .get(
    "get-test",
    "/test",
    handleGetTest,
  );

function handleGetTest(context: Context) {
  console.log(`The URL for the test route is: ${routes.url("get-test")}`);
}

// The URL for the test route is: /test