Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing RESTful Web Services using Node.js

Writing RESTful Web Services using Node.js

7dd731d0c97e334d726f740a710904a9?s=128

Jakob Mattsson

June 06, 2013
Tweet

Transcript

  1. Jakob Mattsson @jakobmattsson

  2. Writing RESTful web services using Node.js

  3. Comparison Rocket science Product demo Silver bullet

  4. Comparison Rocket science Product demo Silver bullet NOT

  5. What is it then?

  6. Imagination Quantity Bottom up Principles

  7. Also... CoffeeScript

  8. Node.js

  9. Node.js is a platform built on Chrome's JavaScript runtime for

    easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.
  10. Node.js is a platform built on Chrome's JavaScript runtime for

    easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.
  11. fs = require('fs') fs.readFile 'meaning_of_life.txt', 'utf-8', (err, data) -> console.log(data)

    console.log('end')
  12. end 42

  13. Several protocols, including TCP and HTTP, are built in to

    node.
  14. http = require('http') onRequest = (req, res) -> res.writeHead(200, {

    'Content-Type': 'text/plain' }) res.end('Hello World\n') http.createServer(onRequest).listen(1337)
  15. npm

  16. npm is a package manager for node. You can use

    it to install and publish your node programs. ”It manages dependencies and does other cool stuff.”
  17. npm install underscore

  18. _ = require('underscore') numbers = _.range(1, 10) console.log(_.last(numbers))

  19. Connect

  20. Connect is a midleware framework for node. It’s shipping with

    over 18 bundled middleware. It has a rich selection of 3rd-party middleware.
  21. npm install connect

  22. connect = require('connect') app = connect() app.listen(3000) // last line

    equivalent to // http.createServer(app).listen(3000);
  23. connect = require('connect') app = connect() app.use connect.basicAuth (user, pass)

    -> return user == 'jakob' && pass == 'spainjs' app.use (req, res) -> res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello World\n') app.listen(3000)
  24. logger csrf compress basicAuth bodyParser json urlencoded multipart cookieParser session

    cookieSession methodOverride responseTime staticCache static directory vhost favicon limit query errorHandler Request logger with custom format support Cross-site request forgery protection Gzip compression middleware Basic http authentication Extensible request body parser Application/json parser Application/x-www-form-urlencoded parser Multipart/form-data parser Cookie parser Session management support with bundled MemoryStore Cookie-based session support Faux HTTP method support Calculates response-time and exposes via X-Response-Time Memory cache layer for the static() middleware Streaming static file server supporting Range and more Directory listing middleware Virtual host sub-domain mapping middleware Efficient favicon server (with default icon) Limit the bytesize of request bodies Automatic querystring parser, populating req.query Flexible error handler
  25. Express

  26. High performance high class web development for Node.js

  27. npm install express

  28. express = require('express') app = express.createServer() app.get '/', (req, res)

    -> res.send('Hello World') app.get '/users/:id', (req, res) -> res.send('user ' + req.params.id) app.listen(3000)
  29. express = require('express') app = express.createServer() before1 = (req, res,

    next) -> req.foo = 'bar' next() before2 = (req, res, next) -> res.header('X-Time', new Date().getTime()) next() app.get '/', before1, (req, res) -> res.send('Hello World') app.get '/users/:id', [before1, before2], (req, res) -> console.log(req.foo) res.send('user ' + req.params.id) app.listen(3000)
  30. Data storage

  31. But which one?

  32. One does not simply stick to a single DB

  33. Schemaless is a lie

  34. None
  35. Mongoose

  36. Mongoose is a MongoDB object modeling tool designed to work

    in an asynchronous environment.
  37. npm install mongoose

  38. mongoose = require 'mongoose' mongoose.connect 'mongodb://localhost/tamblr' model = (name, schema)

    -> mongoose.model name, new mongoose.Schema schema, strict: true users = model 'users' name: type: String default: '' bio: type: String default: 'IE6-lover' age: type: Number default: null
  39. blogs = model 'blogs' name: type: String default: '' description:

    type: String default: '' users: type: ObjectId ref: 'users'
  40. posts = model 'posts' title: type: String default: '' body:

    type: String default: '' published: type: Date blogs: type: ObjectId ref: 'blogs'
  41. list = (model, callback) -> model.find {}, callback get =

    (model, id, callback) -> model.findById id, callback del = (model, id, callback) -> model.remove { _id: id }, callback put = (model, id, data, callback) -> model.update { _id: id }, data, { multi: false }, callback post = (model, data, callback) -> new model(data).save callback
  42. app.get '/users/:id', (req, res) -> get users, req.params.id, (err, data)

    -> res.json data
  43. copy-paste!

  44. POST /users GET /users GET /users/42 DELETE /users/42 PUT /users/42

    POST /blogs GET /blogs GET /blogs/42 DELETE /blogs/42 PUT /blogs/42 POST /posts GET /posts GET /posts/42 DELETE /posts/42 PUT /posts/42
  45. or should we?

  46. models = [users, blogs, posts] Object.keys(models).forEach (modelName) -> app.get "/#{modelName}",

    (req, res) -> list models[modelName], (err, data) -> res.json data app.get "/#{modelName}/:id", (req, res) -> get models[modelName], req.params.id, (err, data) -> res.json data app.post "/#{modelName}", (req, res) -> post models[modelName], req.body, (err, data) -> res.json data app.del "/#{modelName}/:id", (req, res) -> del models[modelName], req.parmas.id, (err, count) -> res.json { count: count } app.put "/#{modelName}/:id", (req, res) -> put models[modelName], req.params.id, req.body, (err, count) -> res.json { count: count }
  47. POST /users GET /users GET /users/42 DELETE /users/42 PUT /users/42

    POST /blogs GET /blogs GET /blogs/42 DELETE /blogs/42 PUT /blogs/42 POST /posts GET /posts GET /posts/42 DELETE /posts/42 PUT /posts/42
  48. But what about the relations/associations?

  49. POST /users/42/blogs GET /users/42/blogs POST /blogs/42/posts GET /blogs/42/posts

  50. paths = models[modelName].schema.paths owners = Object.keys(paths).filter (p) -> paths[p].options.type ==

    ObjectId && typeof paths[p].options.ref == 'string' .map (x) -> paths[x].options.ref owners.forEach (owner) -> app.get "/#{owner}/:id/#{name}", (req, res) -> listSub models[name], owner, req.params.id, (err, data) -> res.json data app.post "/#{owner}/:id/#{name}", (req, res) -> postSub models[name], req.body, owner, req.params.id, (err, data) -> res.json data
  51. POST /users/42/blogs GET /users/42/blogs POST /blogs/42/posts GET /blogs/42/posts

  52. Keep on generating!

  53. Authentication

  54. npm install passport npm install passport-local

  55. passport = require('passport') passportLocal = require('passport-local') passport.use new passportLocal.Strategy (user,

    pass, done) -> findUserPlz { username: user, password: pass }, (err, user) -> done(err, user) app.use(passport.initialize()) app.use(passport.authenticate('local'))
  56. npm install passport-twitter

  57. passport = require('passport') twitter = require('passport-twitter') keys = { consumerKey:

    TWITTER_CONSUMER_KEY consumerSecret: TWITTER_CONSUMER_SECRET callbackURL: "http://127.0.0.1:3000/auth/twitter/callback" } passport.use new twitter.Strategy keys, (t, ts, profile, done) -> findOrCreateUserPlz { twitterId: profile.id }, (err, user) -> done(err, user) app.use(passport.initialize()) app.use(passport.authenticate('twitter'))
  58. Part 2 Convention

  59. ALL CHARACTERS AND EVENTS IN THIS SHOW-- EVENT THOSE BASED

    ON REAL PEOPLE--ARE ENTIRELY FICTIONAL. ALL CELEBERTY VOICES ARE IMPERSONATED.....POORLY. THE FOLLOWING PROGRAM CONTAINS COARSE LANGUAGE AND DUE TO ITS CONTENT IT SHOULD NOT BE VIEWED BE ANYONE
  60. Verbs vs Nouns

  61. /users /users/42 /blogs /blogs/73 /posts /posts/314

  62. GET /users GET /users/42 POST /users PUT /users/42 DELETE /users

    DELETE /users/42
  63. Associations

  64. /users /blogs /posts

  65. /users /blogs /posts /users/42/blogs /blogs/314/posts

  66. /users /blogs /posts /users/42/blogs /blogs/314/posts /users/42/blogs/314/posts

  67. /users /blogs /posts /users/42/blogs /blogs/314/posts /users/42/blogs/314/posts

  68. /users /blogs /posts /users/42/blogs /blogs/314/posts /users/42/blogs/314/posts Keep URLs short. Don’t

    overqualify.
  69. GET /blogs/42/posts?tag=javascript

  70. Versions

  71. GET /v1/users

  72. Partial responses

  73. GET /users?fields=email,age GET /users?limit=10&offset=0

  74. Verbs

  75. /convert?from=EUR&to=USD&amount=100

  76. Content-types

  77. GET /v1/users/42.xml

  78. Attributes

  79. { "userId": 1, "firstName": "Jakob", "lastName": "Mattsson" }

  80. Search

  81. GET /search?q=javascript GET /blog/42/posts?q=javascript

  82. Authentication

  83. Part 3 Conclusion

  84. None
  85. It goes for ideas too! Reuse convention.

  86. Reuse code. Resuse ideas. Build new things.

  87. None
  88. None
  89. ”MOST IMPORTANT STEP FOR BUILD PRODUCT IS BUILD PRODUCT” -

    @fakegrimlock
  90. @jakobmattsson Thank you!