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 theserver.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 tohttp.IncomingMessage#url
when used in the request listener argument passed tohttp.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 port8000
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:
- sending the correct headers (e.g. setting the correct
content-type
header) - setting a correct/meaningful status code
- etc.
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!