diff --git a/README.md b/README.md index 3fe47dd59..90b646d30 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,40 @@ # Chinchay [![Build Status](https://travis-ci.com/afontainec/chinchay.svg?branch=master)](https://travis-ci.com/afontainec/chinchay) [![Coverage Status](https://coveralls.io/repos/github/afontainec/chinchay/badge.svg?branch=master)](https://coveralls.io/github/afontainec/chinchay?branch=master) -Building a web app can be very time consuming and tedious, but Chinchay will help you speed up your development. Use Chichay CLI to automate the CRUD (Create, Read, Update, Delete) operations with a MVC (Model View Controller) architecture pattern. You can easily configure it and modify it. It you are a bit lost, Chinchay does provide a default Structure. +Building a web app is no easy task. The coding itself can be very time consuming and tedious, but it's not the trickiest part: having an organized file structure, following industry best practices, restricting access and managing HTTP status codes correctly is where development gets puzzling. But fear not! Chinchay will speed up your development making sure you end up with a top-notch web app. -But it does not stop there! Chinchay offers a fully featured, flexible and extendable Models to make quering to your Database easier. With no sql knowledge you can easily make complex queries and more. +Going technical, [Chinchay](https://www.npmjs.com/package/chinchay) is a npm package that works on top of [express.js](https://expressjs.com/) (and therefore [node.js](https://nodejs.org/es/)) and connects to a [postgreSQL](https://www.postgresql.org/) database through [knex.js](http://knexjs.org/), a SQL query builder. +### So what does Chinchay actually do? +* Use [Chinchay’s CLI](./docs/cli) to automate [CRUD](https://www.codecademy.com/articles/what-is-crud) (Create, Read, Update, Delete) operations. It will follow a [MVC(Model View Controller)](https://techterms.com/definition/mvc) architecture pattern. With it, I can assure you coding will not be tedious nor time consuming. Chinchay tries not to be highly-opinionated and favours flexibility, therefore is fully customizable through the [.chainfile.js](./docs/chainfile). +* [Chinchay’s CLI](./docs/cli) will create an API following the [REST](https://restfulapi.net/) application architecture. +* If you do not use the Chinchay CLI, Chinchay does offer a [HATEOAS generator](./docs/hateoas) that can be added to your API, one step forward towards a RESTful API. +* Chinchay provides an [ErrorHandler](./errorhandler/) to manage HTTP status codes, returning meaningful messages and codes. +* Protect your app, so that only authorized users access the data. Use Chinchay [Access Module](./middleware/access), [Chinchay’s middleware](./middleware/middleware) and Chinchay’s sister package [TheWall](https://www.npmjs.com/package/thewall) to fully control who can access what. See the [API tutorial](./gettingstarted/apiMiddleware) for a complete guide! +* Chinchay’s CLI will also create Frontend views to work with the generated API. It can either be with [ejs](https://ejs.co/) or [Angular](https://angular.io/). [Currently working in more frontend options!] +* Chinchay allows API clients to customize their queries. With just a couple of endpoints and a very simple syntax, API clients can query what they need. Obviously this can be limited and disabled as needed. See [API: Client Querying](./docs/clientside) for more! +* Last, But definitely not least, Chinchay offers a flexible and extendable [Table Gateway Model](./models/). To organize and manage your database queries without even knowing any SQL. -Any Contribution are welcome! - -* [Github Repository](https://github.com/afontainec/chinchay) -* [Issues](https://github.com/afontainec/chinchay/issues) -* [Pull Requests](https://github.com/afontainec/chinchay/pulls) - -For more information visit the [Chinchay website](https://afontainec.github.io/chinchay/clitutorial) +### Getting Started +So what are you waiting for?? Go to the [getting started section](./gettingstarted/) to begin your journey with Chinchay! -## Command Line Interface +Here some quick links: -Use a Command Line Interface to make a web app that creates, read, updated and deletes entries from a Postgres Database. Do it only with one simple command: +* [See our tutorials to get started!](./gettingstarted) +* [See the detailed Documentation](./docs) +* [the full docs of our table gateway model](./table-gateway) +* [Protect your routes with our middleware](./middleware) +* [Manage your http responses with our ErrorHandler](./errorhandler) +* [Any help is welcome! If you want to collaborate see our collaboration manual!](./collaborate) -``` -$ chinchay new relation_name -``` -Files Created are completely configurable. - -* [Go to a full tutorial, creating an app from cero](https://afontainec.github.io/chinchay/clitutorial) -* [Go to the Command Line Interface Documentation!](https://afontainec.github.io/chinchay/clidocs) +### Collaborate +Any Contribution are welcome! -## Extendable Models +* [Github Repository](https://github.com/afontainec/chinchay) +* [Issues](https://github.com/afontainec/chinchay/issues) +* [Pull Requests](https://github.com/afontainec/chinchay/pulls) -Do not get stuck making complex SQL queries. Chinchay offers a fully featured, flexible and extendable Models to make quering easier to your Database easier. With no sql knowledge you can easily make complex queries and more. +For more information visit the [Chinchay website](http://chinchay.accionet.net) diff --git a/cliApp/lector.js b/cliApp/lector.js index 6c8d4c6a5..7e36fe4d8 100755 --- a/cliApp/lector.js +++ b/cliApp/lector.js @@ -49,7 +49,7 @@ program program .on('--help', () => { - console.log(' For help visit https://afontainec.github.io/chinchay/'); + console.log(' For help visit http://chinchay.accionet.net'); }); program diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html deleted file mode 100644 index c1c8252d8..000000000 --- a/docs/_layouts/default.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - -{% seo %} - - - - -
-
- - - -

Chinchay

- - {% if site.logo %} - Logo - {% endif %} - -

{{ site.description | default: site.github.project_tagline }}

- - {% if site.github.is_project_page %} -

View the Project on GitHub {{ site.github.repository_nwo }}

- {% endif %} - - {% if site.github.is_user_page %} -

View My GitHub Profile

- {% endif %} - - -
-
- - {{ content }} - -
-
-
-

Contributing

-

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. -Please make sure to update tests as appropriate.

- -
- -
- - {% if site.google_analytics %} - - {% endif %} - - diff --git a/docs/all_clitutorial_commands.txt b/docs/all_clitutorial_commands.txt deleted file mode 100644 index 93bcd70d4..000000000 --- a/docs/all_clitutorial_commands.txt +++ /dev/null @@ -1,80 +0,0 @@ -rm -r test_chinchay -psql postgres -c "DROP DATABASE test_chinchay" - -npm install express -g -express test_chinchay && cd test_chinchay -npm install pg -s -npm install knex -s -npm install ejs -s -npm install -psql postgres -c "CREATE DATABASE test_chinchay" -mkdir database -mkdir database/migrations -echo "const path = require('path'); - -module.exports = { - test: { - client: 'pg', - connection: 'postgres://localhost:5432/test_chinchay', - migrations: { - directory: path.join(__dirname, '/database/migrations'), - }, - seeds: { - directory: path.join(__dirname, '/database/seeds/test'), - }, - acquireConnectionTimeout: 10000, - }, - development: { - client: 'pg', - connection: 'postgres://localhost:5432/test_chinchay', - migrations: { - directory: path.join(__dirname, '/database/migrations'), - }, - seeds: { - directory: path.join(__dirname, '/database/seeds/development'), - }, - acquireConnectionTimeout: 10000, - }, - production: { - client: 'pg', - connection: process.env.DATABASE_URL || 'postgres://localhost:5432/test_chinchay', - migrations: { - directory: path.join(__dirname, '/database/migrations'), - }, - seeds: { - directory: path.join(__dirname, '/database/seeds/production'), - }, - acquireConnectionTimeout: 10000, - }, - staging: { - client: 'pg', - connection: process.env.DATABASE_URL || 'postgres://localhost:5432/test_chinchay', - migrations: { - directory: path.join(__dirname, '/database/migrations'), - }, - seeds: { - directory: path.join(__dirname, '/database/seeds/production'), - }, - acquireConnectionTimeout: 10000, - }, -};" > knexfile.js -echo "const environment = process.env.NODE_ENV || 'development'; -const config = require('./knexfile')[environment]; -module.exports = require('knex')(config);" > knex.js -echo "const path = require('path'); - -module.exports = { - models: { - directory: path.join(__dirname, '/models'), - }, - controllers: { - directory: path.join(__dirname, '/controllers') - }, - views: { - directory: path.join(__dirname, '/views') - }, - routes: { - directory: path.join(__dirname, '/routes') - }, - knex: path.join(__dirname, 'knex.js') -};" > .chainfile.js diff --git a/docs/clidocs.md b/docs/clidocs.md deleted file mode 100644 index ce5ba0dbd..000000000 --- a/docs/clidocs.md +++ /dev/null @@ -1,1316 +0,0 @@ -## Command Line Interface Documentation - -Creating CRUD (Create, Read, Update, Delete) operations has never being easier than with Chinchay. Use a intuitive and easy-to-use CLI to get started, generating Model View Controllers with just one command. - -Do not get stuck making complex SQL queries. Chinchay makes that part easy using readable JSON object. - - -### Getting Started - -First things first, we need a Node.js app with express, knex and postgresql. So go ahead and create it, if you are lost or have no idea what are we talking about, no worries, follow our step-by-step [CLI Tutorial](/chinchay/clitutorial). - -Once you have your app nice and ready, lets add the Chinchay Dependency, run: -``` -$ npm install chinchay -s -$ npm install chinchay -g -``` -
-The **-g** its really important for the CLI to work. - -Now its time for magic, run the command: -``` -$ chinchay new relation_name -``` -
- -Where _relation_name_ is the name of the relation you want to set the CRUD operations. For this documentations with a relation named _coffee_. Therefore, the command would be: -``` -$ chinchay new coffee -``` -
- -This command will create 2 route files, 1 controller, 1 model, 4 views files and 1 migration file. We will explain how to workaround each file. You can modify the directory where they were saved by creating a .chainfile.js, feel free to go to [chainfile section](#.chainfile) for more information. - - -We need to declare the schema of our relation, we do this in the migration file. Go ahead and do so, if you don't have a clue of what are we talking about, check our [migration section](#migration). In our case, we will use the following schema, but feel free to use another one! -```javascript -exports.up = function (knex) { - return knex.schema.createTable('coffee', (table) => { - // Incremental id - table.increments(); - table.string('name').notNullable(); - table.integer('price'); - // created_at and updated_at - table.timestamps(); - }); -}; - -exports.down = function (knex) { - return knex.schema.dropTable('coffee'); -}; -``` -
- Don't forget to run: `$ knex migrate:latest` - -Lastly we add our routes to the app.js file, in the general case add the following lines: - - -```javascript -var relation_name = require('./routes/relation_name'); -var relation_nameAPI = require('./routes/relation_nameAPI'); -app.use('/', relation_name); -app.use('/', relation_nameAPI); -``` -
- -In our case, using the _coffee_ relation, those lines should be: - -```javascript -var coffee = require('./routes/coffee'); -var coffeeAPI = require('./routes/coffeeAPI'); -app.use('/', coffee); -app.use('/', coffeeAPI); -``` -
- -To view are app, run: -``` -$ npm start -``` -
- And browse to [http://localhost:3000/coffee](http://localhost:3000/coffee) to start working with your Chinchay app! - - -### Working with the generated API: {#generated-api} - -Chinchay will build a fully functional api so you can start working with your CRUD operation. Here is a list of the URL created and examples of how to work with them: - -
-#### POST /api/relation_name/new -
-##### **Description:** - Receives a JSON object and, in the database, inserts an entry with values defined in the JSON. It will return whether it was successful or not, and the saved entry. -##### **Example:** - _NOTE:_ For this, and *all* the examples we are using the _coffee_ relation we created, if another one was used, change accordingly. - -The following: -```javascript -Requestify.post('http://localhost:3000/api/coffee/new', {name: "this is the name", price: 100}); -``` -
-Will save in the database an entry were _name="this is the name"_ and _price=100_ in the relation _coffee_. The Return is as follows: - -```javascript -{ - "message": "Elemento guardado exitosamente", - "data": { - "id": 1, - "name": "this is the name", - "price": 100, - "created_at": "2018-11-21T11:54:42.840Z", - "updated_at": "2018-11-21T11:54:42.840Z", - "links": [ - { "rel": "self", "href": "/api/coffee/1", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/1/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/1/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" } - ], - } -} -``` -
- -The following: -```javascript -Requestify.post('http://localhost:3000/api/coffee', {name: "this is the name"}); -``` -
- -Will save in the database an entry, were _name="this is the name"_ and _price=null_, in the relation _coffee_. The response is: - -```javascript -{ - "message": "Elemento guardado exitosamente", - "data": { - "id": 2, - "name": "this is the name", - "price": null, - "created_at": "2018-11-21T11:57:02.767Z", - "updated_at": "2018-11-21T11:57:02.767Z", - "links": [ - { "rel": "self", "href": "/api/coffee/2", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" } - ], - } -} -``` -
-In both cases, the return is an JSON object with a _message_ and a _data_ property with a JSON object representing the saved entry. -
- -#### GET /api/relation_name/:id {#find-by-id-api} -
- -##### **Description:** -Returns a JSON object representing the object with id = :id. If there is no such entry, it reports the error. -##### **Example:** - -The following: -```javascript -Requestify.get('http://localhost:3000/api/coffee/1'); -``` -
- -Will return a JSON representing the object with id=1: - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": { - "id": 1, - "name": "this is the name", - "price": 100, - "created_at": "2018-11-21T11:54:42.840Z", - "updated_at": "2018-11-21T11:54:42.840Z", - "links": [ - { "rel": "self", "href": "/api/coffee/1", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/1/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/1/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" } - ], - } -} -``` -
- - -#### GET /api/relation_name/find {#find-api} -
-##### **Description:** -Returns an array with all the entries matching the given query. -##### **Simple Queries:** -Here are some examples of how to work with simple queries: The query will filter with the given format _key=value_. - - **1. Get all:** - - If no query is defined, it will return all the elements. - - Therefore, the request: - - ```javascript - Requestify.get('http://localhost:3000/api/coffee/find'); - ``` -
- - Will return an array with all the entries: - - ```javascript - { - "message": "Busqueda encontrada exitosamente", - "data": [{ - "id": 1, - "name": "this is the name", - "price": 100, - "created_at": "2018-11-21T11:54:42.840Z", - "updated_at": "2018-11-21T11:54:42.840Z", - "links": [ { "rel": "self", "href": "/api/coffee/1", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/1/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/1/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "id": 2, - "name": "this is the name", - "price": null, - "created_at": "2018-11-21T11:57:02.767Z", - "updated_at": "2018-11-21T11:57:02.767Z", - "links": [ { "rel": "self", "href": "/api/coffee/2", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "id": 3, - "name": "other", - "price": 100, - "created_at": "2018-11-21T12:06:04.065Z", - "updated_at": "2018-11-21T12:06:04.065Z", - "links": [ { "rel": "self", "href": "/api/coffee/3", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/3/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/3/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "id": 4, - "name": "expensive", - "price": 110, - "created_at": "2018-11-21T12:06:22.400Z", - "updated_at": "2018-11-21T12:06:22.400Z", - "links": [ { "rel": "self", "href": "/api/coffee/4", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/4/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/4/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - } - ], - } - ``` -
- - - **2. Filter with query:** - This is the simplest but powerful way of querying, the query will filter with the given format _key=value_. - - Therefore, the request: - - ```javascript - Requestify.get('http://localhost:3000/api/coffee/find?price=100'); - ``` -
- Will return an array of all the entries were _price=100_: - - ```javascript - { - "message": "Busqueda encontrada exitosamente", - "data": [{ - "id": 1, - "name": "this is the name", - "price": 100, - "created_at": "2018-11-21T11:54:42.840Z", - "updated_at": "2018-11-21T11:54:42.840Z", - "links": [ - { "rel": "self", "href": "/api/coffee/1", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/1/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/1/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "id": 3, - "name": "other", - "price": 100, - "created_at": "2018-11-21T12:06:04.065Z", - "updated_at": "2018-11-21T12:06:04.065Z", - "links": [ - { "rel": "self", "href": "/api/coffee/3", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/3/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/3/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }], - } - ``` -
- - and the following: - - ```javascript - Requestify.get('http://localhost:3000/api/relation_name/find?price=100&name=other'); - ``` -
- - Will return an array of all the entries were price=100 and name="other": - - ```javascript - { - "message": "Busqueda encontrada exitosamente", - "data": [ { - "id": 3, - "name": "other", - "price": 100, - "created_at": "2018-11-21T12:06:04.065Z", - "updated_at": "2018-11-21T12:06:04.065Z", - "links": [ - { "rel": "self", "href": "/api/coffee/3", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/3/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/3/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }], - } - ``` -
- - -##### **Complex Queries:** -Here are some examples of how to work with more complex queries. In the query you should pass an array with two values, as such: key=["command",value]. The query will translate to SQL as follows `WHERE key command value`. For example: - - - price=["<>", 90] will translate to `WHERE price <> 90` - - price=["in", [90, 100]] will translate to `WHERE price in {90, 100}` - - price=["not in", [90, 100]] will translate to `WHERE price not in {90, 100}` - - It is very important for the brackets to be before and after every array, otherwise it will be parse as a string, for instance: - - price=">",90 will translate to `WHERE price = '">", 90'` - -Therefore, the following: - ```javascript - Requestify.get('http://localhost:3000/api/coffee/find?price=[">", 105]'); - ``` -
- - Will return an array of all the entries where price > 105 : -```javascript -{ -"message": "Busqueda encontrada exitosamente", -"data": [ { - "id": 4, - "name": "expensive", - "price": 110, - "created_at": "2018-11-21T12:06:22.400Z", - "updated_at": "2018-11-21T12:06:22.400Z", - "links": [ - { "rel": "self", "href": "/api/coffee/4", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/4/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/4/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }], -} -``` - -
- -And the following: - ```javascript - Requestify.get('http://localhost:3000/api/relation_name/find?price=["in",[110,100]]'); - ``` -
- - Will return an array of all the entries where price = 110 or price = 100 : -```javascript -{ -"message": "Busqueda encontrada exitosamente", -"data": [ { - "id": 1, - "name": "this is the name", - "price": 100, - "created_at": "2018-11-21T11:54:42.840Z", - "updated_at": "2018-11-21T11:54:42.840Z", - "links": [ - { "rel": "self", "href": "/api/coffee/1", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/1/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/1/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "id": 3, - "name": "other", - "price": 100, - "created_at": "2018-11-21T12:06:04.065Z", - "updated_at": "2018-11-21T12:06:04.065Z", - "links": [ - { "rel": "self", "href": "/api/coffee/3", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/3/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/3/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "id": 4, - "name": "expensive", - "price": 110, - "created_at": "2018-11-21T12:06:22.400Z", - "updated_at": "2018-11-21T12:06:22.400Z", - "links": [ - { "rel": "self", "href": "/api/coffee/4", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/4/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/4/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }], -} -``` -
- -##### **Raw Query:** - -With simple queries and complex queries you can work around most cases, however, sometimes its just not enough. Chinchay have a fully configurable querying option. You may add a _rawWhere_ to your query. Whatever you pass in the raw where will go explicitly as it is in the where, *be careful, it can lead to SQL injection*. To work with SQL injections you can also pass an array, where the first argument is the command and the second the values to insert. - -For instance, the following: - -```javascript -Requestify.get("http://localhost:3000/api/coffee/find?rawWhere=name = 'expensive' or name = 'other'"); -``` -
- -Will return all the entries where _name_ = 'other' or _name_ = 'expensive'. -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [ { - "id": 3, - "name": "other", - "price": 100, - "created_at": "2018-11-21T12:06:04.065Z", - "updated_at": "2018-11-21T12:06:04.065Z", - "links": [ - { "rel": "self", "href": "/api/coffee/3", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/3/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/3/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "id": 4, - "name": "expensive", - "price": 110, - "created_at": "2018-11-21T12:06:22.400Z", - "updated_at": "2018-11-21T12:06:22.400Z", - "links": [ - { "rel": "self", "href": "/api/coffee/4", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/4/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/4/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }], -} -``` -
- -However, if you want to specify in an array-format you could run the following command: - -And this will have the same return: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/find?rawWhere=["name = ? or name = ? ", ["expensive", "other"]]`); -``` -
- -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [ { - "id": 3, - "name": "other", - "price": 100, - "created_at": "2018-11-21T12:06:04.065Z", - "updated_at": "2018-11-21T12:06:04.065Z", - "links": [ - { "rel": "self", "href": "/api/coffee/3", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/3/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/3/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "id": 4, - "name": "expensive", - "price": 110, - "created_at": "2018-11-21T12:06:22.400Z", - "updated_at": "2018-11-21T12:06:22.400Z", - "links": [ - { "rel": "self", "href": "/api/coffee/4", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/4/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/4/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }], -} -``` -
- - - -##### **Columns:** - - -Sometimes we don't want to get all the information, just the essential stuff. The columns options comes handy. In an array you can specify all the columns you want to get. - -For instance, the following: - -```javascript -Requestify.get("http://localhost:3000/api/coffee/find?columns=name"); -``` -
- -Will return all the entries giving only the name. -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [{ - "name": "this is the name", - "links": [ - { "rel": "self", "href": "/api/coffee/:id", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/:id/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/:id/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "name": "this is the name", - "links": [ - { "rel": "self", "href": "/api/coffee/:id", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/:id/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/:id/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "name": "other", - "links": [ - { "rel": "self", "href": "/api/coffee/:id", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/:id/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/:id/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }, { - "name": "expensive", - "links": [ - { "rel": "self", "href": "/api/coffee/:id", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/:id/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/:id/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" } ], - }], -} -``` -
- -*NOTE:* Hateoas was unable to insert the id in the hyperlinks (href attribute) because the id column was not asked for. You need to ask for the id attribute for the correct link. For instance: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/find?columns=["id","name"]`); -``` -
- -Will return all the entries giving their name and id, with all the hateoas correctly compiled. - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [{ - "id": 1, - "name": "this is the name", - "links": [{ "rel": "self", "href": "/api/coffee/1", "type": "GET"}, - { "rel": "edit", "href": "/api/coffee/1/edit", "type": "POST"}, - { "rel": "delete", "href": "/api/coffee/1/delete", "type": "DELETE"}, - { "rel": "new", "href": "/api/coffee/new", "type": "POST"}, - { "rel": "all", "href": "/api/coffee/find", "type": "GET"}, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - }, - { - "id": 2, - "name": "this is the name", - "links": [{ "rel": "self", "href": "/api/coffee/2", "type": "GET"}, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST"}, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE"}, - { "rel": "new", "href": "/api/coffee/new", "type": "POST"}, - { "rel": "all", "href": "/api/coffee/find", "type": "GET"}, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - }, - { - "id": 3, - "name": "other", - "links": [{ "rel": "self", "href": "/api/coffee/3", "type": "GET"}, - { "rel": "edit", "href": "/api/coffee/3/edit", "type": "POST"}, - { "rel": "delete", "href": "/api/coffee/3/delete", "type": "DELETE"}, - { "rel": "new", "href": "/api/coffee/new", "type": "POST"}, - { "rel": "all", "href": "/api/coffee/find", "type": "GET"}, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - }, - { - "id": 4, - "name": "expensive", - "links": [{ "rel": "self", "href": "/api/coffee/4", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/4/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/4/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - } - ], -} -``` -
- -##### **Advance Options:** - -It just does not end here! There are some more options to do your querying even more complete! - -##### ** Start Date and End Date:** - -You can specify a range of dates to query. It will filter all the values where startDate < created_at < endDate. If startDate it is not defined it will query since dawn of time, whereas if endDate is not defined it will query till the end of time. - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/find?columns=["id","created_at"]&startDate=2018-11-21T11:55:00.000Z&endDate=2018-11-21T12:00:00.000Z`); -``` -
- -Will return all the entries created between 11:55 AM 21/11/2018 and 12:00 PM 21/11/2018. Note it will only return the id and created_at. - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [{ - "id": 2, - "created_at": "2018-11-21T11:57:02.767Z", - "links": [{ "rel": "self", "href": "/api/coffee/2", "type": "GET"}, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST"}, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE"}, - { "rel": "new", "href": "/api/coffee/new", "type": "POST"}, - { "rel": "all", "href": "/api/coffee/find", "type": "GET"}, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - }], -} -``` -
- -##### ** order By, limit and offset:** - -There are more options, for instance: -```javascript -Requestify.get(`http://localhost:3000/api/coffee/find?columns=["id"]&orderBy=id&limit=2`); -``` -
- -It will get the first two entries ids ordered by id. - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [{ - "id": 1, - "links": [{ "rel": "self", "href": "/api/coffee/1", "type": "GET"}, - { "rel": "edit", "href": "/api/coffee/1/edit", "type": "POST"}, - { "rel": "delete", "href": "/api/coffee/1/delete", "type": "DELETE"}, - { "rel": "new", "href": "/api/coffee/new", "type": "POST"}, - { "rel": "all", "href": "/api/coffee/find", "type": "GET"}, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - }, { - "id": 2, - "links": [{ "rel": "self", "href": "/api/coffee/2", "type": "GET"}, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST"}, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE"}, - { "rel": "new", "href": "/api/coffee/new", "type": "POST"}, - { "rel": "all", "href": "/api/coffee/find", "type": "GET"}, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - }], -} -``` -
- -Moreover, you can set orderBy as an array, not only specifying the column to order by but also if its ascending (asc) or descending (desc). To do so, orderBy is defined as column to order and then the direction that can either be: "asc" or "desc". for instance: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/find?columns=["id"]&orderBy=["id","desc"]&limit=2`); -``` -
- -It will get the second and third entries ids ordered by id in descending order: - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [{ - "id": 3, - "links": [{ "rel": "self", "href": "/api/coffee/3", "type": "GET"}, - { "rel": "edit", "href": "/api/coffee/3/edit", "type": "POST"}, - { "rel": "delete", "href": "/api/coffee/3/delete", "type": "DELETE"}, - { "rel": "new", "href": "/api/coffee/new", "type": "POST"}, - { "rel": "all", "href": "/api/coffee/find", "type": "GET"}, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - }, { - "id": 2, - "links": [{ "rel": "self", "href": "/api/coffee/2", "type": "GET"}, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST"}, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE"}, - { "rel": "new", "href": "/api/coffee/new", "type": "POST"}, - { "rel": "all", "href": "/api/coffee/find", "type": "GET"}, - { "rel": "count", "href": "/api/coffee/count", "type": "GET"}], - }], -} -``` -
- -This does not ends here, if you need to order by id decreasing and the by nace ascending, the following query will do the trick: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/find?columns=["id"]&orderBy=[["id","desc"],["name", "asc"]]&limit=2`); -``` -
- -You can order by as many variables as you need, just add one extra array indicating the variable name and order. - - -##### ** rawSelect:** - -RawSelect allows you to be even more specific on what you want to ask for. It can be given as a string or an array for sql injection. Fo instance: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/find?limit=1&rawSelect=EXTRACT(MONTH FROM created_at) as month`); -``` -
- -Will extract the month. Note it also brings all the attributes, if you just want to extract the month, you should also add the _clearSelect_ option. - -```javascript -{ -"message": "Busqueda encontrada exitosamente", -"data": [{ - "month": 11, - "id": 1, - "name": "this is the name", - "price": 100, - "created_at": "2018-11-21T11:54:42.840Z", - "updated_at": "2018-11-21T11:54:42.840Z", - "links": [ - { "rel": "self", "href": "/api/coffee/1", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/1/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/1/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }], -} -``` -
- -Same request, with clearSelect and with rawSelect as an array: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/find?limit=1&rawSelect=["EXTRACT(MONTH from ??) as month", "created_at"]&clearSelect=true`); -``` -
- -```javascript -{ -"message": "Busqueda encontrada exitosamente", -"data": [{ - "month": 11, - "links": [ - { "rel": "self", "href": "/api/coffee/:id", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/:id/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/:id/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - }], -} -``` -
- - -#### GET /relation_name/count {#count-api} - -##### **Description:** - -This would allow you to count how many entries are there. Extremely useful for analytics and key metrics. Most of what has been said for find applies to count requests, with simple Queries, Complex Queries, raw Query and advance options such us startDate, endDate and rawSelect. - -##### **Simple Queries:** - -```javascript -Requestify.get('http://localhost:3000/api/coffee/count?price=100'); -``` -
- -Will return: - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": "2" -} -``` -
- -##### **Complex Queries:** - -```javascript -Requestify.get('http://localhost:3000/api/coffee/count?price=[">", 105]'); -``` -
- -Returns: - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": 1 -} -``` -
- -##### **Raw Query:** - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/count?rawWhere=["name = ? or name = ? ", ["expensive", "other"]]`); -``` -
- -Returns: - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": 2 -} -``` -
- -##### **Advanced Options:** - -##### ** Start Date and End Date:** - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/count?startDate=2018-11-21T11:55:00.000Z&endDate=2018-11-21T12:00:00.000Z`); -``` -
- -Returns: - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": 1 -} -``` -
- -##### ** Group by and order By:** - -There is an extra option that was not present in the find examples: _groupBy_. You can group your answer according to an attribute. -For instance: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/count?groupBy=name`); -``` -
- -Will return how many entries are there per each name. - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [ - { - "count": 2, - "name": "this is the name" - }, - { - "count": 1, - "name": "other" - }, - { - "count": 1, - "name": "expensive" - }], -} -``` -
- -You can also order this answers: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/count?groupBy=name&orderBy=count`); -``` -
- -Will return the save results as before but ordered by count in ascending order. -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [{ - "count": 1, - "name": "other" - }, { - "count": 1, - "name": "expensive" - }, { - "count": 2, - "name": "this is the name" - }], -} -``` -
- -##### ** rawSelect:** - -There rawSelect option its also valid. However, be caution when using it because sometimes it will not be valid if its does not came with a group By. -Here an example: - -```javascript -Requestify.get(`http://localhost:3000/api/coffee/count?groupBy=minute&rawSelect=EXTRACT(minutes from created_at) as minutes`); -``` -
- -Will return how many entries were created grouped by the minute of there creation. - -```javascript -{ - "message": "Busqueda encontrada exitosamente", - "data": [{ - "count": 1, - "minutes": 57 - }, { - "count": 2, - "minutes": 6 - }, { - "count": 1, - "minutes": 54 - }], -} -``` -
- - -#### PUT PATCH POST /relation_name/:id/edit - -##### **Description:** -This URL can be called either with PUT, PATCH or POST. It receives a JSON object and, in the database, updates the values defined in the JSON for the entry with id = :id. It will response if it was successful the update and the entry updated. - -##### **Example:** - -The following: -```javascript -Requestify.post('http://localhost:3000/api/coffee/2/edit', {name: 'this is an updated name', price: 80}); -``` -
- -Will change in the database the entry with id = 2 in the relation relation_name the values with name and price to "this is an updated name" and 80. - -It will return the updated entry: - -```javascript -{ - "message": "Elemento actualizado exitosamente", - "data": { - "id": 2, - "name": "this is an updated name", - "price": 80, - "created_at": "2018-11-21T11:57:02.767Z", - "updated_at": "2018-12-12T11:52:32.750Z", - "links": [ - { "rel": "self", "href": "/api/coffee/2", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - } -} -``` -
- - -The following: -```javascript -Requestify.post('http://localhost:3000/api/coffee/2/edit', {price: 90}); -``` -
- -Will change in the database the entry with id = 2 in the relation relation_name the value price to 80 and leave the name intact. - -```javascript -{ - "message": "Elemento actualizado exitosamente", - "data": { - "id": 2, - "name": "this is an updated name", - "price": 90, - "created_at": "2018-11-21T11:57:02.767Z", - "updated_at": "2018-12-12T11:55:59.828Z", - "links": [ - { "rel": "self", "href": "/api/coffee/2", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - } -} -``` -
- - -#### DELETE /relation_name/:id - -In the database deletes the entry with id = :id. - -The following: -```javascript - Requestify.delete('http://localhost:3000/api/relation_name/2'); -``` -
- -Will delete in the database the entry with id = 2, and return the deleted element. - -```javascript -{ - "message": "Elemento eliminado exitosamente", - "data": { - "id": 2, - "name": "this is an updated name", - "price": 90, - "created_at": "2018-11-21T11:57:02.767Z", - "updated_at": "2018-12-12T11:55:59.828Z", - "links": [ - { "rel": "self", "href": "/api/coffee/2", "type": "GET" }, - { "rel": "edit", "href": "/api/coffee/2/edit", "type": "POST" }, - { "rel": "delete", "href": "/api/coffee/2/delete", "type": "DELETE" }, - { "rel": "new", "href": "/api/coffee/new", "type": "POST" }, - { "rel": "all", "href": "/api/coffee/find", "type": "GET" }, - { "rel": "count", "href": "/api/coffee/count", "type": "GET" }], - } -} -``` -
- -### Working with the generated web app: {#web-app} - -Chinchay gives you a fully-functional API to play around. But also it provides a web app to create, edit, and view your entries. This web app is just a frontend that makes API calls to the [generated API](#generated-api) - -#### web index - -If you navigate to [http://localhost:3000/relation_name](http://localhost:3000/relation_name) ([http://localhost:3000/coffee](http://localhost:3000/coffee) in our case) you will see a table were every row is one entry from the database. -On every column you can click [show](#web show) to view a page that render that particular entry or [edit](#web edit) to redirect to a page where you can edit that entry. - -On the bottom you have a [new](#web new) button to go and create a new entry! - -The file renderer is named index.ejs, view the [views section](#views) for more details. - -#### web new - -If you navigate to http://localhost:3000/relation_name/new you will see a form. It will have inputs to define all the columns of a given relation except for id, created_at and updated_at. Click save to save this entry! - -It will render the file named create.ejs, view the [views section](#views) for more details. - - -#### web show - -If you navigate to http://localhost:3000/relation_name/:id you will see all the information regarding the entry with id = :id. You can click [edit](#web edit) to edit that entry or [index](#web index) to go back to the index. - -It will render the file named show.ejs, view the [views section](#views) for more details. - -#### web edit - -If you navigate to http://localhost:3000/relation_name/:id/edit you will see a form to edit the information regarding the entry with id = :id. It is very similar to the form of [new](#web new), you will not be able to edit the id, created_at nor updated_at. You can click [update](#web edit) to edit that entry. - -It will render the file named edit.ejs, view the [views section](#views) for more details. - - -### The "new" Command {#new-command} - -This command will create a migration, a model, a controller, several views and two routes files for a given relation. Basically with just one command you are all set to for the CRUD (create, read, update, delete) of than relation. You can use it by running: - -``` -$ chinchay new relation_name -``` - -Where relation_name is the name of the relation you want to work with. - -#### Migration - -A migration file will be created on the directory specified in the knexfile. If you are unfamiliar of how knexfile or knex works see the [knex documentation](https://knexjs.org/). - -But, in a glance, knex uses migrations to make changes to the database schema. For every change you want to make, you create a migration file. -This file has two main methods: _up_ and _down_. The change to the database must be included in the _up_ method, whereas the _down_ method should contain code to reverse the change. Therefore you can go back and forth a migration running the _down_ and _up_ method. -The main commands that knex uses here are: - -``` -$ knex migrate:latest -``` -
-This will run all the _up_ methods of the migration files. It will register which migrations has ran, therefore if you create more migration and then run this command again, only the new migrations will be run. -``` -$ knex migrate:rollback -``` -
-This will run all the _down_ methods of the migration files. Its kind of an "undo" for the previous command. -``` -$ knex migrate:make migration_name -``` -
-This will create a new migration file called _migration_name_. - -When you run: -``` -$ chinchay new relation_name -``` -
-A migration file is created as if you had ran: -``` -$ knex migrate:make relation_name -``` -
- -By default this file is as follows: - -```javascript -exports.up = function (knex) { - return knex.schema.createTable('relation_name', (table) => { - // Incremental id - table.increments(); - - // created_at and updated_at - table.timestamps(); - }); -}; - -exports.down = function (knex) { - return knex.schema.dropTable('relation_name'); -}; -``` -
-It will create a table with an incremental id and a created_at and updated_at columns. You can edit this file as you wish, for example: - -```javascript -exports.up = function (knex) { - return knex.schema.createTable('relation_name', (table) => { - // Incremental id - table.increments(); - table.string('name').notNullable(); - table.integer('price'); - // created_at and updated_at - table.timestamps(); - }); -}; - -exports.down = function (knex) { - return knex.schema.dropTable('relation_name'); -}; -``` -
- -Go to the [knex documentation](https://knexjs.org/) for more info of how to work around migrations. - -Dont forget to run: `$ knex migrate:latest ` in order for the migration to take place! - -#### Routes - -The command will generate two files of routes to work around with the [web app](#web-app) and the [API](#generated-api). Both files are created within the directory specified in the [chainfile](#chainfile). By default, it will be in the directory: `./routes/` - -The file relationName.js contains all the routes for the [web app](#web-app) and relationNameAPI.js contains all the routes for the [API](#generated-api). - -Don't forget to include this files in the app.js file: - -```javascript -var relation_name = require('./routes/relation_name'); -var relation_nameAPI = require('./routes/relation_nameAPI'); -app.use('/', relation_name); -app.use('/', relation_nameAPI); -``` -
- -#### Views - -The command will generate a folder with four files to render the [web app](#web-app) and the [API](#generated-api). This folder will be named `relation_name` and created within the directory specified in the [chainfile](#chainfile). By default, it will be in the directory: `./views/`. - -Each files uses ejs to generate the html file. Feel free to visit the [ejs website](https://ejs.co/) for further information on how to work with ejs. But in a nutshell, it allows you to embed javascript within <%%> tags. - -The four files created are the following: - -1. create.js: File to render a form to create a new entry. As input it receives a JSON object representing an empty instance of the relation. Note line 12 is filtering that you cannot set the id, created_at nor updated_at. This is the file rendered when the [web new](#web new) URL is visited. -2. edit.js: File to render a form to edit a given entry. As input it receives a JSON object representing the object to edit. Note line 12 is filtering that you cannot set the id, created_at nor updated_at. This is the file rendered when the [web edit](#web edit) URL is visited. -3. index.js: File that shows a table with all the entries from the relation Render in [web index](#web index). Receives an Array of JSON objects, each object representing an entry on the database. This is the file rendered when the [web index](#web index) URL is visited. -4. show.js: File renders a table to show the value of every variable of the object. Receives a JSON object representing the entry to show. This is the file rendered when the [web show](#web show) URL is visited. -
- -#### Controller -The command will generate one controller file within the directory specified in the [chainfile](#chainfile). By default, it will be in the directory: `./controllers/`. This file _sticks_ all together. It is called from the [Router](#routes), then calls the [Model](#model) to get the requested information and then send the information back to the client either in the [views](#views) or in JSON objects. - -Before it calls the [Model](#model) it extract from the request what is being asked for. This information is passed in three different ways: - -1. Params: This are the _parameters_ of the request. To know there value you need to do: ` req.params `. They are defined within the URL. For instance, if the [show](#web_show) url is _/relation_name/:id_, meaning that to view the entry with id = 1 you have to visit the url _/relation_name/1_. The controller knows which entry to extract by looking in to the params, i.e: ` req.params.id `. -2. . Query: This is the _query_ of the request. To know its value you need to do: ` req.query `.This is the information given after the URL, it is separated from the URL by a '?' character. For instance: _/api/relation_name/find?price=100_ has the query: _price=100_. ThThe use of the query varies, but usually is used to filter what to be shown. -3. Body: This is the _body_ of the request. To know its value you need to do: ` req.body `. This are not defined in the URL, they are given in the body. Usually is used to give more sensible or complex information. Note than the GET request cannot receive a body property. - -It the case of the API calls: [find](#find-api), [find by id](#find-by-id-api) and [count](#count-api), the information is passed by the query and the its split into tree different objects: query, options and columns. If you are curious of what this is and how it works, visit the [Model Documentation]((/chinchay/models). - -After the models are called information is send back to the client. In the case of the [web app](#web-app) this is done by calling the method ` res.render() `. This function receives two parameters, the first one is the file's path. The second one is all the information you what to pass to the _ejs_ in order to render, for instance in the case of [web show](#web show) an object called _result_ is passed. This object is JSON representing the entry to be displayed. - -In the case of the [API](#generated-api) the information is passed out as a JSON. This JSON is build by the [httpResponse]((/codemaster/httpresponse). Feel free to visit its [documentation]((/codemaster/httpresponse) for more information. In a nutshell, the success functions receives three parameters, first a message to be given out, then the key used for the information and finally the values that key should have. Therefore: - -```javascript - httpResponse.success('This is the message', 'key', 'here goes the data'); -``` -
-Will return: -```javascript - { - message: 'This is the message', - key: 'here goes the data' - } -``` -
- -When an error occurs, the model response with an [Message object](/codemaster/message) of the [Codemaster](https://github.com/afontainec/codemaster) package. This message has three properties: - -1. code: It uses [the Http Codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) to inform what "type" of error. -2. message: A message to inform what is the error. -3. fullMessage: This is the complete error. - -With this message error, the response is build by calling httpResponse.error(..). For more information on this method visit [this link]((/codemaster/httpresponse). But in a nutshell, it receives two parameters. The first is the error to be informed to the client. Therefore it should be easy to understand for everyone. The second parameter is used for development environment. Its the full error that has been thrown. This is to help developers understand why it fell. However, in production environments, it will not be added. Therefore: - -```javascript - httpResponse.error('The entry you are looking does not exist', 'No entry with id = 3 was found in table "coffee"'); -``` -
-In an development environment will return: -```javascript - { - error: 'The entry you are looking does not exist', - fullError: 'No entry with id = 3 was found in table "coffee"' - } -``` -
- -Note that most of the api calls, also return a _links_ property. This links follows the [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) component of a [REST application architecture](https://en.wikipedia.org/wiki/Representational_state_transfer). This mean it gives out all the links necessary to browse through the API. You can see how they are defined in the `javascript initializeHATEOAS() ` function. For more information on this, feel free to go to the [HateoasGenerator documentation](/chinchay/hateoasgenerator). - - -#### Model - -Lastly, a model file is created. We *strongly* recommend to visit the [Model Documentation](/chinchay/models) to fully understand how to work with it. However, the file created its a [Singleton](https://www.dofactory.com/javascript/singleton-design-pattern) that manages all the queries to a certain relation. It extends the Table class and it has a constructor where it is specified the name of the table it should query to. If you need customize model functions, in this file you should add them. - -### .chainfile.js {#chainfile} - -This is the configuration file. Chinchay will provide a default file, therefore is _optional_. This file has the following structure: - -```javascript -const path = require('path'); - -module.exports = { - models: { - directory: path.join(process.cwd(), 'models') - }, - controllers: { - directory: path.join(process.cwd(), 'controllers') - }, - views: { - directory: path.join(process.cwd(), 'views') - }, - routes: { - directory: path.join(process.cwd(), 'routes') - }, - knex: path.join(process.cwd(), 'knex.js') -}; -``` - -* **models.directory:** Indicates in which directory the models will be saved. -* **controllers.directory:** Indicates in which directory the controllers will be saved. -* **routes.directory:** Indicates in which directory the routes will be saved. -* **knex:** Where knex is saved. It used to compute the path when requiring knex. diff --git a/docs/clitutorial.md b/docs/clitutorial.md deleted file mode 100644 index b0d2903c2..000000000 --- a/docs/clitutorial.md +++ /dev/null @@ -1,294 +0,0 @@ -## CLI Tutorial - -### Requirements - * [npm](https://www.npmjs.com/get-npm) - * [express](https://expressjs.com/) - * [Postgres](https://www.postgresql.org/) - - If you do not have express installed you can easily install it with npm -``` -$ npm install express -g -``` - -## Create nodejs app with express - -Create a nodejs app called: test_chinchay -``` -$ express test_chinchay && cd test_chinchay -``` -
-We will install drivers to use PostgresSQL database. we will use knexjs and pg - -``` -$ npm install pg -s -$ npm install knex -s -``` -
-Also we will use ejs instead of jade. So we need to run -``` -$ npm install ejs -s -``` -
-You can run the following commands to see the default express app -``` -$ npm install -$ npm start -``` -
-Visit [http://localhost:3000](http://localhost:3000) to see the defaut express web app - -## Create Postgresql Database - -In this tutorial we will not dig in how Postgres fully work. For more information on how to work around Postgres visit [https://www.postgresql.org/](https://www.postgresql.org/). - -In order to connect to Postgres, we need to create a database. If you have postgresql installed you can run -``` -$ psql -``` -
-This should open up postgresql console. Run the following command: - -``` -postgres=# CREATE DATABASE test_chinchay; -``` -*NOTE:* Depending on your default user and psql version the syntax of the previous line may vary. - -if its successful close psql, run: -``` -postgres=# \q -``` - - -## Connecting to the Database - -In this tutorial we will not dig in how knex fully work. For more information on how to work around knex [click here](https://knex.org/). - -First of all, we highly recommend to install knex globally: - -``` -$ npm install knex -g -``` -
-Until now we should have the following Directory Structure: - - . - ├── bin - ├── node_modules - ├── public - ├── routes - ├── views - ├── app.js - ├── package-lock.json - └── package.json - -We will add the following: - - . - ├── bin - ├── database - ├── migrations - └── seeds - ├── development - ├── production - └── test - ├── node_modules - ├── public - ├── routes - ├── views - ├── app.js - ├── knexfile.js - ├── knex.js - ├── package-lock.json - └── package.json - -
- -* database/migrations/ directory will hold all the migrations (changes) to the database. -* database/seed/ directory will hold all the seed files. Every subdirectory will hold the seed corresponding to that environment. -* knex.js Will be the instance to connect to the database. -
-Go ahead and create those files - -Before we continue we need to create a configuration file to let knex know how to interact with the database. We need to create a knexfile.js -``` -$ touch knexfile.js -``` -
-Add the following code to knexfile.js - -```javascript -const path = require('path'); - -module.exports = { - test: { - client: 'pg', - connection: 'postgres://localhost:5432/test_chinchay', - migrations: { - directory: path.join(__dirname, '/database/migrations'), - }, - seeds: { - directory: path.join(__dirname, '/database/seeds/test'), - }, - acquireConnectionTimeout: 10000, - }, - development: { - client: 'pg', - connection: 'postgres://localhost:5432/test_chinchay', - migrations: { - directory: path.join(__dirname, '/database/migrations'), - }, - seeds: { - directory: path.join(__dirname, '/database/seeds/development'), - }, - acquireConnectionTimeout: 10000, - }, - production: { - client: 'pg', - connection: process.env.DATABASE_URL || 'postgres://localhost:5432/test_chinchay', - migrations: { - directory: path.join(__dirname, '/database/migrations'), - }, - seeds: { - directory: path.join(__dirname, '/database/seeds/production'), - }, - acquireConnectionTimeout: 10000, - }, - staging: { - client: 'pg', - connection: process.env.DATABASE_URL || 'postgres://localhost:5432/test_chinchay', - migrations: { - directory: path.join(__dirname, '/database/migrations'), - }, - seeds: { - directory: path.join(__dirname, '/database/seeds/production'), - }, - acquireConnectionTimeout: 10000, - }, -}; - -``` -_NOTE:_ If your Postgres user it is not postgres change it accordingly in the connection URL. - -We will not get ni detail of how this file works, but basically we are telling knex were we want to save the migrations, the seeds and what is the url to connect to the database. Note that the knexfile defines this variables for every environment by separate. - - -Now we need to add the following code to the knex.js file: - -```javascript -const environment = process.env.NODE_ENV || 'development'; -const config = require('./knexfile')[environment]; -module.exports = require('knex')(config); -``` -
-Now knex is configured to connect to the database. - - -## Using Chinchay - -Now its the simple part. But before we need to create one last file: -* .chainfile.js: This file holds all of the configurations for chinchay. -
-Go ahead and create this file. - - -In the .chainfile.js add the following: - - -```javascript -const path = require('path'); - -module.exports = { - models: { - directory: path.join(__dirname, '/models'), - }, - controllers: { - directory: path.join(__dirname, '/controllers') - }, - views: { - directory: path.join(__dirname, '/views') - }, - routes: { - directory: path.join(__dirname, '/routes') - }, - knex: path.join(__dirname, 'knex.js') -}; -``` -
-Here we are defining which directories will hold the models, the controllers, the views and the routes. - -Install chinchay: -``` -$ npm install chinchay -s -$ npm install chinchay -g -``` -
-Installing chinchay globally will allow you to run chinchay CLI. - -Lets build a new relation called coffee and the files to create, view, update and delete entries to it: - -``` -$ chinchay new coffee -``` -
-This will create a model, a controllers, views, routes and a knex migration in the directories defined in .chainfile.js. - - -The migrations will be saved in the directory database/migrations/. The name will vary, as it takes the current date and time to make the file, but it will be appended by an coffee.js - -In this file insert the following: -```javascript -exports.up = function (knex) { - return knex.schema.createTable('coffee', (table) => { - // Incremental id - table.increments(); - table.string('name').notNullable(); - table.integer('price'); - // created_at and updated_at - table.timestamps(); - }); -}; - -exports.down = function (knex) { - return knex.schema.dropTable('coffee'); -}; -``` -
-This piece of code will create a relation called coffee within our database with the variables name and price. Also will generate a id and a created_at and updated_at timestamps for every entry. To run this migration: - -``` -$ knex migrate:latest -``` -
- -Last, but not least, i the app.js file, right after these lines: - -```javascript -app.use('/', routes); -app.use('/users', users); -``` - -add the following: - -```javascript -var coffee = require('./routes/coffee'); -var coffeeAPI = require('./routes/coffeeAPI'); -app.use('/', coffee); -app.use('/', coffeeAPI); -``` -
-This lines makes the app use the routes for the CRUD operations to the coffee relation. -
-Now run the app: -``` -$ npm start -``` -
-and visit [localhost:3000/coffee](localhost:3000/coffee) - -Click new to create a coffee! - -Enjoy! - -For more information to work around Chinchay CLI: - -[See the Command Line Interface Documentation!](https://afontainec.github.io/chinchay/clidocs) diff --git a/docs/clitutorialangular.md b/docs/clitutorialangular.md deleted file mode 100644 index 6af47ee6c..000000000 --- a/docs/clitutorialangular.md +++ /dev/null @@ -1,169 +0,0 @@ -## CLI Tutorial Chinchay + Angular - -### Overview - - For this tutorial we will have to servers running at the same time. One with the backend and another running the Angular app. - -## Backend Server - - For the backend server, follow [the cli tutorial](https://afontainec.github.io/chinchay/clitutorial). The only difference, is when running the chinchay new command, you can add the flag `--frontend disable` as follows: - -``` -$ chinchay new coffee --frontend disable -``` - - This will not create the ejs files. We will not be needing them, we are going to do the frontend with Angular. - - The only new difference, its we need to configure so we do not get blocked by CORS. If you do not know what this is you can read [this blog](https://www.codecademy.com/articles/what-is-cors), but in short, by default the server will block any request that coming from another app. Therefore, it will block the requests of the frontend. On the backend, we add the following to our app.js. Its important that this should be defined *BEFORE* we indicate the app to use the coffeeAPI routes. - - ```javascript - app.use((req, res, next) => { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); - next(); - }); - ``` - - Lets go and run the backend, run the following command on the backend directory: - -``` -$ npm start -``` - - -## Angular App - -Next, we need to create an Angular app. We need to install the [Angular cli](https://angular.io/cli). -If you do not have it go and run: - -``` -$ npm install -g @angular/cli -``` - -*NOTE*: You will need to have [npm](https://www.npmjs.com/get-npm) installed. - -Then we simply create the angular app called testChinchayAngular - -``` -$ ng new testChinchayAngular && cd testChinchayAngular -``` - - -A prompt asking if you like to add the angular routing will show, press y to confirm we will like to add it and then select the stylesheet format of your preference. We will use CSS. - -Next, we will add couple of modules. In the app.module.ts file, found within the src/app directory we will add the `FormsModule` and the `HttpClientModule` to the imports: - -```javascript -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { HttpClientModule } from '@angular/common/http'; -import { FormsModule } from '@angular/forms'; - -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; - -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ - FormsModule, - HttpClientModule, - BrowserModule, - AppRoutingModule - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } - -``` - -We are now ready to run our chinchay command. - - -## Chinchay - -We install chinchay: -``` -$ npm install chinchay -s -$ npm install chinchay -g -``` - -Then we simply run the command to create all the views and logic to work with the `coffee` relation of the backend: - -``` -$ chinchay new coffee --frontend angular --backend disable -``` - -Note we are indicating the frontend is angular and that no backend files should be created. - -This will create a coffee directory within src/app. This will include: - -* Index component: Where a list of all the coffees will display. -* New component: To create a new coffee. -* Edit component: To edit an existing coffee. -* Show component: To view a coffee and its properties. -* Service: For connecting with the backend, to create, edit or retrieve data. -* Router: Configuration for the routes. - -
- -## Connecting Backend and Frontend - -To connect the backend to the frontend we need to configure one more thing. In the file src/environments/environment.ts we need to add the variable `backend = http://localhost:3000`. This will indicate that the backend is running in the port 3000 of our machine. - -```javascript -export const environment = { - production: false, - backend: 'http://localhost:3000' -}; -``` - -Last, but not least we need to add the routes to the router. The router is in `src/app/app-routing.module.ts`. We import the routes generated by chinchay and concatenate them to the end of our routes. Note, the routes variable must not be a constant. - -```javascript -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; -import { coffeeRoutes } from './coffee/router'; - - -let routes: Routes = []; - -routes = routes.concat(coffeeRoutes); - -@NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] -}) -export class AppRoutingModule { } -``` - - - -## Running the app - -Now we are ready to run our app! We run: - - -``` -$ ng serve -``` - -This will make our app run on [localhost:4200](localhost:4200). - -Well, when you visit your app you get a Angular default webpage... To remove it, we go to `src/app/app.component.html` and leave only the router-outlet. - -```html - -``` - -Now we reload, and get an empty page. But lets navigate to [localhost:4200/coffee](localhost:4200) and we will see all our coffees! Go ahead and create more coffees! - -Important: remember that the backend must be running! - -Enjoy! - -For more information to work around Chinchay CLI: - -[See the Command Line Interface Documentation!](https://afontainec.github.io/chinchay/clidocs) diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index d8ba46b53..000000000 --- a/docs/index.md +++ /dev/null @@ -1,34 +0,0 @@ -# Chinchay ![CI status](https://img.shields.io/badge/build-passing-brightgreen.svg) - -Building a web app can be very time consuming and tedious, but Chinchay will help you speed up your development. Use Chichay CLI to automate the CRUD (Create, Read, Update, Delete) operations with a MVC (Model View Controller) architecture pattern. You can easily configure it and modify it. It you are a bit lost, Chinchay does provide a default Structure. - -But it does not stop there! Chinchay offers a fully featured, flexible and extendable Models to make quering to your Database easier. With no sql knowledge you can easily make complex queries and more. - - - -Any Contribution are welcome! - -* [Github Repository](https://github.com/afontainec/chinchay) -* [Issues](https://github.com/afontainec/chinchay/issues) -* [Pull Requests](https://github.com/afontainec/chinchay/pulls) - -For more information visit the [Chinchay website](https://afontainec.github.io/chinchay/clitutorial) - - -## Command Line Interface - -Use a Command Line Interface to make a web app that creates, read, updated and deletes entries from a Postgres Database. Do it only with one simple command: - -``` -$ chinchay new relation_name -``` - -Files Created are completely configurable. - -* [Go to a full tutorial, creating an app from cero](https://afontainec.github.io/chinchay/clitutorial) -* [Go to the Command Line Interface Documentation!](https://afontainec.github.io/chinchay/clidocs) - - -## Extendable Models - -Do not get stuck making complex SQL queries. Chinchay offers a fully featured, flexible and extendable Models to make quering easier to your Database easier. With no sql knowledge you can easily make complex queries and more. diff --git a/docs/models.md b/docs/models.md deleted file mode 100644 index 988552b62..000000000 --- a/docs/models.md +++ /dev/null @@ -1,36 +0,0 @@ -## Extendable Models Documentation - - -### Getting Started - -Explain how to create/extend a Table. - - -### Main Methods - -1. new -2. save -3. update -4. delete -5. deleteWhere -6. find -7. all -8. findById -9. findIn -10. findIdIn -11. count - - - - -### Other Methods - -1. getFirstDate -2. getAttributesNames - -### Static Methods - -1. extractOptions -2. removeRawOptions -3. extractColumns -4. extractSearch