作者基于例子来讲解如何用nodejs开发web程序,特别是存储部分使用了CouchDB,通过阅读可以熟练使用Journey, Cradle, Winston和Optimist。
文章来源:http://blog.nodejitsu.com/a-simple-webservice-in-nodejs
I recently gave a talk at ADICU Devfest 2011 on node.js. The talk was aimed at Computer Science students who did not know anything about node.js and more importantly, how to get started building simple and elegant web services from scratch. The slides (and code) from my talk are available onGitHub.
This article will walk through the code that I presented to build a simple RESTful bookmarking API using node.js and:
- Journey: A liberal JSON-only HTTP request router for node.js
- Cradle: A high-level, caching, CouchDB library for Node.js
- Winston: A multi-transport async logging library for node.js
- Optimist: Light-weight option parsing for node.js
Getting Started
The first step to doing anything with node.js is creating a server to accept incoming HTTP requests. You can also create a TCP server or work with UDP, but I won't be going into that here. So what does such a server look like?
The above example is simple (almost trivial): it exports a server that reads the request body, logs the request using winston, and responds with the appropriate HTTP response code (501 - Not Implemented). Not very exciting but enough to get us started.
Running the development server
The development server lives in bin/server
and has been configured so that we can run the development server at the various stages of development outlined in this tutorial. Running it is simple, just remember to pass the -t
argument which specifies which directory under /lib to use for the server:
- 00getting-started: The full source code from 'Getting Started'
- 01routing: The full source code from 'Adding some Routes'
- 02couchdb: The full source code from 'Interacting with CouchDB'
- 03authentication: The full source code from 'Adding HTTP Basic Auth'
$ bin/server -t 00getting-started Pinpoint demo server listening for 00getting-started on http://127.0.0.1:8000 4 Feb 17:59:22 - info: Incoming Request url=/
Adding some Routes
Now that we have a server that tells the world we haven't done anything it's time to think about what our application does:
- List: GET to /bookmarks should respond with a list of bookmarks
- Create: POST to /bookmarks should create a new bookmark
- Show: GET to /bookmarks/:id should respond with a specific bookmark
- Update: PUT to /bookmarks/:id should update a specific bookmark
- Destroy: DELETE to /bookmarks/:id should delete a specific bookmark
Pretty simple right? Absolutely. To accomplish this routing tasks we are going to use Journey. Journey is a great library with a lot of features. 0.3.1 was just released, which we will be taking advantage of in this post.
The above code generates a Journey router that matches the routes we outlined using regular expressions. In each route, we respond with 501 Not Implemented
and the corresponding action so that we can be sure we've hit the correct route.
We use the map.path
syntax to scope the subsequent routes behind /bookmarks
. The changes that need to be made to our development server are minimal. We just need to add code to create our router and later it to route our request with the associated request body within our HTTP server:
You can view the entire file on GitHub here.
Testing Routes using http-console
One of the things I did differently in this demo than I do in my own projects is that there are no vowsjs tests. I choose not to include tests in this demo because the additional overhead of understanding how a particular test framework works seemed a little high for the complete beginner.
The alternative was to use http-console: a simple, intuitive HTTP REPL. Getting http-console is easy to install using npm:
So lets fire-up http-console for an interactive session a couple of our newly minted routes:
We made two request to /bookmarks
and /bookmarks/foobar
respectively and got back 501 in both cases with valid JSON representing the specified action that is not yet implemented. We also got back the application/json
header which was automatically set for us by Journey.
Interacting with CouchDB
Interacting with a persistent data store is a must have for any webservice or web application. At Nodejitsu we use CouchDB and our library of choice iscradle. We will define a Bookmark resource with a couple of methods for performing basic CRUD on our bookmark object. Before we get to that we need to configure our Couch with a Design Document for Bookmark resources. If you want to learn more about Design Doucments see CouchDB: The Definitive Guide.
The above code requires cradle, configures it with the remote host, port and authentication (if required). If options.setup
is set then it asynchronously creates the Design Document in CouchDB and later responds with the database connection db
.
Now that we have a connection to CouchDB for our application, we can go ahead and create a Bookmark resource that consumes the connection. Within this resource we want to define functions for each of the CRUD operations we've outlined: create
, show
, list
, update
and destroy
.
I won't go into the details of how each of these methods work, but rest assured that they do. It's all very basic usage for cradle, so if you're interested in the specifics I invite you to read the documentation on the cradle GitHub page.
So now that we've configured CouchDB and we have a Bookmark resource, we need to connect our resource to the Journey router that we defined earlier.
In each of the new routes, we send the appropriate request data to the Bookmark resource, and asynchronously respond with the appropriate HTTP response code when complete. In the event of an error, we always send 500 Internal Server Error
with the error message.
Adding HTTP Basic Auth
The main focus of Journey 0.3.0 was to add a feature where the programmer could specify a filter function that takes the request and body. This filter function will intercept requests before they are passed to any route handler. If the filter function returns a pre-defined Journey error, the router will short-circuit and respond with the status code. We will use this feature to define a filter function that performs HTTP Basic Auth.
We can set this method on the Journey router by passing it in the options hash. Any routes that we wish to be behind the authentication filter need to be wrapped in a call to map.filter(function () { ... })
Wrapping up
Now that we have completed our web service, lets fire up http-console for an interactive session with our Bookmark resource.
I hope this has been helpful for those of you looking to get started with node.js. Check out the rest of our blog for more advanced libraries and tutorials. Come back soon for more on the Art of Nodejitsu.