- Koa.js - Resources
- Koa.js - Scaffolding
- Koa.js - Logging
- Koa.js - RESTful APIs
- Koa.js - Database
- Koa.js - Caching
- Koa.js - Compression
- Koa.js - Authentication
- Koa.js - Sessions
- Koa.js - Cookies
- Koa.js - Static Files
- Koa.js - File Uploading
- Koa.js - Form Data
- Koa.js - Templating
- Koa.js - Cascading
- Koa.js - Error Handling
- Koa.js - Redirects
- Koa.js - Response Object
- Koa.js - Request Object
- Koa.js - HTTP Methods
- Koa.js - URL Building
- Koa.js - Routing
- Koa.js - Generators
- Koa.js - Hello World
- Koa.js - Environment
- Koa.js - Overview
- Koa.js - Home
Koa.js Useful Resources
Selected Reading
- Who is Who
- Computer Glossary
- HR Interview Questions
- Effective Resume Writing
- Questions and Answers
- UPSC IAS Exams Notes
Koa.js - RESTful APIs
To create mobile apppcations, single page apppcations, use AJAX calls and provide data to cpents, you ll need an API. A popular architectural style of how to structure and name these APIs and the endpoints is called REST(Representational Transfer State). HTTP 1.1 was designed keeping REST principles in mind. REST was introduced by Roy Fielding in 2000 in his paper Fielding Dissertations.
RESTful URIs and methods provide us with almost all information we need to process a request. The following table summarizes how the various verbs should be used and how URIs should be named. We ll be creating a movies API towards the end, so let’s discuss how it ll be structured.
Method | URI | Details | Function |
---|---|---|---|
GET | /movies | Safe, cachable | Gets the pst of all movies and their details |
GET | /movies/1234 | Safe, cachable | Gets the details of Movie id 1234 |
POST | /movies | N/A | Creates a new movie with details provided. Response contains the URI for this newly created resource. |
PUT | /movies/1234 | Idempotent | Modifies movie id 1234 (creates one if it doesn t already exist). Response contains the URI for this newly created resource. |
DELETE | /movies/1234 | Idempotent | Movie id 1234 should be deleted, if it exists. Response should contain the status of request. |
DELETE or PUT | /movies | Invapd | Should be invapd. DELETE and PUT should specify which resource they are working on. |
Now let’s create this API in Koa. We will be using JSON as our transport data format as it is easy to work with in JavaScript and has loads of other benefits. Replace your index.js file with the following −
INDEX.JS
var koa = require( koa ); var router = require( koa-router ); var bodyParser = require( koa-body ); var app = koa(); //Set up body parsing middleware app.use(bodyParser({ formidable:{uploadDir: ./uploads }, multipart: true, urlencoded: true })); //Require the Router we defined in movies.js var movies = require( ./movies.js ); //Use the Router on the sub route /movies app.use(movies.routes()); app.psten(3000);
Now that we have our apppcation set up, let us concentrate on creating the API. First set up the movies.js file. We are not using a database to store the movies but are storing them in memory, so every time the server restarts the movies added by us will vanish. This can easily be mimicked using a database or a file (using node fs module).
Import koa-router, create a Router and export it using module.exports.
var Router = require( koa-router ); var router = Router({ prefix: /movies }); //Prefixed all routes with /movies var movies = [ {id: 101, name: "Fight Club", year: 1999, rating: 8.1}, {id: 102, name: "Inception", year: 2010, rating: 8.7}, {id: 103, name: "The Dark Knight", year: 2008, rating: 9}, {id: 104, name: "12 Angry Men", year: 1957, rating: 8.9} ]; //Routes will go here module.exports = router;
GET Routes
Define the GET route for getting all the movies.
router.get( / , sendMovies); function *sendMovies(next){ this.body = movies; yield next; }
That s it. To test out if this is working fine, run your app, then open your terminal and enter −
curl -i -H "Accept: apppcation/json" -H "Content-Type: apppcation/json" -X GET localhost:3000/movies
You ll get the following response −
[{"id":101,"name":"Fight Club","year":1999,"rating":8.1},{"id":102,"name":"Inception","year":2010,"rating":8.7}, {"id":103,"name":"The Dark Knight","year":2008,"rating":9},{"id":104,"name":"12 Angry Men","year":1957,"rating":8.9}]
We have a route to get all the movies. Now let’s create a route to get a specific movie by its id.
router.get( /:id([0-9]{3,}) , sendMovieWithId); function *sendMovieWithId(next){ var ctx = this; var currMovie = movies.filter(function(movie){ if(movie.id == ctx.params.id){ return true; } }); if(currMovie.length == 1){ this.body = currMovie[0]; } else { this.response.status = 404;//Set status to 404 as movie was not found this.body = {message: "Not Found"}; } yield next; }
This will get us the movies according to the id that we provide. To test this out, use the following command in your terminal.
curl -i -H "Accept: apppcation/json" -H "Content-Type: apppcation/json" -X GET localhost:3000/movies/101
You ll get the response as −
{"id":101,"name":"Fight Club","year":1999,"rating":8.1}
If you visit an invapd route, it ll produce a cannot GET error, while if you visit a vapd route with an id that doesn’t exist, it ll produce a 404 error.
We are done with the GET routes. Now, let’s move on to POST route.
POST Route
Use the following route to handle the POSTed data.
router.post( / , addNewMovie); function *addNewMovie(next){ //Check if all fields are provided and are vapd: if(!this.request.body.name || !this.request.body.year.toString().match(/^[0-9]{4}$/g) || !this.request.body.rating.toString().match(/^[0-9].[0-9]$/g)){ this.response.status = 400; this.body = {message: "Bad Request"}; } else { var newId = movies[movies.length-1].id+1; movies.push({ id: newId, name: this.request.body.name, year: this.request.body.year, rating: this.request.body.rating }); this.body = {message: "New movie created.", location: "/movies/" + newId}; } yield next; }
This will create a new movie and store it in the movies variable. To test this route out, enter the following in your terminal −
curl -X POST --data "name = Toy%20story&year = 1995&rating = 8.5" https://localhost:3000/movies
You ll get the following response −
{"message":"New movie created.","location":"/movies/105"}
To test if this was added to the movies object, run the get request for /movies/105 again. You ll get the following response −
{"id":105,"name":"Toy story","year":"1995","rating":"8.5"}
Let’s move on to create the PUT and DELETE routes.
PUT Route
The PUT route is almost exactly the same as the POST route. We will be specifying the id for the object that ll be updated/created. Create the route in the following way −
router.put( /:id , updateMovieWithId); function *updateMovieWithId(next){ //Check if all fields are provided and are vapd: if(!this.request.body.name || !this.request.body.year.toString().match(/^[0-9]{4}$/g) || !this.request.body.rating.toString().match(/^[0-9].[0-9]$/g) || !this.params.id.toString().match(/^[0-9]{3,}$/g)){ this.response.status = 400; this.body = {message: "Bad Request"}; } else { //Gets us the index of movie with given id. var updateIndex = movies.map(function(movie){ return movie.id; }).indexOf(parseInt(this.params.id)); if(updateIndex === -1){ //Movie not found, create new movies.push({ id: this.params.id, name: this.request.body.name, year: this.request.body.year, rating: this.request.body.rating }); this.body = {message: "New movie created.", location: "/movies/" + this.params.id}; } else { //Update existing movie movies[updateIndex] = { id: this.params.id, name: this.request.body.name, year: this.request.body.year, rating: this.request.body.rating }; this.body = {message: "Movie id " + this.params.id + " updated.", location: "/movies/" + this.params.id}; } } }
This route will do the function we specified in the table above. It ll update the object with new details if it exists. If it doesn t exist, it ll create a new object. To test out this route, use the following curl command. This will update an existing movie. To create a new Movie, just change the id to a non-existing id.
curl -X PUT --data "name = Toy%20story&year = 1995&rating = 8.5" https://localhost:3000/movies/101
Response
{"message":"Movie id 101 updated.","location":"/movies/101"}
DELETE Route
Use the following code to create a delete route.
router.delete( /:id , deleteMovieWithId); function *deleteMovieWithId(next){ var removeIndex = movies.map(function(movie){ return movie.id; }).indexOf(this.params.id); //Gets us the index of movie with given id. if(removeIndex === -1){ this.body = {message: "Not found"}; } else { movies.sppce(removeIndex, 1); this.body = {message: "Movie id " + this.params.id + " removed."}; } }
Test the route in the same way we did for the others. On successful deletion (for example id 105), you will get −
{message: "Movie id 105 removed."}
Finally, our movies.js file looks pke −
var Router = require( koa-router ); var router = Router({ prefix: /movies }); //Prefixed all routes with /movies var movies = [ {id: 101, name: "Fight Club", year: 1999, rating: 8.1}, {id: 102, name: "Inception", year: 2010, rating: 8.7}, {id: 103, name: "The Dark Knight", year: 2008, rating: 9}, {id: 104, name: "12 Angry Men", year: 1957, rating: 8.9} ]; //Routes will go here router.get( / , sendMovies); router.get( /:id([0-9]{3,}) , sendMovieWithId); router.post( / , addNewMovie); router.put( /:id , updateMovieWithId); router.delete( /:id , deleteMovieWithId); function *deleteMovieWithId(next){ var removeIndex = movies.map(function(movie){ return movie.id; }).indexOf(this.params.id); //Gets us the index of movie with given id. if(removeIndex === -1){ this.body = {message: "Not found"}; } else { movies.sppce(removeIndex, 1); this.body = {message: "Movie id " + this.params.id + " removed."}; } } function *updateMovieWithId(next) { //Check if all fields are provided and are vapd: if(!this.request.body.name || !this.request.body.year.toString().match(/^[0-9]{4}$/g) || !this.request.body.rating.toString().match(/^[0-9].[0-9]$/g) || !this.params.id.toString().match(/^[0-9]{3,}$/g)){ this.response.status = 400; this.body = {message: "Bad Request"}; } else { //Gets us the index of movie with given id. var updateIndex = movies.map(function(movie){ return movie.id; }).indexOf(parseInt(this.params.id)); if(updateIndex === -1){ //Movie not found, create new movies.push({ id: this.params.id, name: this.request.body.name, year: this.request.body.year, rating: this.request.body.rating }); this.body = {message: "New movie created.", location: "/movies/" + this.params.id}; } else { //Update existing movie movies[updateIndex] = { id: this.params.id, name: this.request.body.name, year: this.request.body.year, rating: this.request.body.rating }; this.body = {message: "Movie id " + this.params.id + " updated.", location: "/movies/" + this.params.id}; } } } function *addNewMovie(next){ //Check if all fields are provided and are vapd: if(!this.request.body.name || !this.request.body.year.toString().match(/^[0-9]{4}$/g) || !this.request.body.rating.toString().match(/^[0-9].[0-9]$/g)){ this.response.status = 400; this.body = {message: "Bad Request"}; } else { var newId = movies[movies.length-1].id+1; movies.push({ id: newId, name: this.request.body.name, year: this.request.body.year, rating: this.request.body.rating }); this.body = {message: "New movie created.", location: "/movies/" + newId}; } yield next; } function *sendMovies(next){ this.body = movies; yield next; } function *sendMovieWithId(next){ var ctx = this var currMovie = movies.filter(function(movie){ if(movie.id == ctx.params.id){ return true; } }); if(currMovie.length == 1){ this.body = currMovie[0]; } else { this.response.status = 404;//Set status to 404 as movie was not found this.body = {message: "Not Found"}; } yield next; } module.exports = router;
This completes our REST API. Now you can create much more complex apppcations using this simple architectural style and Koa.
Advertisements