Bird
Raised Fist0
Denoframework~15 mins

Data fetching with handlers in Deno - Deep Dive

Choose your learning style10 modes available
Overview - Data fetching with handlers
What is it?
Data fetching with handlers in Deno means writing small functions that respond to web requests by getting data from somewhere, like a database or an API, and then sending that data back to the user. These handlers listen for specific requests and decide what data to send based on the request details. They help build web servers or APIs that deliver information dynamically.
Why it matters
Without data fetching handlers, web servers would not be able to respond with the right information when users ask for it. This would make websites static and unhelpful. Handlers let servers understand requests and fetch or compute the exact data needed, making web apps interactive and useful in real life.
Where it fits
Before learning data fetching handlers, you should understand basic JavaScript/TypeScript and how HTTP requests work. After mastering handlers, you can learn about middleware, routing frameworks, and advanced server-side rendering techniques.
Mental Model
Core Idea
A data fetching handler is like a smart waiter who listens to your order (request), goes to the kitchen (data source), gets your food (data), and brings it back to you (response).
Think of it like...
Imagine a restaurant where you tell the waiter what you want. The waiter understands your request, goes to the kitchen to get the exact dish, and serves it to you. The waiter is the handler, the kitchen is the data source, and your order is the request.
┌─────────────┐      ┌───────────────┐      ┌───────────────┐
│  Client     │─────▶│  Handler      │─────▶│  Data Source  │
│ (Browser)   │      │ (Waiter)      │      │ (Kitchen)     │
└─────────────┘      └───────────────┘      └───────────────┘
       ▲                                         │
       │                                         ▼
       └─────────────────────────────────────────┘
                 Response with Data
Build-Up - 7 Steps
1
FoundationUnderstanding HTTP Requests Basics
🤔
Concept: Learn what an HTTP request is and how a server receives it.
When you open a website or call an API, your browser sends an HTTP request to a server. This request includes a method (like GET or POST), a URL, and sometimes extra data. The server listens for these requests and decides how to respond.
Result
You know that a server waits for requests and that each request asks for some action or data.
Understanding requests is key because handlers react to these requests to fetch and send data.
2
FoundationCreating a Simple Handler Function
🤔
Concept: Write a basic function that receives a request and sends back a fixed response.
In Deno, a handler is a function that takes a Request object and returns a Response object. For example: async function handler(request) { return new Response('Hello from handler!'); } This function ignores the request details and always replies with the same message.
Result
When you run this handler on a server and visit it, you see 'Hello from handler!' in your browser.
Knowing how to write a handler function is the foundation for fetching and returning data dynamically.
3
IntermediateFetching Data Inside Handlers
🤔Before reading on: do you think handlers can fetch data from external APIs or databases directly? Commit to your answer.
Concept: Handlers can perform asynchronous operations like fetching data from APIs or databases before responding.
Inside a handler, you can use JavaScript's fetch() to get data from another API: async function handler(request) { const apiResponse = await fetch('https://api.example.com/data'); const data = await apiResponse.json(); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } }); } This handler fetches data from an external source and sends it back as JSON.
Result
The server responds with live data fetched from the external API, not just a fixed message.
Understanding that handlers can do asynchronous data fetching unlocks dynamic, real-time responses.
4
IntermediateUsing Request Details to Customize Fetching
🤔Before reading on: do you think handlers can read URL parameters to decide what data to fetch? Commit to your answer.
Concept: Handlers can inspect the request URL and parameters to fetch specific data based on user input.
You can parse the request URL to get query parameters: async function handler(request) { const url = new URL(request.url); const id = url.searchParams.get('id'); const apiResponse = await fetch(`https://api.example.com/data/${id}`); const data = await apiResponse.json(); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } }); } This handler fetches data for a specific id passed in the URL.
Result
Users get different data depending on the id they provide in the request URL.
Knowing how to read request details lets handlers serve personalized or filtered data.
5
IntermediateHandling Different HTTP Methods
🤔
Concept: Handlers can respond differently based on the HTTP method like GET or POST.
You can check the request method to decide what to do: async function handler(request) { if (request.method === 'GET') { // fetch and return data } else if (request.method === 'POST') { const body = await request.json(); // process and store data return new Response('Data saved'); } else { return new Response('Method not allowed', { status: 405 }); } } This lets the same handler support reading and writing data.
Result
The server behaves correctly for different request types, improving API flexibility.
Understanding HTTP methods helps build handlers that support full CRUD operations.
6
AdvancedError Handling and Response Status Codes
🤔Before reading on: do you think a handler should always return status 200 even if data fetching fails? Commit to your answer.
Concept: Handlers should handle errors gracefully and return appropriate HTTP status codes.
Wrap data fetching in try-catch blocks: async function handler(request) { try { const response = await fetch('https://api.example.com/data'); if (!response.ok) { return new Response('Failed to fetch data', { status: response.status }); } const data = await response.json(); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response('Server error', { status: 500 }); } } This ensures clients know if something went wrong.
Result
Clients receive meaningful error messages and status codes, improving user experience and debugging.
Knowing how to handle errors prevents silent failures and helps build reliable APIs.
7
ExpertOptimizing Handlers with Caching and Streaming
🤔Before reading on: do you think handlers always wait for all data before sending a response? Commit to your answer.
Concept: Advanced handlers can cache data to reduce load and stream responses for large data efficiently.
You can store fetched data in memory or external cache to reuse it: let cache = null; async function handler(request) { if (cache) { return new Response(JSON.stringify(cache), { headers: { 'Content-Type': 'application/json' } }); } const response = await fetch('https://api.example.com/data'); const data = await response.json(); cache = data; // cache for next requests return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } }); } For streaming, you can use ReadableStream to send data chunks as they arrive, improving performance for big data.
Result
Handlers respond faster on repeated requests and handle large data without blocking the server.
Understanding caching and streaming techniques is key for building scalable, high-performance data fetching handlers.
Under the Hood
When a request arrives, Deno's runtime passes it to the handler function. The handler runs asynchronously, allowing it to fetch data from external sources without blocking other requests. The handler constructs a Response object, which Deno sends back over the network. Internally, Deno uses event loops and promises to manage multiple concurrent handlers efficiently.
Why designed this way?
Deno was designed to be secure and modern, using async/await to handle I/O without blocking. Handlers follow this pattern to keep servers responsive. This design avoids older callback hell and makes code easier to read and maintain.
┌───────────────┐
│ Incoming HTTP │
│   Request     │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│   Deno Server │
│  Event Loop   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│  Handler Func │
│ (async/await) │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Data Fetching │
│ (fetch, DB)   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Response Sent │
│  to Client    │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do handlers block the server while fetching data? Commit to yes or no.
Common Belief:Handlers block the server until data fetching finishes, making the server slow.
Tap to reveal reality
Reality:Handlers in Deno use async/await and promises, so they do not block the server. Other requests can be handled concurrently.
Why it matters:Believing handlers block leads to unnecessary complexity or avoiding async code, reducing server efficiency.
Quick: Can a handler send multiple responses for one request? Commit to yes or no.
Common Belief:A handler can send multiple responses to the same request to update data progressively.
Tap to reveal reality
Reality:Each request gets exactly one response. To send multiple updates, streaming or websockets are needed, not multiple responses.
Why it matters:Misunderstanding this causes bugs where code tries to send multiple responses, leading to runtime errors.
Quick: Is it safe to store fetched data in global variables inside handlers? Commit to yes or no.
Common Belief:Storing data in global variables inside handlers is always safe and recommended for caching.
Tap to reveal reality
Reality:Global variables can cause stale data or race conditions in concurrent requests. Proper cache invalidation or external caching is needed.
Why it matters:Ignoring this leads to inconsistent data served to users and hard-to-debug bugs.
Quick: Do handlers automatically parse JSON request bodies? Commit to yes or no.
Common Belief:Handlers receive parsed JSON objects automatically from requests with JSON bodies.
Tap to reveal reality
Reality:Handlers receive raw Request objects; you must explicitly call request.json() to parse JSON bodies.
Why it matters:Assuming automatic parsing causes runtime errors when accessing request data.
Expert Zone
1
Handlers can be composed or wrapped to add features like logging, authentication, or rate limiting without changing core logic.
2
Using streaming responses can reduce memory usage and improve latency for large datasets, but requires careful backpressure handling.
3
Proper cache invalidation strategies are critical; naive caching can cause stale or incorrect data to be served.
When NOT to use
For extremely high-performance needs, consider native modules or edge functions that run closer to users. Also, for complex workflows, use dedicated backend frameworks or microservices instead of simple handlers.
Production Patterns
In production, handlers are often combined with routing libraries to organize endpoints, middleware for cross-cutting concerns, and environment-based configuration for secure data fetching.
Connections
Event Loop
Data fetching handlers rely on the event loop to handle asynchronous operations efficiently.
Understanding the event loop explains why handlers can fetch data without blocking the server.
REST API Design
Handlers implement REST API endpoints by responding to HTTP methods and URLs.
Knowing REST principles helps design handlers that are intuitive and interoperable.
Restaurant Service Model
Handlers act like waiters in a restaurant, taking orders and delivering food.
This cross-domain connection helps grasp the request-response cycle as a service interaction.
Common Pitfalls
#1Ignoring asynchronous nature and blocking the server.
Wrong approach:function handler(request) { const data = fetch('https://api.example.com/data').then(res => res.json()); return new Response(JSON.stringify(data)); }
Correct approach:async function handler(request) { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return new Response(JSON.stringify(data)); }
Root cause:Not using async/await causes the response to send a Promise object instead of actual data.
#2Not setting Content-Type header for JSON responses.
Wrong approach:return new Response(JSON.stringify(data));
Correct approach:return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } });
Root cause:Omitting headers causes clients to misinterpret the response format.
#3Trying to read JSON body without awaiting.
Wrong approach:const body = request.json(); // missing await console.log(body);
Correct approach:const body = await request.json(); console.log(body);
Root cause:request.json() returns a Promise; forgetting await leads to logging a Promise object, not the data.
Key Takeaways
Data fetching handlers in Deno are async functions that receive requests and return responses with data.
Handlers use async/await to fetch data from APIs or databases without blocking the server.
Reading request details like URL and method lets handlers customize responses dynamically.
Proper error handling and status codes improve API reliability and client experience.
Advanced handlers use caching and streaming to optimize performance and scalability.

Practice

(1/5)
1. What is the main purpose of using a handler function with serve in Deno for data fetching?
easy
A. To compile TypeScript code into JavaScript
B. To create a database connection automatically
C. To style the webpage with CSS
D. To process incoming requests and send back appropriate responses

Solution

  1. Step 1: Understand the role of serve and handler

    The serve function listens for incoming HTTP requests and uses the handler function to decide how to respond.
  2. Step 2: Identify the handler's purpose

    The handler processes each request and returns the correct response, such as data or status messages.
  3. Final Answer:

    To process incoming requests and send back appropriate responses -> Option D
  4. Quick Check:

    Handler processes requests = D [OK]
Hint: Handler functions decide responses for requests [OK]
Common Mistakes:
  • Thinking handler creates database connections automatically
  • Confusing handler with styling or compilation tasks
  • Assuming serve compiles code
2. Which of the following is the correct syntax to start a basic HTTP server in Deno using a handler function named handler?
easy
A. handler.serve();
B. serve(handler);
C. serve = handler();
D. serve->handler();

Solution

  1. Step 1: Recall the serve function usage

    In Deno, serve is called with the handler function as an argument to start the server.
  2. Step 2: Check syntax correctness

    The correct syntax is serve(handler); which passes the handler function properly.
  3. Final Answer:

    serve(handler); -> Option B
  4. Quick Check:

    serve called with handler function = C [OK]
Hint: Call serve with handler function name only [OK]
Common Mistakes:
  • Using assignment instead of function call
  • Trying to call serve as a method on handler
  • Using invalid arrow syntax
3. Given this handler code snippet in Deno:
async function handler(req) {
  if (req.url.endsWith('/hello')) {
    return new Response('Hello World');
  }
  return new Response('Not Found', { status: 404 });
}

What will the server respond when a request is made to http://localhost:8000/hello?
medium
A. A response with body 'Hello World'
B. A 404 Not Found response
C. An empty response with status 200
D. A runtime error occurs

Solution

  1. Step 1: Analyze the URL condition in handler

    The handler checks if the request URL ends with '/hello'. If yes, it returns 'Hello World'.
  2. Step 2: Match the request URL

    The request URL is exactly '/hello', so the condition is true and the response is 'Hello World'.
  3. Final Answer:

    A response with body 'Hello World' -> Option A
  4. Quick Check:

    URL ends with '/hello' returns 'Hello World' = B [OK]
Hint: Check URL endsWith condition to find response [OK]
Common Mistakes:
  • Assuming 404 because of missing else
  • Thinking response body is empty
  • Expecting runtime error from this code
4. Identify the error in this Deno handler code snippet:
async function handler(req) {
  if (req.url === '/data') {
    const data = await fetch('https://api.example.com/data');
    return data.json();
  }
  return new Response('Not Found', { status: 404 });
}
medium
A. req.url should be req.path
B. Cannot use fetch inside handler
C. Missing await before data.json()
D. Response status 404 is invalid

Solution

  1. Step 1: Understand fetch and response handling

    The fetch returns a Response object; calling data.json() returns a Promise that must be awaited.
  2. Step 2: Identify missing await

    The code returns data.json() without awaiting it, which returns a Promise instead of a Response object.
  3. Final Answer:

    Missing await before data.json() -> Option C
  4. Quick Check:

    Await JSON parsing Promise = A [OK]
Hint: Always await async JSON parsing in handlers [OK]
Common Mistakes:
  • Thinking fetch is disallowed in handlers
  • Using req.path instead of req.url
  • Believing 404 status is invalid
5. You want to create a Deno server that fetches user data from an external API only when the request URL is /user. Which handler function correctly fetches the JSON data and returns it as a JSON response?
hard
A. async function handler(req) { if (req.url.endsWith('/user')) { const response = await fetch('https://api.example.com/user'); const data = await response.json(); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } }); } return new Response('Not Found', { status: 404 }); }
B. async function handler(req) { if (req.url === '/user') { const data = fetch('https://api.example.com/user').json(); return new Response(data); } return new Response('Not Found'); }
C. function handler(req) { if (req.url === '/user') { const data = await fetch('https://api.example.com/user').json(); return new Response(JSON.stringify(data)); } return new Response('Not Found'); }
D. async function handler(req) { if (req.url.includes('/user')) { const response = await fetch('https://api.example.com/user'); return response.json(); } return new Response('Not Found', { status: 404 }); }

Solution

  1. Step 1: Check correct async handling and URL matching

    async function handler(req) { if (req.url.endsWith('/user')) { const response = await fetch('https://api.example.com/user'); const data = await response.json(); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } }); } return new Response('Not Found', { status: 404 }); } uses endsWith('/user') to match URL and awaits both fetch and response.json(), which is correct.
  2. Step 2: Verify response formatting

    async function handler(req) { if (req.url.endsWith('/user')) { const response = await fetch('https://api.example.com/user'); const data = await response.json(); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } }); } return new Response('Not Found', { status: 404 }); } returns a new Response with JSON string and correct Content-Type header, ensuring proper JSON response.
  3. Final Answer:

    Option A correctly fetches and returns JSON data with proper async handling and headers -> Option A
  4. Quick Check:

    Await fetch and json, set JSON header = A [OK]
Hint: Await fetch and json, set JSON content-type header [OK]
Common Mistakes:
  • Not awaiting response.json()
  • Returning raw Promise instead of Response
  • Missing Content-Type header for JSON
  • Using non-async function with await
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard