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

Writing RESTful web services using Node.js - Frontend Dev Conf 2013, Minsk

Writing RESTful web services using Node.js - Frontend Dev Conf 2013, Minsk

An introduction to node.js, using it to build a simple restful webservice. Talk given 2013-04-20.

Jakob Mattsson

April 20, 2013
Tweet

More Decks by Jakob Mattsson

Other Decks in Programming

Transcript

  1. @jakobmattsson

    View Slide

  2. View Slide

  3. Started out doing consulting

    View Slide

  4. TUBSUVQT
    3FDSVJUJOH
    "EWFSUJTJOH
    'FFECBDL

    View Slide

  5. 2 000 000 000 writes/day!

    View Slide

  6. Quick and
    easy customer
    feedback
    Touch&Tell helps you find out
    what your guests really think
    about your establishment or
    event. You ask questions, they
    answer. It’s that simple.

    View Slide

  7. Writing RESTful
    web services
    using Node.js

    View Slide

  8. Comparison
    Rocket science
    Product demo
    Silver bullet

    View Slide

  9. Comparison
    Rocket science
    Product demo
    Silver bullet
    NOT

    View Slide

  10. What is it then?

    View Slide

  11. Imagination
    Quantity
    Bottom up
    Principles

    View Slide

  12. From zero to:
    - Getting a server up and running
    - Getting, storing and updating data
    - Declaring the service interface
    - Authenticating the user

    View Slide

  13. Also... CoffeeScript

    View Slide

  14. View Slide

  15. Node.js

    View Slide

  16. View Slide

  17. 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.

    View Slide

  18. 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.

    View Slide

  19. fs = require('fs')
    fs.readFile 'meaning_of_life.txt', 'utf-8', (err, data) ->
    console.log(data)
    console.log('end')

    View Slide

  20. end
    42

    View Slide

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

    View Slide

  22. http = require('http')
    onRequest = (req, res) ->
    res.writeHead(200, { 'Content-Type': 'text/plain' })
    res.end('Hello World\n')
    http.createServer(onRequest).listen(1337)

    View Slide

  23. npm

    View Slide

  24. 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.”

    View Slide

  25. npm install underscore

    View Slide

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

    View Slide

  27. Connect

    View Slide

  28. Connect is a midleware framework for node.
    It’s shipping with over 18 bundled middleware.
    It has a rich selection of 3rd-party middleware.

    View Slide

  29. npm install connect

    View Slide

  30. connect = require('connect')
    app = connect()
    app.listen(3000)
    // last line equivalent to
    // http.createServer(app).listen(3000);

    View Slide

  31. connect = require('connect')
    app = connect()
    app.use connect.basicAuth (user, pass) ->
    return user == 'jakob' && pass == 'fdc13'
    app.use (req, res) ->
    res.writeHead(200, { 'Content-Type': 'text/plain' })
    res.end('Hello World\n')
    app.listen(3000)

    View Slide

  32. 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

    View Slide

  33. Express

    View Slide

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

    View Slide

  35. npm install express

    View Slide

  36. 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)

    View Slide

  37. 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)

    View Slide

  38. Data storage

    View Slide

  39. But which one?

    View Slide

  40. Schemaless is a lie

    View Slide

  41. View Slide

  42. Mongoose

    View Slide

  43. Mongoose is a MongoDB object modeling tool
    designed to work in an asynchronous environment.

    View Slide

  44. npm install mongoose

    View Slide

  45. 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

    View Slide

  46. blogs = model 'blogs'
    name:
    type: String
    default: ''
    description:
    type: String
    default: ''
    users:
    type: ObjectId
    ref: 'users'

    View Slide

  47. posts = model 'posts'
    title:
    type: String
    default: ''
    body:
    type: String
    default: ''
    published:
    type: Date
    blogs:
    type: ObjectId
    ref: 'blogs'

    View Slide

  48. 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

    View Slide

  49. app.get '/users/:id', (req, res) ->
    get users, req.params.id, (err, data) ->
    res.json data

    View Slide

  50. copy-paste!

    View Slide

  51. 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

    View Slide

  52. or should we?

    View Slide

  53. 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 }

    View Slide

  54. 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

    View Slide

  55. But what about the relations/associations?

    View Slide

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

    View Slide

  57. 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

    View Slide

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

    View Slide

  59. Keep on generating!

    View Slide

  60. Authentication

    View Slide

  61. npm install passport
    npm install passport-local

    View Slide

  62. 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'))

    View Slide

  63. npm install passport-twitter

    View Slide

  64. 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'))

    View Slide

  65. Part 2
    Convention

    View Slide

  66. 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

    View Slide

  67. Verbs vs Nouns

    View Slide

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

    View Slide

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

    View Slide

  70. Associations

    View Slide

  71. /users
    /blogs
    /posts

    View Slide

  72. /users
    /blogs
    /posts
    /users/42/blogs
    /blogs/314/posts

    View Slide

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

    View Slide

  74. /users
    /blogs
    /posts
    /users/42/blogs
    /blogs/314/posts
    /users/42/blogs/314/posts

    View Slide

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

    View Slide

  76. GET /blogs/42/posts?tag=javascript

    View Slide

  77. Versions

    View Slide

  78. GET /v1/users

    View Slide

  79. Partial responses

    View Slide

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

    View Slide

  81. Verbs

    View Slide

  82. /convert?from=EUR&to=BYR&amount=100

    View Slide

  83. Content-types

    View Slide

  84. GET /v1/users/42.xml

    View Slide

  85. Attributes

    View Slide

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

    View Slide

  87. Search

    View Slide

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

    View Slide

  89. Authentication

    View Slide

  90. Part 3
    Conclusion

    View Slide

  91. View Slide

  92. It goes for ideas too!
    Reuse convention.

    View Slide

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

    View Slide

  94. View Slide

  95. View Slide

  96. ”MOST IMPORTANT STEP
    FOR BUILD PRODUCT
    IS BUILD PRODUCT”
    - @fakegrimlock

    View Slide

  97. @jakobmattsson
    Thank you!

    View Slide