Home > Mobile >  Differents routes on an HTTP handler with Deno
Differents routes on an HTTP handler with Deno

Time:07-30

Im trying to do a REST API with the deno http lib, the problem arrive when I "declare" routes comparing the req.url property Here is the TypeScript code:

async function handler(req: Request): Promise<Response> {
    if (req.url === "/") {
        return new Response(`Ruta visitada ${req.url}`)
    }
    if (req.url === "/about") {
        return new Response(`Ruta visitada ${req.url}`)
    }
}

(Getting the function type Promise<Response> underlined as a error)

But I get the next error:

Function lacks ending return statement and return type does not include 'undefined'.

So how can separe the routes if the function must return directly a response?

CodePudding user response:

I'll address one part of your question at a time, starting with the types used:


Types

Because you mentioned "the deno http lib" (presumably you mean the collection of modules at https://deno.land/std/http), the request handler function you've shown is being passed as the first argument to the serve function export from https://deno.land/[email protected]/http/server.ts. Its type signature looks like this:

async function serve(handler: Handler, options?: ServeInit): Promise<void>;

The type signature of that Handler function looks like this:

type Handler = (request: Request, connInfo: ConnInfo) => Response | Promise<Response>;

Here's a potentially helpful tip: the Handler type is exported from the server.ts module, so you can just import it and use it as a type annotation for a function expression, avoiding the need to write out the whole function signature type again in a declaration:

import { type Handler } from "https://deno.land/[email protected]/http/server.ts";

const handler: Handler = async (req) => {
  // ...
};

The Request type is an instance of the Request class from the fetch standard.

Now, with that covered, I'll address the different concerns of your question:


Understanding the Request URL

the req.url property

The code you've shown implies that you think the value at this property is the URL string without the origin portion (or, in other words, a concatenation of the pathname and query string).

I don't want to make any assumptions, but perhaps you are used to the syntax req.url often seen in Node.js code, which refers to http.IncomingMessage#url when used in the request listener argument passed to http.createServer. Example:

http.createServer((req, res) => {
  req.url; // <--- `req` is actually "IncomingMessage"
  // ...
});

In Deno: because the req parameter is a class instance described above in the types section of this answer, req.url is the full URL string (including the origin). To get a URL object that you can use to branch your program logic (this is called routing), you can use the URL constructor like this:

const url = new URL(req.url);

Then, use the properties of that object to make decisions. For example, if you want to send one kind of response when the pathname is "/about", you can use this:

const url = new URL(req.url);

if (url.pathname === "/about") {
  // Send a response here...
}

This leads to your error and main question(s):


Basic routing logic based on URL pathname

(Getting the function type Promise<Response> underlined as a error)

But I get the next error:

Function lacks ending return statement and return type does not include 'undefined'.

So how can separe the routes if the function must return directly a response?

This is at the core of every web framework, and — just like there are many web frameworks — there are many approaches with varying levels of complexity. I'll show a simple approach based on the code sample in your question:

I'll create an example TypeScript module, and I'll name it based on the ID of this Stack Overflow question: so-73171658.ts. Here is the content of the module:

import {
  type Handler,
  serve,
} from "https://deno.land/[email protected]/http/server.ts";

const handler: Handler = (req) => {
  const url = new URL(req.url);

  if (url.pathname === "/") {
    return new Response(`Ruta visitada ${req.url}`);
  }
  if (url.pathname === "/about") {
    return new Response(`Ruta visitada ${req.url}`);
  }
  return new Response("Not found", { status: 404 });
};

await serve(handler);

Notice that, after the two conditional statements, I added a final return statement:

return new Response("Not found", { status: 404 });

This is what is returned in the case that a request doesn't match either of the pathnames in the conditional statements.

This change alone will fix the error you received.

Let's test it by running the server:

% deno run --allow-net so-73171658.ts
Listening on http://localhost:8000/

Note that, by default, the serve function listens on the hostname "0.0.0.0" and the port 8000

Now that the server is running, let's send a few test requests and read the response body as text. In a separate terminal shell (you can just open a new tab/window in your terminal):

First, let's send a request to the root path "/":

% deno eval --print "await (await fetch('http://localhost:8000/')).text()"
Ruta visitada http://localhost:8000/

As expected, we get the value of request.url. Next, let's send a request to the path "/about":

% deno eval --print "await (await fetch('http://localhost:8000/about')).text()"
Ruta visitada http://localhost:8000/about

Again (as expected) we get the value of request.url. Now, let's send a request to the path "/another-path":

% deno eval --print "await (await fetch('http://localhost:8000/another-path')).text()"
Not found

This time we get "Not found". This is because there's no conditional statement for the "/another-path" pathname, so the final return statement is used. The same will happen for all other paths which aren't explicitly handled.

Let's switch back to the terminal shell (tab/window) where the server is running and stop it using ctrl c.


Conclusion

There's a lot more involved in routing that I didn't cover in this answer, and is out of scope of the question that you asked. Even just in regard to forming correct responses:

You'll need to consult the many sources of available documentation on the internet in order to ensure that your API follows the established standards. (MDN is a great one — I've cited it many times in this answer.)

But you don't have to re-implement everything yourself: there are existing Deno web frameworks to help you. Here are a couple:

  • Oak - "A middleware framework for Deno's native HTTP server, Deno Deploy and Node.js 16.5 and later. It also includes a middleware router."
  • Sift - "Sift is a routing and utility library for Deno and Deno Deploy."

I hope this answer helps you understand some of the basics!

  • Related