john-kelly / elm-postgrest Goto Github PK
View Code? Open in Web Editor NEWMake PostgREST requests in Elm
Make PostgREST requests in Elm
Mostly field names will match record fields, but sometimes they might not, so having an ability to name them differently to the name of the field in the Elm record could be useful.
https://groups.google.com/forum/#!topic/elm-discuss/igxYW0Q3Clw
although this is not yet added -- we should start thinking about the API changes necessary for this:
The current plan is to release the next version of the library in sync with the 0.19 release of Elm.
With that being said, there are still a few things I'd like to finish before an official release:
Additionally, there are a few issue we may want to consider/think about before the next release. At the very least we should examine if they are candidates for non major bump updates:
At the time of writing, my focus is on documentation.
now:
type Limit
= Limit (Maybe Int)
proposed:
type Limit
= NoLimit
| LimitTo Int
https://facebook.github.io/graphql/#sec-Language.Operations
maybe Operations are what we can turn into requests! and they require a token and a url
Operation
Query
Mutation
1.) Schema represents from json -> to elm type
2.) Schema represents just from json
I think 2 (based on convo with elm-graphql guy)
Based on this conversation, how should things be named?
https://en.wikipedia.org/wiki/Relation_(database)
https://docs.djangoproject.com/en/1.10/ref/models/fields/
http://www.django-rest-framework.org/api-guide/schemas/
https://hexdocs.pm/ecto/Ecto.Schema.html
http://graphql.org/learn/schema/
{-| Is comparison
-}
is : Maybe Bool -> (schema -> Field (Maybe Bool)) -> schema -> Filter
is =
singleValueFilterFn Is
fetchSchools =
PG.query Schema.school School
|> PG.select .name
|> PG.select .streetAddress
|> PG.select .city
|> PG.select .stateAbbrev
|> PG.readMany "http://localhost:3000"
{ filter = []
, order = []
, limit = Nothing
, offset = 0
}
vs
fetchSchools =
PG.from Schema.school School
|> PG.select .name
|> PG.select .streetAddress
|> PG.select .city
|> PG.select .stateAbbrev
|> PG.readMany "http://localhost:3000"
{ filter = []
, order = []
, limit = Nothing
, offset = 0
}
Likely we'll want this to be a new type of field. Reason being that we don't want users to have to do things like:
|> filter [ .name |> eq (Just "john") ]
we want:
|> filter [ .name |> eq "john" ]
need to explore this.
i'm leaning on the side of filter
it's hard to remember which record name is correct filter vs filters -- order vs orders
Note to self that I think operator names should be looked at again. My gut is telling me that camel casing everything (I'm looking at you ilike) makes more sense.
maybe unnecessarily confusing that you HAVE to use Postgrest.not
Is it currently possible to provide different codecs for decoding and encoding?
The use-case is as follows:
Resource foo
has a field email
that could be null
, so we'd like our Foo
model to represent email
as Maybe String
. The resource and query definition look like this:
fooResource =
resourse "foo"
{ ...
, email = field "email" <| Decode.maybe Decode.string
, ...
}
fooQuery =
query fooResource Foo
|> ...
|> select .email
|> ...
fooRequest : List String -> Cmd (List Foo)
fooRequest emails =
fooQuery
|> filter [ .email |> in' emails ]
|> ...
The problem is that if I try to apply a filter to the query (|> filter [ .email |> in' [<list of strings>]
) the types don't line up, because the resource uses a Maybe String
codec.
Is there a way to say which codec to use when making requests?
There is a high chance that I'm missing something fundamental though.
Okay, so in terms of implementing Json without depending on order
[9:52]
Let's start by defining a simple mode:
[9:52]
{ name : String
, age : Int
}
[9:53]
Nothing fancy really. Now let's describe how we'd like our decoders to look:
[9:53]
decodeModel value =
Ok { name = "", age = 0 }
|> decodeField "age" Json.int setAge value
|> decodeField "name" Json.string setName value
[9:53]
Notice how I've gotten rid of the confusing Decoder
abstraction for now
[9:53]
Also notice that name and age are out of place
[9:54]
Also notice that I define setAge and setName, lens style
[9:55]
they're mostly boilerplate but for clarity, this is what they look like:
setName model name = { model | name = name }
setAge model age = { model | age = age }
[9:55]
finally, we use it like this:
E.object [ ("name", E.string "noah"), ("age", E.int 10) ]
|> decodeModel
|> toString
|> Html.text
[9:56]
decodeModel will return a result of Ok { name = "noah", age = 10 }
[9:56]
decodeField is a scary looking thing which is actually pretty simple:
[9:56]
String
-> Json.Decoder a
-> (Model -> a -> Model)
-> Json.Value
-> Result String Model
-> Result String Model
decodeField fieldName decoder setter value model =
case model of
Err s -> model
Ok v ->
Json.decodeValue (Json.field fieldName decoder) value
|> Result.map (setter v)
[9:56]
(there are various ways of cleaning this up some more, but this is how you do out-of-order decoding in elm)
[9:59]
It could be further simplified if elm provided a couple of things. Namely, things that were taken out of the type system that didn't need to be (e.g adding fields to a record - then we wouldn't need to provide a default)
[9:59]
This approach is infinitely more workable.
[10:00]
You can make refactoring changes to your model without worrying about the order
[10:00]
If you have the right tooling, then generating setters/getters is trivial
[10:00]
Anyway, the other point: notice how I flatten the types as soon as possible.
[10:01]
People get so confused when they see Decoder a
and don't know how to make their own
fredcy [10:01 AM]
Fascinating. But my brain hurts. I hope someday you'll write this up. (I know that's not your thing though).
eeue56 [10:02 AM]
Which bit is making your brain hurt? Maybe I can explain that a bit better
fredcy [10:04 AM]
The role of the first line of decodeModel
puzzles me. Just haven't seen record values built up from a default as that appears to be.
rtfeldman [10:12 AM]
AndMap (not a big fan of the name either) is exactly the right tool for the job
of course, the json-pipeline library aliases andMap, but to understand how that decoder works, you need to know how andMap works
this is not a great pitch for andMap
the function itself as opposed to the concept behind it ๐
eeue56 [10:15 AM]
@fredcy, it's exactly what decode
does in elm-decode-pipeline
glenjamin [10:15 AM]
@eeue56 when thinking about it, I found the Generator one harder to refactor (only tried in my head) because of the need to chain the RNG state around - did I miss something?
eeue56 [10:15 AM]
You can think of it as "raising" or "lifting" a value into a type. Basically, just taking a Model
and turning into Result x Model
[10:16]
which can then be piped down to the other functions, so they can keep a clean sig
[10:16]
@glenjamin you can pass the state onwards in the same way through a tuple if you want
[10:16]
I just didn't for the decode example since it doesn't need it
glenjamin [10:17 AM]
ah right, same general approach then - you get something like bind
and return
, but name them based on the concrete case?
eeue56 [10:19 AM]
you'd need a return for that example yeah
may need an andThen for versioned apis -- i find this pretty interesting. let's hold off until it's actually needed.. but just wanted to record this.
http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode#andThen
I currently believe that all api choices should be as close to sql as possible. PostgREST really is a thin layer over sql and i think that it would be important to reveal this fact in the elm client api. There is definitely a possibility that a more general realization of this library would change api naming, however, this library is not that. Despite this realization, it is important to note that priorities are as follows:
So instead of Nothing
it would simply be 0
include relations in the resource definition
Brainstorming: https://gist.github.com/john-kelly/8ac5aa5d5e38bd148b70e10b0c44408c
Maybe Travis?
thank you postgrest! PostgREST/postgrest#821
I like what I see in the dev branch, but I am struggling to construct a prototype.
I am not able to interpret from reading the source and from the compiler errors what the dev branch needs in init and update. In the source, you mention that an examples/Main.elm might be the first stop, but I do not find that. Might you find time to draft an example to get us started?
check the change log: https://github.com/begriffs/postgrest/releases
apparently you can order (maybe filter) by nested json.
as an aside. this may affect naming of things:
ecto calls this embedded schemas
I just ran into my first case of needing to retrieve ten fields in a query. Right now it looks like I have to make two queries, but it would be helpful to find a more general way instead. It's not unusual for rows in a database to have more than eight columns.
reason being -- include
is currently forced to be Maybe... because of the possibility of filtering the query
something like this.
sessionCmd =
let
speakerQuery =
PG.query Resources.speaker Speaker
|> PG.select .id
|> PG.select .name
|> PG.select .bio
in
PG.query Resources.session Session
|> PG.select .id
|> PG.select .location
|> PG.select .start_time
|> PG.include .speaker speakerQuery
|> PG.list "http://postgrest.herokuapp.com/"
[ .location |> PG.not PG.ilike "%russia%" ]
[ PG.asc .start_time ]
PG.noLimit
|> Http.send Fetch
vs
sessionCmd =
let
speakerQuery =
PG.query Resources.speaker Speaker
|> PG.select .id
|> PG.select .name
|> PG.select .bio
in
PG.query Resources.session Session
|> PG.select .id
|> PG.select .location
|> PG.select .start_time
|> PG.include .speaker speakerQuery
|> PG.list "http://postgrest.herokuapp.com/"
{ filter = [ .location |> PG.not PG.ilike "%russia%" ]
, order = [ PG.asc .start_time ]
, limit = PG.noLimit
}
|> Http.send Fetch
confusing name in the context of the field decoders
although using the same name as elm-decode-pipeline is nice for reusing common naming conventions...
similar functions (in our lib) use a verb convention instead of the adj convention. for example: select, set, insert... so maybe hardcode
instead of hardcoded
just bringing this up for discussion.
when the value does not come from the api.
{-| -}
hardcoded : a -> Query uniq schema (a -> b) -> Query uniq schema b
hardcoded val (Query queryName schema params queryDecoder) =
Query queryName
schema
params
(apply queryDecoder (Decode.succeed val))
-- example
majorQuery =
PG.query Resources.major Major
|> PG.select .id
|> PG.select .assist_id
|> PG.select .title
|> PG.select .description
|> PG.select .school_id
|> PG.hardcoded True
type alias Major =
{ id : Int
, assist_id : String
, title : String
, description : Maybe String
, school_id : Int
, collapsed : Bool
}
based on my shallow exploration, it appears that these are different. i do not know how or why, so this may be worth looking into.
embed
type Select = Nested | Simple
i dont think that i makes sense for this to be a sum type. there may very well be no benefit from this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.