(This page has no text content)
Fullstack Node.js The Complete Guide to Building Production Apps with Node.js Written by David Guttman Edited by Nate Murray © 2019 Fullstack.io All rights reserved. No portion of the book manuscript may be reproduced, stored in a retrieval system, or transmitted in any form or by any means beyond the number of purchased copies, except for a single backup or archival copy. The code may be used freely in your projects, commercial or otherwise. The authors and publisher have taken care in preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damagers in connection with or arising out of the use of the information or programs container herein. Published by Fullstack.io. FULLSTACK.io
Contents PRERELEASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Book Revision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Join Our Discord Channel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Bug Reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Be notified of updates via Twitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 We’d love to hear from you! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Your First Node API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Hello Node.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 A Rich Module Ecosystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 When To Use Node.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 When Node.js May Not Be The Best Choice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Front-end Vs. Back-end JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Diving In: Your First Node.js API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Serving JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Basic Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Dynamic Responses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 File Serving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Express . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Real-Time Chat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Async . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Promises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Async & Await . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Event Emitters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Event Emitters: Going Further . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Async Final Words . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 A Complete Server: Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Modularize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Controlling our API with Query Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
CONTENTS Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 A Complete Server: Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Creating Products . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Relationships . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Data For Development And Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 File Uploads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 A Complete Server: Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Private Endpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 A Complete Server: Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 What You Will Learn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 Deployment Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 Deployment Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 Wrapping Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 Command Line Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Building A CLI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 Testing Node.js Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 Changelog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 Revision 2 (11-25-2019) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 Revision 1 (10-29-2019) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
CONTENTS 1 PRERELEASE This version of the book is a PRERELEASE. The final version of the book will change from this revision. Please report any bugs you find to us@fullstack.io Thanks for checking out the early version – Nate & David Book Revision • Revision 2p - 2019-11-25 Join Our Discord Channel https://newline.co/discord/nodejs¹ Bug Reports If you’d like to report any bugs, typos, or suggestions just email us at: us@fullstack.io. Be notified of updates via Twitter If you’d like to be notified of updates to the book on Twitter, follow us at @fullstackio². We’d love to hear from you! Did you like the book? Did you find it helpful? We’d love to add your face to our list of testimonials on the website! Email us at: us@fullstack.io³. ¹https://newline.co/discord/nodejs ²https://twitter.com/fullstackio ³mailto:us@fullstack.io
Your First Node API Hello Node.js Node.js was first released in 2009 by Ryan Dahl as a reaction to how slow web servers were at the time. Most web servers would block for any I/O task⁴, such as reading from the file system or accessing the network, and this would dramatically lower their throughput. For example, if a server was receiving a file upload, it would not be able to handle any other request until the upload was finished. At that time, Dahl mostly worked with Ruby, and the dominant model for web applications was to have a pool of ruby processes that a web server (e.g. Ngninx) would proxy to. If one Ruby process was blocked with an upload, Nginx served the request to another. Node.js changed this model by making all I/O tasks non-blocking and asynchronous. This allowed web servers written in Node.js to serve thousands of requests concurrently – subsequent requests didn’t have to wait for previous ones to complete. The first demo of Node.js was generated so much interest because it was the first time that a developer could create their own web server easily and have it work so well. Over time Node.js became good at system tasks other than web serving and started to shine as a flexible yet lower level server-side language. It could do anything typically done with Python, Ruby, Perl, and PHP, and it was faster, used less memory, and in most cases had better APIs for the system calls. For example, with Node.js we can create HTTP and TCP servers with only a few lines of code. We’ll dive in and build one together soon, but just to show what we mean, here’s a functioning Node.js web server in only 80 characters: 01-first-node-api/00-hello-world.js require('http') .createServer((req, res) => res.end('hello world!')) .listen(8080) A Rich Module Ecosystem Node.js began to shine with the introduction of npm, the package manager bundled with Node.js. A core philosophy of Node.js is to have only a small collection of built-in modules that come preinstalled with the language. ⁴https://en.wikipedia.org/wiki/Input/output
Your First Node API 2 Examples of these modules are fs, http, tcp, dns, events, child_process, and crypto. There’s a full list in the Node.js API documentation⁵. This may seem to be a bad thing. Many people would be puzzled as why Node.js would choose not to have a large collection of standard modules preinstalled and available to the user. The reason is a bit counterintuitive, but has ultimately been very successful. Node.js wanted to encourage a rich ecosystem of third-party modules. Any module that becomes a built-in, core module will automatically prevent competition for its features. In addition, the core module can only be updated on each release of Node.js. This has a two-fold suppression effect on module authorship. First, for each module that becomes a core module in the standard library, many third-party modules that perform a similar feature will never be created. Second, any core modules will have development slowed by the Node.js release schedule. This strategy has been a great success. npm modules have grown at an incredible pace, overtaking all other package managers. In fact, one of the best things about Node.js is having access to a gigantic number of modules. When To Use Node.js Node.js is a great choice for any task or project where one would typically use a dynamic language like Python, PHP, Perl, or Ruby. Node.js particularly shines when used for: • HTTP APIs, • distributed systems, • command-line tools, and • cross-platform desktop applications. Node.js was created to be a great web server and it does not disappoint. In the next section, we’ll see how easy it is to build an HTTP API with the built-in core http module. Web servers and HTTP APIs built with Node.js generally have much higher performance than other dynamic languages like Python, PHP, Perl, and Ruby. This is partly because of its non-blocking nature, and partly because the Node.js V8 JavaScript interpreter is so well optimized. There are many popular web and API frameworks built with Node.js such as express⁶, hapi⁷, and restify⁸. Distributed systems are also very easy to build with Node.js. The core tcp module makes it very easy to communicate over the network, and useful abstractions like streams allow us to build systems using composable modules like dnode⁹. ⁵https://nodejs.org/api/index.html ⁶https://expressjs.com ⁷https://hapijs.com ⁸https://restify.com ⁹https://www.npmjs.com/package/dnode
Your First Node API 3 Command-line tools can make a developer’s life much easier. Before Node.js, there wasn’t a good way to create CLIs with JavaScript. If you’re most comfortable with JavaScript, Node.js will be the best way to build these programs. In addition, there are tons of Node.js modules like yargs¹⁰, chalk¹¹, and blessed¹² that make writing CLIs a breeze. Electron¹³, allows us to build cross-platform desktop applications using JavaScript, HTML, and CSS. It combines a browser GUI with Node.js. Using Node.js we’re able to access the filesystem, network, and other operating system resources. There’s a good chance you use a number of Electron apps regularly. When Node.js May Not Be The Best Choice Node.js is a dynamic, interpreted language. It is very fast compared to other dynamic languages thanks to the V8 JIT compiler. However, if you are looking for a language that can squeeze the most performance out of your computing resources, Node.js is not the best. CPU-bound workloads can typically benefit from using a lower-level language like C, C++, Go, Java, or Rust. As an extreme example, when generating fibonacci numbers¹⁴ Rust and C are about three times faster than Node.js. If you have a specialized task that is particularly sensitive to performance, and does not need to be actively developed and maintained, consider using a lower-level level language. Certain specialized software communities like machine learning, scientific computing, and data science have traditionally used languages other than JavaScript. Over time they have created many packages, code examples, tutorials, and books using languages like Python, R, and Java that either do not exist in JavaScript, are not at the same level of maturity, or do not have the same level of optimization and performance. Node.js might become more popular for these tasks in the future as more flagship projects like TensorFlow.js¹⁵ are developed. However, at this current time, fewer people in these disciplines have much Node.js experience. Front-end Vs. Back-end JavaScript If you’re more familiar with using JavaScript in the browser than you are with using it in Node.js, there a few differences worth paying attention to. The biggest difference between Node.js and running JavaScript in the browser is the lack of globals and common browser APIs. For example, window¹⁶ and document¹⁷ are unavailable in Node.js. Of ¹⁰https://yargs.js.org/ ¹¹https://github.com/chalk/chalk#readme ¹²https://github.com/chjj/blessed ¹³https://github.com/electron/electron#readme ¹⁴https://github.com/RisingStack/node-with-rust ¹⁵https://js.tensorflow.org/ ¹⁶https://developer.mozilla.org/en-US/docs/Web/API/Window ¹⁷https://developer.mozilla.org/en-US/docs/Web/API/Document
Your First Node API 4 course, this should not be not surprising; Node.js does not need to maintain a DOM or other browser- related technologies to function. For a list of global objects that browsers and Node.js share, see MDN’s list of Standard Built-in Objects¹⁸. Both Node.js and browser-based JavaScript can perform many of the same functions such as access the network or filesystem. However, the way these functions are accomplished will be different. For example, in the browser one will use the globally available fetch() API to create an HTTP request. In Node.js, this type of action would be done by first using const http = require('http') to load the built-in core http module, and afterwards using http.get('http://www.fullstack.io/', function (res) { ... }). Diving In: Your First Node.js API We’re going to start off by creating our own web server. At first, it will be very simple; you’ll be able to open your browser and see some text. What makes this impressive is just how little code is required to make this happen. 01-first-node-api/01-server.js 1 const http = require('http') 2 3 const port = process.env.PORT || 1337 4 5 const server = http.createServer(function (req, res) { 6 res.end('hi') 7 }) 8 9 server.listen(port) 10 console.log(`Server listening on port ${port}`) Run this file with node 01-server.js, and you should see Server listening on port 1337 printed to your terminal: ¹⁸https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
Your First Node API 5 After you see that, open your browser and go to http://localhost:1337 to see your message:
Your First Node API 6 Hello! Let’s look at this file line by line. First up: 01-first-node-api/01-server.js const http = require('http') This loads the core http¹⁹ module and stores it in our http variable. require()²⁰ is a globally accessible function in Node.js and is always available. http is a core module, which means that it is always available to be loaded via require(). Later on we’ll cover third-party modules that need to be installed before we can load them using require(). ¹⁹https://nodejs.org/api/http.html ²⁰https://nodejs.org/api/modules.html#modules_require_id
Your First Node API 7 01-first-node-api/01-server.js const port = process.env.PORT || 1337 Here we choose which port our web server should listen to for requests. We store the port number in our port variable. Also, we encounter a Node.js global object, process²¹. process is a global object²² with information about the currently running process, in our case it’s the process that is spawned when we run node 01-server.js. process.env is an object that contains all environment variables. If we were to run the server with PORT=3000 node 01-server.js instead, process.env.PORT would be set to 3000. Having environment variable control over port usage is a useful convention for deployment, and we’ll be starting that habit early. 01-first-node-api/01-server.js const server = http.createServer(function (req, res) { res.end('hi') }) Now we get to the real meat of the file. We use http.createServer()²³ to create a HTTP server object and assign it to the server variable. http.createServer() accepts a single argument: a request listener function. Our request listener function will be called every time there’s an HTTP request to our server (e.g., each time you hit http://localhost:1337 in your browser). Every time it is called, this function will receive two arguments: a request object²⁴ (req) and a response object²⁵ (res). For now we’re going to ignore req, the request object. Later on we’ll use it to get information about the request like url and headers. The second argument to our request listener function is the response object, res. We use this object to send data back to the browser. We can both send the string 'hi' and end the connection to the browser with a single method call: res.end('hi'). At this point our server object has been created. If a browser request comes in, our request listener function will run, and we’ll send data back to the browser. The only thing left to do, is to allow our server object to listen for requests on a particular port: ²¹https://nodejs.org/api/process.html#process_process ²²https://nodejs.org/api/globals.html#globals_require ²³https://nodejs.org/api/http.html#http_http_createserver_options_requestlistener ²⁴https://nodejs.org/api/http.html#http_class_http_incomingmessage ²⁵https://nodejs.org/api/http.html#http_class_http_serverresponse
Your First Node API 8 01-first-node-api/01-server.js server.listen(port) Finally, for convenience, we print a message telling us that our server is running and which port it’s listening on: 01-first-node-api/01-server.js console.log(`Server listening on port ${port}`) And that’s all the code you need to create a high-performance web server with Node.js. Of course this is the absolute minimum, and it’s unlikely that this server would be useful in the real world. From here we’ll begin to add functionality to turn this server into a usable JSON API with routing. Serving JSON When building web apps and distributed systems, it’s common to use JSON APIs to serve data. With one small tweak, we can change our server to do this. In our previous example, we responded with plain text: 01-first-node-api/01-server.js const server = http.createServer(function (req, res) { res.end('hi') }) In this example we’re going to respond with JSON instead. To do this we’re going to replace our request listener function with a new one: 01-first-node-api/02-server.js const server = http.createServer(function (req, res) { res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ text: 'hi', numbers: [1, 2, 3] })) }) When building a production server, it’s best to be explicit with responses so that clients (browsers and other consumers of our API) don’t handle our data in unexpected ways (e.g. rendering an image as text). By sending plain text without a Content-Type header, we didn’t tell the client what kind of data it should expect.
Your First Node API 9 In this example, we’re going to let the client know our response is JSON-formatted data by setting the Content-Type response header. In certain browsers this will allow the JSON data to be displayed with pretty printing and syntax highlighting. To set the Content-Type we use the res.setHeader()²⁶ method: 01-first-node-api/02-server.js res.setHeader('Content-Type', 'application/json') Next, we use the same method as last time to send data and close the connection. The only difference is that instead of sending plain text, we’re sending a JSON-stringified object: 01-first-node-api/02-server.js res.end(JSON.stringify({ text: 'hi', numbers: [1, 2, 3] })) Run node 02-server.js and navigate to http://localhost:1337 in your browser to see our new JSON response: ²⁶https://nodejs.org/api/http.html#http_request_setheader_name_value
Your First Node API 10 What our JSON response looks like Not all browsers will pretty-print JSON. In this screenshot I’m using Firefox, but there are several extensions available for Chrome like JSON Formatter²⁷ that will achieve the same result. Of course, our API needs some work before it’s useful. One particular issue is that no matter the URL path we use, our API will always return the same data. You can see this behavior by navigating to each of these URLs: • http://localhost:1337/a • http://localhost:1337/fullstack • http://localhost:1337/some/long/random/url. If we want to add functionality to our API, we should be able to handle different requests to different url paths or endpoints. For starters, we should be able to serve both our original plain text response and our new JSON response. ²⁷https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa/related
Your First Node API 11 Basic Routing Not all client requests are the same, and to create a useful API, we should be able to respond differently depending on the requested url path. We previously ignored the request object argument, req, and now we’re going to use that to see what url path the client is requesting. Depending on the path, we can do one of three things: • respond with plain text, • respond with JSON, or • respond with a 404 “Not Found” error. We’re going to change our request listener function to perform different actions depending on the value of req.url²⁸. The url property of the req object will always contain the full path of the client request. For example, when we navigate to http://localhost:1337 in the browser, the path is /, and when we navigate to http://localhost:1337/fullstack, the path is /fullstack. We’re going to change our code so that when we open http://localhost:1337 we see our initial plain-text “hi” mesage, when we open http://localhost:1337/json we’ll see our JSON object, and if we navigate to any other path, we’ll receive a 404 “Not Found” error. We can do this very simply by checking the value of req.url in our request listener function and running a different function depending on its value. First we need to create our different functions – one for each behavior. We’ll start with the functions for responding with plain-text and JSON. These new functions will use the same arguments as our request listener function and behave exactly the same as they did before: 01-first-node-api/03-server.js function respondText (req, res) { res.setHeader('Content-Type', 'text/plain') res.end('hi') } function respondJson (req, res) { res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ text: 'hi', numbers: [1, 2, 3] })) } The third function will have new behavior. For this one, we’ll respond with a 404 “Not Found” error. To do this, we use the res.writeHead()²⁹ method. This method will let us set both a response status code and header. We use this to respond with a 404 status and to set the Content-Type to text/plain. ²⁸https://nodejs.org/api/http.html#http_message_url ²⁹https://nodejs.org/api/http.html#http_response_writehead_statuscode_statusmessage_headers
Your First Node API 12 The 404 status code tells the client that the communication to the server was successful, but the server is unable to find the requested data. After that, we simply end the response with the message "Not Found": 01-first-node-api/03-server.js function respondNotFound (req, res) { res.writeHead(404, { 'Content-Type': 'text/plain' }) res.end('Not Found') } What our server returns for paths that don’t exist With our functions created, we can now create a request listener function that calls each one depending on the path in req.url:
Your First Node API 13 01-first-node-api/03-server.js const server = http.createServer(function (req, res) { if (req.url === '/') return respondText(req, res) if (req.url === '/json') return respondJson(req, res) respondNotFound(req, res) }) Now that we have basic routing set up, we can add more functionality to our server at different endpoints. Dynamic Responses Currently, our endpoints respond with the same data every time. For our API to be dynamic, it needs to change its responses according to input from the client. For apps and services in the real-world, the API will be responsible for pulling data out of a database or other resource according to specific queries sent by the client and filtered by authorization rules. For example, the client may want the most recent comments by user dguttman. The API server would first look to see if that client has authorization to view the comments of "dguttman", and if so, it will construct a query to the database for this data set. To add this style of functionality to our API, we’re going to add an endpoint that accepts arguments via query parameters. We’ll then use the information provided by the client to create the response. Our new endpoint will be /echo and the client will provide input via the input query parameter. For example, to provide “fullstack” as input, the client will use /echo?input=fullstack as the url path. Our new endpoint will respond with a JSON object with the following properties: • normal: the input string without a transformation • shouty: all caps • characterCount: the number of characters in the input string • backwards: the input string ordered in reverse To begin, we’ll first have our request listener function check to see if the request.url begins with /echo, the endpoint that we’re interested in. If it is, we’ll call our soon-to-be-created function respondEcho():
Your First Node API 14 01-first-node-api/04-server.js const server = http.createServer(function (req, res) { if (req.url === '/') return respondText(req, res) if (req.url === '/json') return respondJson(req, res) if (req.url.match(/^\/echo/)) return respondEcho(req, res) respondNotFound(req, res) }) Next, we create the respondEcho() function that will accept the request and response objects. Here’s what the completed function looks like: 01-first-node-api/04-server.js function respondEcho (req, res) { const { input = '' } = querystring.parse( req.url .split('?') .slice(1) .join('') ) res.setHeader('Content-Type', 'application/json') res.end( JSON.stringify({ normal: input, shouty: input.toUpperCase(), characterCount: input.length, backwards: input .split('') .reverse() .join('') }) ) } The important thing to notice is that the first line of our function uses the querystring.parse()³⁰ method. To be able to use this, we first need to use require() to load the querystring³¹ core module. Like http, this module is installed with Node.js and is always available. At the top of our file we’ll add this line: ³⁰https://nodejs.org/api/querystring.html#querystring_querystring_parse_str_sep_eq_options ³¹https://nodejs.org/api/querystring.html
Your First Node API 15 01-first-node-api/04-server.js const querystring = require('querystring') Looking at our respondEcho() function again, here’s how we use querystring.parse(): 01-first-node-api/04-server.js const { input = '' } = querystring.parse( req.url .split('?') .slice(1) .join('') ) We expect the client to access this endpoint with a url like /echo?input=someinput. querystring.parse() accepts a raw querystring argument. It expects the format to be something like query1=value1&query2=value2. The important thing to note is that querystring.parse() does not want the leading ?. Using some quick string transformations we can isolate the input=someinput part of the url, and pass that in as our argument. querystring.parse() will return a simple JavaScript object with query param key and value pairs. For example, { input: 'someinput' }. Currently, we’re only interested in the input key, so that’s the only value that we’ll store. If the client doesn’t provide an input parameter, we’ll set a default value of ''. Next up, we set the appropriate Content-Type header for JSON like we have before: 01-first-node-api/04-server.js res.setHeader('Content-Type', 'application/json') Finally, we use res.end() to send data to the client and close the connection: 01-first-node-api/04-server.js res.end( JSON.stringify({ normal: input, shouty: input.toUpperCase(), characterCount: input.length, backwards: input .split('') .reverse() .join('') }) )
Comments 0
Loading comments...
Reply to Comment
Edit Comment