├── .gitignore ├── package.json ├── index.js ├── README.md └── .jshintrc /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.sublime-workspace 3 | *.sublime-project 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-api-generator", 3 | "description": "A simple module that creates a json api from handlebar templates", 4 | "version": "1.1.0", 5 | "repository": "vikeri/json-api-generator", 6 | "main": "index.js", 7 | "dependencies": { 8 | "console.table": "^0.4.0", 9 | "dummy-json": "0.0.3", 10 | "express": "~4.13.0", 11 | "lodash": "^3.9.3", 12 | "lorem-ipsum": "^1.0.3", 13 | "open": "0.0.5" 14 | }, 15 | "author": "Viktor Eriksson ", 16 | "license": "ISC" 17 | } 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = function(vars) { 4 | 5 | var express = require('express'); 6 | var _ = require('lodash'); 7 | require('console.table'); 8 | var path = require('path'); 9 | var fs = require('fs'); 10 | var dummyjson = require('dummy-json'); 11 | var loremIpsum = require('lorem-ipsum'); 12 | var open = require('open'); 13 | 14 | try { 15 | var files = fs.readdirSync(vars.templateDir); 16 | } 17 | catch(err) { 18 | if (vars.templateDir) { 19 | console.log('Directory not found: '+ vars.templateDir); 20 | } else { 21 | console.log('You have to specify a template directory (templateDir)'); 22 | } 23 | process.exit(); 24 | } 25 | 26 | var app = express(); 27 | 28 | app.use(function(req, res, next) { 29 | res.header("Access-Control-Allow-Origin", "*"); 30 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 31 | res.set('Content-Type', 'application/json'); 32 | next(); 33 | }); 34 | 35 | 36 | 37 | var helpers = _.assign({ 38 | imageUrl: function() { 39 | return "http://lorempixel.com/" + dummyjson.randomInt(60, 500) + "/" + dummyjson.randomInt(60, 500); 40 | }, 41 | text: function() { 42 | return loremIpsum({ 43 | sentenceLowerBound: 10, 44 | sentenceUpperBound: 200 45 | }); 46 | }, 47 | randomFloat: function(min, max) { 48 | return dummyjson.randomFloat(min, max); 49 | } 50 | }, 51 | vars.helpers); 52 | 53 | 54 | // Reads all hbs files and creates their urls 55 | 56 | var templates = {}; 57 | var routes = []; 58 | console.log('\nAvailable routes (GET):'); 59 | for (var i in files) { 60 | if (path.extname(files[i]) === ".hbs") { 61 | var url = "/" + path.basename(files[i], '.hbs').replace("-", "/"); 62 | var file = fs.readFileSync(vars.templateDir + files[i], { 63 | encoding: 'utf8' 64 | }); 65 | routes.push(url); 66 | console.log(url); 67 | templates[url] = { 68 | url: url, 69 | file: dummyjson.parse(file, { 70 | data: {}, 71 | helpers: helpers 72 | }) 73 | }; 74 | } 75 | } 76 | 77 | _.each(templates, function(template) { 78 | var json = JSON.stringify(template.file); 79 | app.get(template.url, function(req, res) { 80 | if (vars.log) { 81 | console.log(template.url); 82 | } 83 | res.send(JSON.parse(this)); 84 | }.bind(json)); 85 | }); 86 | 87 | 88 | var portNo = vars.port ? vars.port : 1989; 89 | var address = vars.address ? vars.address : 'localhost'; 90 | 91 | var server = app.listen(portNo, address, function() { 92 | 93 | var port = server.address().port; 94 | var address = server.address().address; 95 | 96 | console.log('\nExample app listening at http://%s:%s', address, port); 97 | 98 | }); 99 | if (vars.open) { 100 | open("http://" + address + ":" + portNo + routes[0]); 101 | } 102 | }; 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # json-api-generator 2 | > Generates json api endpoints from templates 3 | 4 | Inspired by [mock-json-api](https://www.npmjs.com/package/mock-json-api) and relies heavily on [dummy-json](https://github.com/webroo/dummy-json) 5 | 6 | ## When? 7 | When you want to test your app with a json endpoint that is randomly generated from a template. 8 | 9 | What it does is it generates "random" json responses based on a template. This allows you to test your front-end client with different backend data that still coheres to your api. 10 | 11 | Each time you restart the server, the endpoint will give you a new generated response. 12 | 13 | ## How? 14 | 15 | You create your desired json structure by writing handlebars template files that will generate the json response (using [dummy-json](https://github.com/webroo/dummy-json)) 16 | 17 | See [example](#example) 18 | 19 | ## Anything fancy? 20 | 21 | Multiple endpoints can be defined, just create more template files and put them in your specified template folder. 22 | 23 | ## Install 24 | ```sh 25 | $ npm install json-api-generator --save-dev 26 | ``` 27 | 28 | ## Parameters 29 | The exported function takes a map with the following keys: 30 | 31 | ### `templateDir` 32 | The directory where you keep your template files. 33 | ### `helpers (optional)` 34 | A map of generator helpers. Functions that will be available in your template file. Some helpers are available out of the box, see (helpers)[#helpers] 35 | ### `log (optional)` 36 | A boolean specifying whether to log each request in the terminal. Defaults to false. 37 | ### `address (optional)` 38 | Which address the server should listen to, default to `localhost` 39 | ### `port (optional)` 40 | Which port the server should be run on, defaults to 1989. 41 | ### `open (optional)` 42 | A boolean specifying if the browser should be opened to the first endpoint automatically when running the server. 43 | 44 | ## Helpers 45 | Helpers are functions that can generate random data that can be used in the template. 46 | In addition to the helpers available [here](https://github.com/webroo/dummy-json#available-helpers) 47 | I have added a few others 48 | 49 | | Helper | Description| 50 | | ----- | ----- | 51 | | `imageUrl` | Sends a random image url from [lorempixel](http://lorempixel.com/). | 52 | | `text` | Lorem ipsum text | 53 | | `randomFloat` | Random decimal number | 54 | 55 | 56 | ## Example 57 | In this example we will create a GeoJSON response. 58 | 59 | A full repo with this example can be found here: [geojson-generator](https://github.com/vikeri/geojson-generator) 60 | 61 | An example of a resulting json is found here: [geojson-example](https://github.com/vikeri/geojson-generator/blob/master/example-output.json?short_path=b2147ad) 62 | 63 | ### `index.js` 64 | 65 | ```js 66 | "use strict"; 67 | 68 | var mock = require("json-api-generator"); 69 | 70 | mock({ 71 | // We keep our templates in a directory called templates 72 | templateDir: './templates/', 73 | 74 | // We create a helper that will return one of the values Project, Office or Trip. 75 | helpers: { 76 | place: function() { 77 | var place = ["Project","Office","Trip"]; 78 | return place[Math.floor(Math.random() * place.length)]; 79 | } 80 | }, 81 | 82 | // We wish to see when the resource is requested in the terminal 83 | log: true, 84 | 85 | // We also with that the browser should be opened to the correct url when starting the server 86 | open: true, 87 | }); 88 | 89 | ``` 90 | 91 | ### `templates/map.hbs` 92 | 93 | This is a template that generates a GeoJSON with five points on the map. 94 | 95 | ```hbs 96 | { 97 | "type": "FeatureCollection", 98 | "features": 99 | [ 100 | {{#repeat 5 }} 101 | { 102 | "type": "Feature", 103 | "properties": { 104 | "id": "{{index}}", 105 | "title": "{{place}}", {{! Here we use our handler defined in index.js }} 106 | "description": "{{text}}", 107 | "image": "{{imageUrl}}" 108 | }, 109 | "geometry": { 110 | "coordinates": [ 111 | {{randomFloat -90 90}}, 112 | {{randomFloat -30 60}} 113 | ], 114 | "type": "Point" 115 | }, 116 | "id": "{{index}}" 117 | } 118 | {{/repeat}} 119 | ] 120 | } 121 | 122 | ``` 123 | 124 | ## Changes 125 | 126 | * `1.1.0` Added address parameter 127 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "maxerr" : 50, // {int} Maximum error before stopping 4 | 5 | // Enforcing 6 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 7 | "camelcase" : false, // true: Identifiers must be in camelCase 8 | "curly" : true, // true: Require {} for every new block or scope 9 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 10 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 11 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 12 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 13 | "indent" : 4, // {int} Number of spaces to use for indentation 14 | "latedef" : false, // true: Require variables/functions to be defined before being used 15 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 16 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 17 | "noempty" : true, // true: Prohibit use of empty blocks 18 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 19 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 20 | "plusplus" : false, // true: Prohibit use of `++` & `--` 21 | "quotmark" : false, // Quotation mark consistency: 22 | // false : do nothing (default) 23 | // true : ensure whatever is used is consistent 24 | // "single" : require single quotes 25 | // "double" : require double quotes 26 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 27 | "unused" : true, // true: Require all defined variables be used 28 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 29 | "maxparams" : false, // {int} Max number of formal params allowed per function 30 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 31 | "maxstatements" : false, // {int} Max number statements per function 32 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 33 | "maxlen" : false, // {int} Max number of characters per line 34 | 35 | // Relaxing 36 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 37 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 38 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 39 | "eqnull" : false, // true: Tolerate use of `== null` 40 | "es5" : true, // true: Allow ES5 syntax (ex: getters and setters) 41 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 42 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 43 | // (ex: `for each`, multiple try/catch, function expression…) 44 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 45 | "expr" : true, // true: Tolerate `ExpressionStatement` as Programs 46 | "funcscope" : false, // true: Tolerate defining variables inside control statements 47 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 48 | "iterator" : false, // true: Tolerate using the `__iterator__` property 49 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 50 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 51 | "laxcomma" : false, // true: Tolerate comma-first style coding 52 | "loopfunc" : false, // true: Tolerate functions being defined in loops 53 | "multistr" : false, // true: Tolerate multi-line strings 54 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 55 | "notypeof" : false, // true: Tolerate invalid typeof operator values 56 | "proto" : false, // true: Tolerate using the `__proto__` property 57 | "scripturl" : false, // true: Tolerate script-targeted URLs 58 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 59 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 60 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 61 | "validthis" : true, // true: Tolerate using this in a non-constructor function 62 | 63 | // Environments 64 | "browser" : true, // Web Browser (window, document, etc) 65 | "browserify" : false, // Browserify (node.js code in the browser) 66 | "couch" : false, // CouchDB 67 | "devel" : true, // Development/debugging (alert, confirm, etc) 68 | "dojo" : false, // Dojo Toolkit 69 | "jasmine" : false, // Jasmine 70 | "jquery" : true, // jQuery 71 | "mocha" : true, // Mocha 72 | "mootools" : false, // MooTools 73 | "node" : true, // Node.js 74 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 75 | "prototypejs" : false, // Prototype and Scriptaculous 76 | "qunit" : false, // QUnit 77 | "rhino" : false, // Rhino 78 | "shelljs" : false, // ShellJS 79 | "worker" : false, // Web Workers 80 | "wsh" : false, // Windows Scripting Host 81 | "yui" : false, // Yahoo User Interface 82 | 83 | 84 | // Custom Globals 85 | "globals": { 86 | /* CHAI */ 87 | "expect": false, 88 | /* RequireJS */ 89 | "require": false, 90 | "define": false, 91 | "notify": false, 92 | "loadTestDependencies": false 93 | } // additional predefined global variables 94 | } 95 | --------------------------------------------------------------------------------