├── queries ├── redHerring.txt ├── sample.sql ├── sample2.sql └── User │ ├── Nested │ └── select.sql │ └── create.sql ├── .gitignore ├── package.json ├── LICENSE ├── index.js ├── test └── test.js └── README.md /queries/redHerring.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /queries/sample.sql: -------------------------------------------------------------------------------- 1 | SELECT NOW() AS "theTime" -------------------------------------------------------------------------------- /queries/sample2.sql: -------------------------------------------------------------------------------- 1 | SELECT 1::int AS number -------------------------------------------------------------------------------- /queries/User/Nested/select.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM users -------------------------------------------------------------------------------- /queries/User/create.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO users VALUES ($name,$email); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Preql", 3 | "version": "0.0.1", 4 | "description": "A tool for using sql queries in javascript", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test/test.js" 8 | }, 9 | "keywords": [ 10 | "sql", 11 | "database", 12 | "postgres", 13 | "mysql" 14 | ], 15 | "author": "Michael Fine (http://www.michaelfine.me/)", 16 | "license": "MIT", 17 | "dependencies": { 18 | "path": "~0.4.9" 19 | }, 20 | "devDependencies": { 21 | "should": "~4.0.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 NGP VAN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | 4 | 5 | function makeFunction(query) { 6 | return function(queryFunc, fn) { 7 | return queryFunc(query, fn); 8 | } 9 | } 10 | 11 | function isSql(name) { 12 | return name.slice(-4, name.length) === '.sql'; 13 | } 14 | 15 | function isDir(path) { 16 | return fs.statSync(path).isDirectory() 17 | } 18 | 19 | 20 | module.exports = { 21 | makeQuery: function(file) { 22 | return makeFunction(fs.readFileSync(file).toString()); 23 | }, 24 | 25 | makeQueries: function(dir) { 26 | 27 | var queries = {}; 28 | 29 | //cache which are directories to prevent unneeded reads 30 | var dirs = {}; 31 | 32 | //to avoid lexical scoping issues 33 | var that = this; 34 | 35 | var files = fs.readdirSync(dir).filter(function(file) { 36 | if (isDir(path.join(dir, file))) { 37 | dirs[file] = true; 38 | return true 39 | } 40 | 41 | return isSql(file); 42 | }); 43 | 44 | var contents = files.map(function(fileName) { 45 | if (dirs[fileName]) { 46 | return that.makeQueries(path.join(dir, fileName)); 47 | } 48 | 49 | return fs.readFileSync(path.join(dir, fileName)).toString(); 50 | }); 51 | 52 | files.forEach(function(fileName, i) { 53 | if (dirs[fileName]) { 54 | queries[fileName] = contents[i]; 55 | } 56 | 57 | queries[fileName.slice(0, -4)] = makeFunction(contents[i]); 58 | }); 59 | 60 | return queries; 61 | } 62 | }; -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var norm = require('../index.js'); 3 | 4 | 5 | describe('nORM', function(){ 6 | var sample = norm.makeQuery('./queries/sample.sql'); 7 | 8 | it('should generate the correct query function from file', function(done){ 9 | sample(function(query){ 10 | query.should.equal('SELECT NOW() AS "theTime"') 11 | }); 12 | done(); 13 | }); 14 | 15 | it('should pass parameters to the query function', function(done){ 16 | function query(text,obj){ 17 | obj.should.have.property('a',1) 18 | } 19 | sample(query,{a: 1}); 20 | done(); 21 | }); 22 | 23 | it('should pass callback to the query function', function(done){ 24 | sample(function(text,fn){ 25 | fn.should.be.type('function'); 26 | },function(){}); 27 | done(); 28 | }); 29 | 30 | describe('makeQueries', function(){ 31 | var queries = norm.makeQueries('./queries'); 32 | 33 | it('should generate queries for directory', function(done){ 34 | queries.sample2(function(query){ 35 | query.should.equal('SELECT 1::int AS number'); 36 | }) 37 | done(); 38 | }); 39 | 40 | it('should ignore non-sql files',function(done){ 41 | queries.should.not.have.property('redHerring'); 42 | done(); 43 | }); 44 | 45 | it('should recursively generate queries to an arbitrary depth', function(done){ 46 | queries.User.create(function(query){ 47 | query.should.equal('INSERT INTO users VALUES ($name,$email);'); 48 | }); 49 | queries.User.Nested.select(function(query){ 50 | query.should.equal('SELECT * FROM users'); 51 | }); 52 | done(); 53 | }); 54 | }); 55 | }) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Preql.js # 2 | 3 | Preql is a library to make writing SQL in javascript bearable, inspired by [Yesql](https://github.com/krisajenkins/yesql). 4 | 5 | ##Rationale## 6 | 7 | Sometimes, when accessing a SQL database in Javascript, you don't neccesarily want or need an ORM. But, writing something like 8 | 9 | function getPost(id){ 10 | client.query('SELECT * FROM posts WHERE id=$1',[id]); 11 | } 12 | Not only gets repetitive for multiple models, it's hard to read, there's no syntax highlighting, and multiline strings are annoying to format. 13 | 14 | Enter Preql 15 | 16 | ##Usage## 17 | 18 | To use Preql, have a file with your sql query: 19 | 20 | SELECT * 21 | FROM posts 22 | WHERE author = 'John Doe' 23 | LIMIT 10; 24 | 25 | (Note that while this example uses postgres syntax, preql works with any sql database). 26 | 27 | Then, in your javascript file, call preql and now it can be used as a regular javascript function. 28 | 29 | var preql = require('preql'); 30 | var pg = require('pg'); 31 | 32 | var getPosts = preql.makeQuery('./some/directory/get_posts.sql'); 33 | 34 | getPosts(client.query.bind(client), function(err,result){ 35 | console.log(result); 36 | }); 37 | 38 | The functions generated by preql take three parameters. The first is the query function, typically generated by your database client. So where you would normally say 39 | 40 | client.query('SELECT * FROM posts',callback); 41 | with preql you would write 42 | 43 | getPosts(client.query.bind(client),callback); 44 | 45 | The reason you have to call .bind(client) is to compensate for javascript's lexical scoping, allowing the query function to act normally in a closure. 46 | 47 | ### makeQueries ### 48 | 49 | Preql also allows you to recursively require an entire directory of files, creating queries out of the .sql files in that directory. For example, if you had a directory like 50 | 51 | /queries 52 | /users 53 | find.sql 54 | getPosts.sql 55 | createPost.sql 56 | file.txt 57 | when you call 58 | 59 | preql.makeQueries('./queries'); 60 | 61 | it will return 62 | { 63 | getPosts: [Function], 64 | createPosts: [Function], 65 | users: { 66 | find: [Function] 67 | } 68 | } 69 | 70 | ## Development ## 71 | To install: ``npm install preql`` 72 | 73 | Presql uses mocha and should for tests, run ``npm test`` to run the test suite. 74 | 75 | ##Todo## 76 | * Allow for multiple queries per file 77 | 78 | _Written by Michael Fine_ 79 | 80 | 81 | 82 | --------------------------------------------------------------------------------