diff --git a/README.md b/README.md index d668655..279e892 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,29 @@ - + -# Super Graph - Instant GraphQL API for Rails +# Super Graph - Build web products faster. Instant GraphQL APIs for your apps ![MIT license](https://img.shields.io/github/license/dosco/super-graph.svg) ![Docker build](https://img.shields.io/docker/cloud/build/dosco/super-graph.svg) ![Cloud native](https://img.shields.io/badge/cloud--native-enabled-blue.svg) -Get an high-performance GraphQL API for your Rails app in seconds without writing a line of code. Super Graph will auto-learn your database structure and relationships. Built in support for Rails authentication and JWT tokens. +Get an instant high performance GraphQL API for Postgres. No code needed. GraphQL is automatically transformed into efficient database queries. -![Super Graph Web UI](docs/.vuepress/public/super-graph-web-ui.png?raw=true "Super Graph Web UI for web developers") +![GraphQL](docs/.vuepress/public/graphql.png?raw=true "") -## Why I built Super Graph? +## The story of Super Graph? -Honestly, cause it was more fun than my real work. After working on several product though my career I found myself hating building CRUD APIs (Create, Update, Delete, List, Show). It was always the same thing figure out what the UI needs then build an endpoint for it, if related data is needed than join with another table. I didn't want to write that code anymore I wanted the computer to just do it. +After working on several products through my career I find that we spend way too much time on building API backends. Most APIs also require constant updating, this costs real time and money. + +It's always the same thing, figure out what the UI needs then build an endpoint for it. Most API code involves struggling with an ORM to query a database and mangle the data into a shape that the UI expects to see. -I always liked GraphQL it sounded friendly, but it still required me to write all the same database query code. Sure the API was nicer but it took a lot of work sometime even more than a simple REST API would have. I wanted a GraphQL server that just worked the second you deployed it without having to write a line of code. +I didn't want to write this code anymore, I wanted the computer to do it. Enter GraphQL, to me it sounded great, but it still required me to write all the same database query code. -And so after a lot of coffee and some Avocado toasts __Super Graph was born, a GraphQL server that just works, is high performance and easy to deploy__. I hope you find it as useful as I do and there's a lot more coming so hit that :star: to stay in the loop. +Having worked with compilers before I saw this as a compiler problem. Why not build a compiler that converts GraphQL to highly efficient SQL. + +This compiler is what sits at the heart of Super Graph with layers of useful functionality around it like authentication, remote joins, rails integration, database migrations and everything else needed for you to build production ready apps with it. ## Features + - Works with Rails database schemas - Automatically learns schemas and relationships - Belongs-To, One-To-Many and Many-To-Many table relationships @@ -27,14 +32,12 @@ And so after a lot of coffee and some Avocado toasts __Super Graph was born, a G - JWT tokens supported (Auth0, etc) - Join with remote REST APIs - Highly optimized and fast Postgres SQL queries +- Support GraphQL queries and mutations - Configure with a simple config file - High performance GO codebase - Tiny docker image and low memory requirements - -## Watch some talks - -[![Watch the video](https://img.youtube.com/vi/TGq9wJAj78I/hqdefault.jpg)](https://youtu.be/TGq9wJAj78I) - +- Database migrations tool +- Write database seeding scripts in Javascript ## Documentation diff --git a/config/allow.list b/config/allow.list index 7858bd4..480c585 100644 --- a/config/allow.list +++ b/config/allow.list @@ -1,5 +1,44 @@ # http://localhost:8080/ +query { + me { + id + email + full_name + } +} + +variables { + "update": { + "name": "Hellooooo", + "description": "World", + "created_at": "now", + "updated_at": "now" + }, + "user": 123 +} + +mutation { + products(update: $update, where: {id: {eq: 134}}) { + id + name + description + } +} + +query { + users { + id + email + picture: avatar + products(limit: 2, where: {price: {gt: 10}}) { + id + name + description + } + } +} + variables { "update": { "name": "Hellooooo", @@ -33,37 +72,16 @@ mutation { } query { - me { + users { id email - full_name + picture: avatar + products(limit: 2, where: {price: {gt: 10}}) { + id + name + description + } } } -variables { - "update": { - "name": "Hellooooo", - "description": "World", - "created_at": "now", - "updated_at": "now" - }, - "user": 123 -} - -mutation { - products(update: $update, where: {id: {eq: 134}}) { - id - name - description - } -} - -query { - me { - id - email - full_name - } -} - diff --git a/docker-compose.yml b/docker-compose.yml index ea51297..bf0426a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,16 +11,16 @@ services: # ports: # - "6379:6379" - # rails_app: - # build: rails-app/. - # command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" - # volumes: - # - ./rails-app:/app - # - /app/tmp - # ports: - # - "3000:3000" - # depends_on: - # - db + rails_app: + build: rails-app/. + command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" + volumes: + - ./rails-app:/app + - /app/tmp + ports: + - "3000:3000" + depends_on: + - db super_graph: build: @@ -37,6 +37,6 @@ services: command: wu -pattern="*.go" go run main.go serv depends_on: - db - #- rails_app + - rails_app # - redis diff --git a/docs/.vuepress/components/HomeLayout.vue b/docs/.vuepress/components/HomeLayout.vue index fc7b795..794a0ea 100644 --- a/docs/.vuepress/components/HomeLayout.vue +++ b/docs/.vuepress/components/HomeLayout.vue @@ -51,21 +51,122 @@ -
+

What is {{ data.heroText }}?

- {{ data.description }} + Super Graph can automatically learn a Postgres database and instantly serve it as a fast and secured GraphQL API. It comes with tools to create a new app and manage it's database. You get it all, a very productive developer and a highly scalable app backend. It's designed to work well on serverless platforms by Google, AWS, Microsoft, etc. The goal is to save you a ton of time and money so you can focus on you're apps core value.
-
- +
+
+ +
+ +
+ +
- + +
+
+

+ How to use {{ data.heroText }}? +

+
+ Use the below command to download and install Super Graph. You will need Go 1.13 or above +
‣ GO111MODULE=on go get -u github.com/dosco/super-graph
+ + Create a new app and change to it's directory +
‣ super-graph new blog; cd blog
+ + Setup the app database and seed it with fake data. Docker compose will start a Postgres database for your app +
‣ docker-compose run blog_api ./super-graph db:setup
+ + And finally launch Super Graph configured for your app +
‣ docker-compose up
+
+
+
+ +
+
+

+ The story of {{ data.heroText }} +

+
+ After working on several products through my career I find that we spend way too much time on building API backends. Most APIs also require constant updating, this costs real time and money.

+ + It's always the same thing, figure out what the UI needs then build an endpoint for it. Most API code involves struggling with an ORM to query a database and mangle the data into a shape that the UI expects to see.

+ + I didn't want to write this code anymore, I wanted the computer to do it. Enter GraphQL, to me it sounded great, but it still required me to write all the same database query code.

+ + Having worked with compilers before I saw this as a compiler problem. Why not build a compiler that converts GraphQL to highly efficient SQL.

+ + This compiler is what sits at the heart of Super Graph with layers of useful functionality around it like authentication, remote joins, rails integration, database migrations and everything else needed for you to build production ready apps with it. +
+
+
+ + + +
+
+

+ Try it with a demo Rails app +

+
+ Download the Docker compose config for the demo +
‣ curl -o demo.yml https://bit.ly/2mq05lW
+ + Setup the demo database +
‣ docker-compose -f demo.yml run rails_app rake db:create db:migrate db:seed
+ + Run the demo +
‣ docker-compose -f demo.yml up
+ + Signin to the demo app (user1@demo.com / 123456) +
‣ open http://localhost:3000
+ + Try the super graph web ui +
‣ open http://localhost:8080
+ +
+            ## Try a query
+            query {
+              users {
+                id
+                email
+                picture : avatar
+                products(limit: 2, where: { price: { gt: 10 } }) {
+                  id
+                  name
+                  description
+                }
+              }
+            }
+            
+
+
+
+ +
+
+ +
+ +
+
+
+
'pg_catalog' - AND n.nspname <> 'information_schema' - AND n.nspname !~ '^pg_toast' + AND n.nspname <> ('pg_catalog') + AND n.nspname <> ('information_schema') + AND n.nspname !~ ('^pg_toast') AND pg_catalog.pg_table_is_visible(c.oid);` var tables []*DBTable @@ -53,14 +53,14 @@ AND pg_catalog.pg_table_is_visible(c.oid);` } type DBColumn struct { - ID int + ID int16 Name string Type string NotNull bool PrimaryKey bool UniqueKey bool FKeyTable string - FKeyColID []int + FKeyColID []int16 fKeyColID pgtype.Int2Array } @@ -106,7 +106,7 @@ ORDER BY id;` } defer rows.Close() - cmap := make(map[int]*DBColumn) + cmap := make(map[int16]*DBColumn) for rows.Next() { c := DBColumn{} @@ -115,7 +115,6 @@ ORDER BY id;` if err != nil { return nil, err } - c.fKeyColID.AssignTo(&c.FKeyColID) if v, ok := cmap[c.ID]; ok { if c.PrimaryKey { @@ -128,6 +127,10 @@ ORDER BY id;` v.UniqueKey = true } } else { + err := c.fKeyColID.AssignTo(&c.FKeyColID) + if err != nil { + return nil, err + } cmap[c.ID] = &c } } @@ -208,7 +211,7 @@ func (s *DBSchema) updateSchema( aliases map[string][]string) { // Foreign key columns in current table - colByID := make(map[int]*DBColumn) + colByID := make(map[int16]*DBColumn) columns := make(map[string]*DBColumn, len(cols)) colNames := make([]string, 0, len(cols)) @@ -309,7 +312,7 @@ func (s *DBSchema) updateSchema( func (s *DBSchema) updateSchemaOTMT( ct string, col1, col2 *DBColumn, - colByID map[int]*DBColumn) { + colByID map[int16]*DBColumn) { t1 := strings.ToLower(col1.FKeyTable) t2 := strings.ToLower(col2.FKeyTable) diff --git a/rails-app/Gemfile b/rails-app/Gemfile index f823a14..a4a4dc8 100644 --- a/rails-app/Gemfile +++ b/rails-app/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby '2.5.5' +ruby '2.5.6' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 6.0.0.rc1' diff --git a/rails-app/Gemfile.lock b/rails-app/Gemfile.lock index f739603..e69de29 100644 --- a/rails-app/Gemfile.lock +++ b/rails-app/Gemfile.lock @@ -1,301 +0,0 @@ -GIT - remote: https://github.com/stympy/faker.git - revision: 32bac72d67d275bfb26ee7049efe5f3489fff3d0 - branch: master - specs: - faker (1.9.3) - i18n (>= 0.7) - pastel (~> 0.7.2) - thor (~> 0.20.0) - tty-pager (~> 0.12.0) - tty-screen (~> 0.6.5) - tty-tree (~> 0.2.0) - -GEM - remote: https://rubygems.org/ - specs: - actioncable (6.0.0.rc1) - actionpack (= 6.0.0.rc1) - nio4r (~> 2.0) - websocket-driver (>= 0.6.1) - actionmailbox (6.0.0.rc1) - actionpack (= 6.0.0.rc1) - activejob (= 6.0.0.rc1) - activerecord (= 6.0.0.rc1) - activestorage (= 6.0.0.rc1) - activesupport (= 6.0.0.rc1) - mail (>= 2.7.1) - actionmailer (6.0.0.rc1) - actionpack (= 6.0.0.rc1) - actionview (= 6.0.0.rc1) - activejob (= 6.0.0.rc1) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (6.0.0.rc1) - actionview (= 6.0.0.rc1) - activesupport (= 6.0.0.rc1) - rack (~> 2.0) - rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actiontext (6.0.0.rc1) - actionpack (= 6.0.0.rc1) - activerecord (= 6.0.0.rc1) - activestorage (= 6.0.0.rc1) - activesupport (= 6.0.0.rc1) - nokogiri (>= 1.8.5) - actionview (6.0.0.rc1) - activesupport (= 6.0.0.rc1) - builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (6.0.0.rc1) - activesupport (= 6.0.0.rc1) - globalid (>= 0.3.6) - activemodel (6.0.0.rc1) - activesupport (= 6.0.0.rc1) - activerecord (6.0.0.rc1) - activemodel (= 6.0.0.rc1) - activesupport (= 6.0.0.rc1) - activestorage (6.0.0.rc1) - actionpack (= 6.0.0.rc1) - activejob (= 6.0.0.rc1) - activerecord (= 6.0.0.rc1) - marcel (~> 0.3.1) - activesupport (6.0.0.rc1) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - zeitwerk (~> 2.1, >= 2.1.4) - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) - archive-zip (0.12.0) - io-like (~> 0.3.0) - bcrypt (3.1.12) - bindex (0.5.0) - bootsnap (1.4.1) - msgpack (~> 1.0) - builder (3.2.3) - byebug (11.0.1) - capybara (3.15.0) - addressable - mini_mime (>= 0.1.3) - nokogiri (~> 1.8) - rack (>= 1.6.0) - rack-test (>= 0.6.3) - regexp_parser (~> 1.2) - xpath (~> 3.2) - childprocess (0.9.0) - ffi (~> 1.0, >= 1.0.11) - chromedriver-helper (2.1.0) - archive-zip (~> 0.10) - nokogiri (~> 1.8) - coffee-rails (4.2.2) - coffee-script (>= 2.2.0) - railties (>= 4.0.0) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) - concurrent-ruby (1.1.5) - crass (1.0.4) - devise (4.6.1) - bcrypt (~> 3.0) - orm_adapter (~> 0.1) - railties (>= 4.1.0, < 6.0) - responders - warden (~> 1.2.3) - equatable (0.5.0) - erubi (1.8.0) - execjs (2.7.0) - ffi (1.10.0) - globalid (0.4.2) - activesupport (>= 4.2.0) - i18n (1.6.0) - concurrent-ruby (~> 1.0) - io-like (0.3.0) - jbuilder (2.8.0) - activesupport (>= 4.2.0) - multi_json (>= 1.2) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - loofah (2.2.3) - crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) - mini_mime (>= 0.1.1) - marcel (0.3.3) - mimemagic (~> 0.3.2) - method_source (0.9.2) - mimemagic (0.3.3) - mini_mime (1.0.1) - mini_portile2 (2.4.0) - minitest (5.11.3) - msgpack (1.2.9) - multi_json (1.13.1) - nio4r (2.3.1) - nokogiri (1.10.3) - mini_portile2 (~> 2.4.0) - orm_adapter (0.5.0) - pastel (0.7.2) - equatable (~> 0.5.0) - tty-color (~> 0.4.0) - pg (1.1.4) - public_suffix (3.0.3) - puma (3.12.1) - rack (2.0.7) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (6.0.0.rc1) - actioncable (= 6.0.0.rc1) - actionmailbox (= 6.0.0.rc1) - actionmailer (= 6.0.0.rc1) - actionpack (= 6.0.0.rc1) - actiontext (= 6.0.0.rc1) - actionview (= 6.0.0.rc1) - activejob (= 6.0.0.rc1) - activemodel (= 6.0.0.rc1) - activerecord (= 6.0.0.rc1) - activestorage (= 6.0.0.rc1) - activesupport (= 6.0.0.rc1) - bundler (>= 1.3.0) - railties (= 6.0.0.rc1) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) - nokogiri (>= 1.6) - rails-html-sanitizer (1.0.4) - loofah (~> 2.2, >= 2.2.2) - railties (6.0.0.rc1) - actionpack (= 6.0.0.rc1) - activesupport (= 6.0.0.rc1) - method_source - rake (>= 0.8.7) - thor (>= 0.20.3, < 2.0) - rake (12.3.2) - rb-fsevent (0.10.3) - rb-inotify (0.10.0) - ffi (~> 1.0) - redis (4.1.0) - redis-actionpack (5.0.2) - actionpack (>= 4.0, < 6) - redis-rack (>= 1, < 3) - redis-store (>= 1.1.0, < 2) - redis-activesupport (5.0.7) - activesupport (>= 3, < 6) - redis-store (>= 1.3, < 2) - redis-rack (2.0.5) - rack (>= 1.5, < 3) - redis-store (>= 1.2, < 2) - redis-rails (5.0.2) - redis-actionpack (>= 5.0, < 6) - redis-activesupport (>= 5.0, < 6) - redis-store (>= 1.2, < 2) - redis-store (1.6.0) - redis (>= 2.2, < 5) - regexp_parser (1.3.0) - responders (2.4.1) - actionpack (>= 4.2.0, < 6.0) - railties (>= 4.2.0, < 6.0) - ruby_dep (1.5.0) - rubyzip (1.2.2) - sass (3.7.3) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - sass-rails (5.0.7) - railties (>= 4.0.0, < 6) - sass (~> 3.1) - sprockets (>= 2.8, < 4.0) - sprockets-rails (>= 2.0, < 4.0) - tilt (>= 1.1, < 3) - selenium-webdriver (3.141.0) - childprocess (~> 0.5) - rubyzip (~> 1.2, >= 1.2.2) - spring (2.0.2) - activesupport (>= 4.2) - spring-watcher-listen (2.0.1) - listen (>= 2.7, < 4.0) - spring (>= 1.2, < 3.0) - sprockets (3.7.2) - concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.1) - actionpack (>= 4.0) - activesupport (>= 4.0) - sprockets (>= 3.0.0) - strings (0.1.4) - strings-ansi (~> 0.1.0) - unicode-display_width (~> 1.4.0) - unicode_utils (~> 1.4.0) - strings-ansi (0.1.0) - thor (0.20.3) - thread_safe (0.3.6) - tilt (2.0.9) - tty-color (0.4.3) - tty-pager (0.12.1) - strings (~> 0.1.4) - tty-screen (~> 0.6) - tty-which (~> 0.4) - tty-screen (0.6.5) - tty-tree (0.2.0) - tty-which (0.4.0) - turbolinks (5.2.0) - turbolinks-source (~> 5.2) - turbolinks-source (5.2.0) - tzinfo (1.2.5) - thread_safe (~> 0.1) - uglifier (4.1.20) - execjs (>= 0.3.0, < 3) - unicode-display_width (1.4.1) - unicode_utils (1.4.0) - warden (1.2.8) - rack (>= 2.0.6) - web-console (3.7.0) - actionview (>= 5.0) - activemodel (>= 5.0) - bindex (>= 0.4.0) - railties (>= 5.0) - websocket-driver (0.7.0) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.3) - xpath (3.2.0) - nokogiri (~> 1.8) - zeitwerk (2.1.6) - -PLATFORMS - ruby - -DEPENDENCIES - bootsnap (>= 1.1.0) - byebug - capybara (>= 2.15) - chromedriver-helper - coffee-rails (~> 4.2) - devise - faker! - jbuilder (~> 2.5) - listen (>= 3.0.5, < 3.2) - pg (>= 0.18, < 2.0) - puma (~> 3.11) - rails (~> 6.0.0.rc1) - redis-rails - sass-rails (~> 5.0) - selenium-webdriver - spring - spring-watcher-listen (~> 2.0.0) - turbolinks (~> 5) - tzinfo-data - uglifier (>= 1.3.0) - web-console (>= 3.3.0) - -RUBY VERSION - ruby 2.5.5p157 - -BUNDLED WITH - 1.17.3