## Getting Started ### Your First (simple) API Let's start with the simplest thing possible. We will expose some tables directly for reading and writing by anyone. Start by making a database ```sh createdb demo1 ``` We'll set it up with a film example (courtesy of [Jonathan Harrington](http://blog.jonharrington.org/postgrest-introduction/)). Copy the following into your clipboard: ```sql BEGIN; CREATE TABLE director ( name text NOT NULL PRIMARY KEY ); CREATE TABLE film ( id serial PRIMARY KEY, title text NOT NULL, year date NOT NULL, director text, rating real NOT NULL DEFAULT 0, language text NOT NULL, CONSTRAINT film_director_fkey FOREIGN KEY (director) REFERENCES director (name) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE ); CREATE TABLE festival ( name text NOT NULL PRIMARY KEY ); CREATE TABLE competition ( id serial PRIMARY KEY, name text NOT NULL, festival text NOT NULL, year date NOT NULL, CONSTRAINT comp_festival_fkey FOREIGN KEY (festival) REFERENCES festival (name) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE ); CREATE TABLE film_nomination ( id serial PRIMARY KEY, competition integer NOT NULL, film integer NOT NULL, won boolean NOT NULL DEFAULT true, CONSTRAINT nomination_competition_fkey FOREIGN KEY (competition) REFERENCES competition (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT nomination_film_fkey FOREIGN KEY (film) REFERENCES film (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE ); COMMIT; ``` Apply it to your new database by running ```sh # On OS X pbpaste | psql demo1 # Or Linux # xclip -selection clipboard -o | psql demo1 ``` Start the PostgREST server and point it at the new database. ```sh postgrest -d demo1 -U postgres -a postgres --v1schema public ```

Note about database users

If you installed PostgreSQL with Homebrew on Mac then the database username may be your own login rather than postgres.

Let's use PostgREST to populate the database. Install a REST client such as [Postman](https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en). Now let's insert some data as a bulk post in CSV format: ```HTTP POST http://localhost:3000/festival Content-Type: text/csv name Venice Film Festival Cannes Film Festival ``` In Postman it will look like this ![Festival bulk insert in postman](/img/post-festivals.png) Notice that the post type is `raw` and that `Content-Type: text/csv` set in the Headers tab. Note that the server returns a multipart response with URL of each created resource. ```HTTP Content-Type: application/json Location: /festival?name=eq.Venice%20Film%20Festival --postgrest_boundary Content-Type: application/json Location: /festival?name=eq.Cannes%20Film%20Festival ``` If you send a GET request to `/festival` it should return ```json [ { "name": "Venice Film Festival" }, { "name": "Cannes Film Festival" } ] ``` Now that you've seen how to do a bulk insert, let's do some more and fully populate the database. Post the following to `/competition`: ```csv name,festival,year Golden Lion,Venice Film Festival,2014-01-01 Palme d'Or,Cannes Film Festival,2014-01-01 ``` Now `/director`: ```csv name Bertrand Bonello Atom Egoyan David Gordon Green Andrey Konchalovskiy Mario Martone Mike Leigh Roy Andersson Saverio Costanzo Alix Delaporte Jean-Pierre Dardenne Xiaoshuai Wang Kaan Müjdeci Tommy Lee Jones Nuri Bilge Ceylan Michel Hazanavicius Xavier Dolan Ramin Bahrani Alice Rohrwacher Andrew Niccol Rakhshan Bani-Etemad David Oelhoffen Bennett Miller David Cronenberg Shin'ya Tsukamoto Joshua Oppenheimer Olivier Assayas Jean-Luc Godard Alejandro González Iñárritu Benoît Jacquot Fatih Akin Francesco Munzi Ken Loach Abel Ferrara Xavier Beauvois Naomi Kawase ``` And `/film`: ```csv title,year,director,rating,language Chuang ru zhe,2014-01-01,Xiaoshuai Wang,6.19999981,english The Look of Silence,2014-01-01,Joshua Oppenheimer,8.30000019,Indonesian Fires on the Plain,2014-01-01,Shin'ya Tsukamoto,5.80000019,Japanese Far from Men,2014-01-01,David Oelhoffen,7.5,english Good Kill,2014-01-01,Andrew Niccol,6.0999999,english Leopardi,2014-01-01,Mario Martone,6.9000001,english Sivas,2014-01-01,Kaan Müjdeci,7.69999981,english Black Souls,2014-01-01,Francesco Munzi,7.0999999,english Three Hearts,2014-01-01,Benoît Jacquot,5.80000019,French Pasolini,2014-01-01,Abel Ferrara,5.80000019,english Le dernier coup de marteau,2014-01-01,Alix Delaporte,6.5,english Manglehorn,2014-01-01,David Gordon Green,7.0999999,english Hungry Hearts,2014-01-01,Saverio Costanzo,6.4000001,English Belye nochi pochtalona Alekseya Tryapitsyna,2014-01-01,Andrey Konchalovskiy,6.9000001,Russian 99 Homes,2014-01-01,Ramin Bahrani,7.30000019,english The Cut,2014-01-01,Fatih Akin,6,Armenian Birdman: Or (The Unexpected Virtue of Ignorance),2014-01-01,Alejandro González Iñárritu,8,English La rançon de la gloire,2014-01-01,Xavier Beauvois,5.69999981,French A Pigeon Sat on a Branch Reflecting on Existence,2014-01-01,Roy Andersson,7.19999981,english Tales,2014-01-01,Rakhshan Bani-Etemad,6.80000019,english The Wonders,2014-01-01,Alice Rohrwacher,6.80000019,Italian Foxcatcher,2014-01-01,Bennett Miller,7.19999981,English Mr. Turner,2014-01-01,Mike Leigh,7,English Jimmy's Hall,2014-01-01,Ken Loach,6.69999981,English The Homesman,2014-01-01,Tommy Lee Jones,6.5999999,English The Captive,2014-01-01,Atom Egoyan,5.9000001,english Goodbye to Language,2014-01-01,Jean-Luc Godard,6.19999981,French The Search,2014-01-01,Michel Hazanavicius,6.9000001,French Still the Water,2014-01-01,Naomi Kawase,6.9000001,Japanese Mommy,2014-01-01,Xavier Dolan,8.30000019,French "Two Days, One Night",2014-01-01,Jean-Pierre Dardenne,7.4000001,French Maps to the Stars,2014-01-01,David Cronenberg,6.4000001,English Saint Laurent,2014-01-01,Bertrand Bonello,6.5,French Clouds of Sils Maria,2014-01-01,Olivier Assayas,6.9000001,english Winter Sleep,2014-01-01,Nuri Bilge Ceylan,8.5,Turkish ``` Finally `/film_nomination`: ```csv competition,film,won 1,1,f 1,2,f 1,3,f 1,4,f 1,5,f 1,6,f 1,7,f 1,8,f 1,9,f 1,10,f 1,11,f 1,12,f 1,13,f 1,14,f 1,15,f 1,16,f 1,17,f 1,18,f 1,19,f 1,20,f 2,21,f 2,22,f 2,23,f 2,24,f 2,25,f 2,26,f 2,27,f 2,28,f 2,29,f 2,30,f 2,31,f 2,32,f 2,33,f 2,34,f 2,35,f ``` At this point nominations are fully specified but it's not a convenient interface for a rest client. Let's make a view they can use. Paste this into `psql demo1`. ```sql create or replace view nomination as select comp.festival, comp.name as competition, comp.year, film.title, film.director, film.rating from film_nomination as nom left join film on nom.film = film.id left join competition as comp on nom.competition = comp.id order by comp.year desc, comp.festival, competition; ``` Time to try it out. Let's get the contents of the new view, ordered by film rating ``` GET http://localhost:3000/nomination?order=rating.desc ``` If you find it more human readable, add an `Accept: text/csv` header. ### Releasing a New Version Suppose we want this endpoint to cater to those moviegoers with attention deficit disorder. In today's busy world we don't have time to read an extra couple words or compare nuanced reviews. In API version two we will truncate the names and round the ratings! Each version lives in a numbered schema, so let's make a schema for version two. ```sql CREATE SCHEMA "2"; GRANT USAGE ON SCHEMA "2" TO PUBLIC; ALTER DATABASE demo1 SET search_path = "2", "public"; ``` To override the `films` endpoint create a view in the "2" schema with that name: ```sql create or replace view "2".film as select id, substring(f.title from 1 for 10) as title, year, director, round(f.rating) as rating, language from "public".film as f; ``` We select the desired version as part of content negotiation. Try this get request: ```HTTP GET http://localhost:3000/film Accept: text/csv; version=2 ``` Then try toggling the version string in the Accept header and watch the results change. Pretty good, now how about writing values? PostgreSQL's nice feature called auto-updatable views allows writes to pass through views. Sadly this view is not eligible because truncation and rounding cannot be uniquely reversed. If we attempt to post a new result it complains: ```json { "hint": null, "details": "View columns that are not columns of their base relation are not updatable.", "code": "0A000", "message": "cannot insert into column \"title\" of view \"film\"" } ``` This is a case where we need explicit triggers ```sql -- TODO - FIX THIS -- CREATE OR REPLACE RULE insert_v2_films AS -- ON INSERT TO "2".film -- DO INSTEAD -- INSERT INTO public.film (id, title, year, director, rating, language) -- VALUES (NEW.id, NEW.title, -- NEW.year, NEW.director, -- NEW.rating, NEW.language) -- RETURNING public.film.*; ```