This library allows you to construct and execute postgrest requests with additional type safety.
Here's what Api.People
might look like:
import Json.Decode exposing (..)
import Json.Encode as JE
import Postgrest.Client as P
-- Optional, but recommended to have a type that
-- represents your primary key.
type PersonID
= PersonID Int
-- And a way to unwrap it...
personID : PersonID -> Int
personID (PersonID id) =
id
-- Define the record you would fetch back from the server.
type alias Person =
{ id : PersonID
, name : String
}
-- Define a submission record, without the primary key.
type alias PersonSubmission =
{ name : String
}
-- Decoders are written using Json.Decode
decodeUnit : Decoder Person
decodeUnit =
map2 Person
(field "id" <| map PersonID int)
(field "name" string)
-- Encoders are written using Json.Encode
encode : PersonSubmission -> JE.Value
encode person =
JE.object
[ ( "name", JE.string person.name )
]
-- Tell Postgrest.Client the column name of your primary key and
-- how to convert it into a parameter.
primaryKey : P.PrimaryKey PersonID
primaryKey =
P.primaryKey ( "id", P.int << personID )
-- Tell Postgrest.Client the URL of the postgrest endpoint and how
-- to decode an individual record from it. Postgrest will combine
-- the decoder with a list decoder automatically when necessary.
endpoint : P.Endpoint Person
endpoint =
P.endpoint "/people" decodeUnit
-- Fetch many records. If you want to specify parameters use `setParams`
getMany : P.Request (List Person)
getMany =
P.getMany endpoint
-- Delete by primary key. This is a convenience function that reduces
-- the likelihood that you delete more than one record by specifying incorrect
-- parameters.
delete : PersonID -> P.Request PersonID
delete =
P.deleteByPrimaryKey endpoint primaryKey
-- Create a record.
post : PersonSubmission -> P.Request Person
post =
P.postOne endpoint << encode
Here's how you could use it:
import Api.People as People
import Postgrest.Client as P
jwt : P.JWT
jwt =
P.jwt "abcdefghijklmnopqrstuvwxyz1234"
type Msg
= PersonCreated (Result P.Error Person)
| PeopleLoaded (Result P.Error (List Person))
| PersonDeleted (Result P.Error PersonID)
toCmd =
P.toCmd jwt
cmdExamples =
[ People.post
{ name = "Yasujirō Ozu"
}
|> P.toCmd jwt PersonCreated
, People.getMany
[ P.order [ P.asc "name" ], P.limit 10 ]
|> toCmd PeopleLoaded
, Person.delete personID
|> toCmd PersonDeleted
]
Postgrest.Internal.Endpoint.Endpoint a
Think of an Endpoint as a combination between a base url like /schools
and
an elm/json Decoder
. The endpoint can be passed to other functions in this library,
sometimes along with PrimaryKey to make constructing certain types of requests easier.
Postgrest.Internal.Requests.Request r
Request can be used with toCmd and toTask to make a request.
endpoint : String -> Json.Decode.Decoder a -> Endpoint a
The simplest way to define an endpoint. You provide it the URL and a decoder. It can then be used to quickly construct POST, GET, PATCH, and DELETE requests. The decoder provided should just be a decoder of the record itself, not a decoder of an object inside an array.
decodePerson : Decoder Person
decodePerson =
map2 Person
(field "first_name" string)
(field "last_name" string)
peopleEndpoint : P.Endpoint Person
peopleEndpoint =
P.endpoint "/rest/people" decodePerson
customEndpoint : String -> Json.Decode.Decoder a -> { defaultSelect : Maybe (List Selectable), defaultOrder : Maybe (List ColumnOrder) } -> Endpoint a
Define an endpoint with extra options. To quickly construct POST, GET, PATCH, and DELETE requests.
defaultOrder
and defaultSelect
can be overriden by using setParams
once a request is constructed.
peopleEndpoint : P.Endpoint Person
peopleEndpoint =
P.endpoint "/rest/people"
decodePerson
{ defaultSelect = Just [ P.attribute "id", P.attribute "name" ]
, defaultOrder = Just [ P.asc "name" ]
}
getMany : Endpoint a -> Request (List a)
Used to GET multiple records from the provided endpoint.
Converts your endpoint decoder into (list decoder)
to decode multiple records.
endpoint : P.Endpoint Person
endpoint =
P.endpoint "/people" decodePerson
getAll : P.Request (List Person)
getAll =
P.getMany endpoint
|> P.setParams [ P.limit 20 ]
getOne : Endpoint a -> Request a
Used to GET a single record. Converts your endpoint decoder into (index 0 decoder)
to extract
it from postgrest's JSON array response and sets limit=1
in the parameters. If you're requesting by
primary key see getOneByPrimaryKey
.
endpoint : P.Endpoint Person
endpoint =
P.endpoint "/people" decodePerson
getOnePersonByName : String -> P.Request Person
getOnePersonByName name =
P.getOne endpoint
|> P.setParams [ P.param "name" <| P.eq name ]
postOne : Endpoint a -> Json.Encode.Value -> Request a
Used to create a single record at the endpoint you provide and an encoded JSON value.
endpoint : P.Endpoint Person
endpoint =
P.endpoint "/people" decodePerson
encodePerson : Person -> JE.Value
encodePerson p =
object
[ ( "first_name", JE.string p.firstName )
, ( "last_name", JE.string p.lastName )
]
post : PersonForm -> P.Request Person
post submission =
P.postOne endpoint (encodePerson submission)
getByPrimaryKey : Endpoint a -> PrimaryKey p -> p -> Request a
Used to GET a single record by primary key. This is the recommended way to do a singular GET request assuming your table has a primary key.
endpoint : P.Endpoint Person
endpoint =
P.endpoint "/people" decodePerson
primaryKey : P.PrimaryKey Int
primaryKey =
P.primaryKey ( "id", P.int )
getByPrimaryKey : Int -> P.Request Person
getByPrimaryKey =
P.getByPrimaryKey endpoint primaryKey
patchByPrimaryKey : Endpoint a -> PrimaryKey p -> p -> Json.Encode.Value -> Request a
Used to PATCH a single record by primary key. This is the recommended way to do a PATCH request assuming your table has a primary key. The decoder will decode the record after it's been patched if the request is successful.
endpoint : P.Endpoint Person
endpoint =
P.endpoint "/people" decodePerson
primaryKey =
P.primaryKey ( "id", P.int )
updatePerson : PersonForm -> Int -> P.Request Person
updatePerson submission id =
P.patchByPrimaryKey endpoint primaryKey (encodeSubmission submission)
-- Would create a request to patch to "/people?id=eq.3"
updatePerson form 3
deleteByPrimaryKey : Endpoint a -> PrimaryKey p -> p -> Request p
Used to DELETE a single record by primary key. This is the recommended way to do a DELETE request if your table has a primary key. The decoder will decode the record after it's been patched if the request is successful.
endpoint : P.Endpoint Person
endpoint =
P.endpoint "/people" decodePerson
primaryKey =
P.primaryKey ( "id", P.int )
delete : Int -> P.Request Int
delete =
P.deleteByPrimaryKey endpoint primaryKey
-- Would create a request to DELETE to "/people?id=eq.3"
-- and the success value would be the ID passed in.
-- So your Msg would look like:
-- | DeleteSuccess (Result P.Error Int)
delete 3
setParams : Params -> Request a -> Request a
Used to set the parameters of your request.
getPeople : P.Request (List Person)
getPeople =
P.getMany endpoint
|> P.setParams
[ P.order [ P.asc "first_name" ]
, P.limit 20
]
setTimeout : Basics.Float -> Request a -> Request a
Sets the timeout of your request. The behaviour is the same of that in the elm/http package.
get : String -> { params : Params, decoder : Json.Decode.Decoder a } -> Request a
The most basic way to make a get request.
post : String -> { params : Params, decoder : Json.Decode.Decoder a, body : Json.Encode.Value } -> Request a
The most basic way to make a post request.
unsafePatch : String -> { body : Json.Encode.Value, decoder : Json.Decode.Decoder a, params : Params } -> Request a
Titled unsafe because if you provide incorrect or no parameters it will make a PATCH request to all resources the requesting user has access to at that endpoint. Use with caution. See Block Full-Table Operations.
unsafeDelete : String -> UnsafeDeleteOptions a -> Request a
Titled unsafe because if you provide incorrect or no parameters it will make a DELETE request to all resources the requesting user has access to at that endpoint. Use with caution. See Block Full-Table Operations.
Postgrest.Internal.JWT.JWT
The type used to store the JWT string.
jwt : String -> JWT
Pass the jwt string into this function to make it a JWT. This is used with toCmd
and toTask
to make requests.
myJWT =
P.jwt "abcdef"
jwtString : JWT -> String
If you've already created a JWT with jwt
you can extract the original string with
this function.
myJWT = P.jwt "abcdef"
jwtString myJWT -- "abcdef"
toCmd : JWT -> (Result Error a -> msg) -> Request a -> Platform.Cmd.Cmd msg
Takes a JWT, Msg and a Request and turns it into a Cmd.
toTask : JWT -> Request a -> Task Error a
Takes a JWT and a Request and turns it into a Task.
Can be used together with endpoint to make request construction easier. See primaryKey and endpoint.
primaryKey : ( String, pk -> Value ) -> PrimaryKey pk
Used to construct a primary key made up of one column. Takes a tuple of the column name of your primary key and a function to convert your elm representation of that primary key into a postgrest parameter.
primaryKey : P.PrimaryKey Int
primaryKey =
primaryKey ( "id", P.int )
is the simplest example. If you have custom type to represent your primary key you could do this:
type ID
= ID Int
idToInt : ID -> Int
idToInt (ID id) =
id
primaryKey : P.PrimaryKey ID
primaryKey =
P.primaryKey ( "id", P.int << idToInt )
primaryKey2 : ( String, pk -> Value ) -> ( String, pk -> Value ) -> PrimaryKey pk
Used to construct a primary key made up of two columns. Takes two tuples, each with a column name and a function to convert your elm representation of that primary key into a postgrest parameter.
primaryKey2 ( "id", P.int )
is the simplest example. If you have custom type to represent your primary key you could do this:
type alias ParentID =
Int
type alias Category =
String
type alias MyPrimaryKey =
( ParentID, Category )
primaryKey : P.PrimaryKey MyPrimaryKey
primaryKey =
P.primaryKey2
( "parent_id", P.int << Tuple.first )
( "category", P.string << Tuple.second )
primaryKey3 : ( String, pk -> Value ) -> ( String, pk -> Value ) -> ( String, pk -> Value ) -> PrimaryKey pk
Used to construct primary keys that are made up of three columns. See primaryKey2 for a similar example of how this could be used.
Error
Looks a lot like Http.Error
except BadStatus
includes a second argument,
PostgrestErrorJSON
with any details that postgrest might have given us about a failed request.
{ message : Maybe String
, details : Maybe String
, hint : Maybe String
, code : Maybe String
}
Contains any details postgrest might have given us about a failed request.
toHttpError : Error -> Http.Error
Converts the custom HTTP error used by this package into an elm/http Error.
This can be useful if you're using Task.map2
, Task.map3
, etc... and each of the
tasks need to have the same error type.
Postgrest.Internal.Params.Param
An individual postgrest parameter.
List Param
A list of Param.
Postgrest.Internal.Params.Selectable
A type representing which attributes and resources you want to select. It also contains parameters that target nested resources.
Postgrest.Internal.Params.ColumnOrder
A type to specify whether you want an order to be ascending or descending, and optionally whether you want nulls to be first or last.
Postgrest.Internal.Params.Value
Type that can be represented in the queries: strings, ints and lists.
Postgrest.Internal.Params.Operator
A type that represents the operator of a query. In name=eq.John
the operator would be the =
.
select : List Selectable -> Param
A constructor for the select parameter.
P.select
[ P.attribute "id"
, P.attribute "title"
, P.resource "user" <|
P.attributes
[ "email"
, "name"
]
]
allAttributes : List Selectable
When you want to select all attributes. This is only useful when used to select attributes of a resource or override default parameters in another function since postgrest returns all attributes by default.
attribute : String -> Selectable
When you want to select a certain column.
attributes : List String -> List Selectable
Shorthand for attributes, when you don't need to specify nested resources:
-- Short version
attributes [ "id" "name" ]
-- Long version
[ attribute "id"
, attribute "name"
]
resource : String -> List Selectable -> Selectable
When you want to select a nested resource with no special parameters for the nested
resources. If you do want to specify parameters, see resourceWithParams
.
resourceWithParams : String -> Params -> List Selectable -> Selectable
When you want to select a nested resource with special praameters.
[ P.select
[ P.resource "sites"
[ P.resourceWithParams "streams"
[ P.order [ P.asc "name" ]
]
allAttributes
]
]
]
|> toQueryString
-- select=sites(streams(*))&sites.streams.order=name.asc
combineParams : Params -> Params -> Params
Takes a default set of params and a custom set of params and prefers the second set. Useful when you're constructing reusable functions that make similar queries.
concatParams : List Params -> Params
Takes a list of Params and combines them, preferring the last sets first.
normalizeParams : Params -> List ( String, String )
Takes Params and returns the parameters as a list of (Key, Value) strings.
toQueryString : Params -> String
Takes Params and returns a query string such as
foo=eq.bar&baz=is.true
param : String -> Operator -> Param
A constructor for an individual postgrest parameter.
param "name" (eq (string "John"))
or : List Param -> Param
Join multiple conditions together with or.
[ or
[ param "age" <| gte <| int 14
, param "age" <| lte <| int 18
]
]
|> toQueryString
-- or=(age.gte.14,age.lte.18)
and : List Param -> Param
Join multiple conditions together with and.
[ and
[ param "grade" <| gte <| int 90
, param "student" <| true
, or
[ param "age" <| gte <| int 14
, param "age" <| null
]
]
]
|> toQueryString
-- and=(grade.gte.90,student.is.true,or(age.gte.14,age.is.null))
nestedParam : List String -> Param -> Param
When you want to specify an operator for a nested resource manually. It is recommended to use resourceWithParams though.
[ select
[ attribute "*"
, resource "actors" allAttributes
]
, nestedParam [ "actors" ] <| limit 10
, nestedParam [ "actors" ] <| offset 2
]
|> toQueryString
-- "select=*,actors(*)&actors.limit=10&actors.offset=2"
eq : Value -> Operator
Used to indicate you need a column to be equal to a certain value.
gt : Value -> Operator
Used to indicate you need a column to be greater than a certain value.
gte : Value -> Operator
Used to indicate you need a column to be greater than or equal than a certain value.
inList : (a -> Value) -> List a -> Operator
Used to indicate you need a column to be within a certain list of values.
param "name" <| inList string [ "Chico", "Harpo", "Groucho" ]
-- name=in.(\"Chico\",\"Harpo\",\"Groucho\")"
limit : Basics.Int -> Param
Limit the number of records that can be returned.
limit 10
lt : Value -> Operator
Used to indicate you need a column to be less than a certain value.
lte : Value -> Operator
Used to indicate you need a column to be less than or equal than a certain value.
neq : Value -> Operator
Used to indicate you need a column to be not equal to a certain value.
not : Operator -> Operator
Negate a condition.
[ param "my_tsv" <| not <| phfts (Just "english") "The Fat Cats"
]
|> toQueryString
-- my_tsv=not.phfts(english).The%20Fat%20Cats
true : Operator
When you need a column value to be true
-- foo=is.true
[ P.param "foo" P.true ]
|> toQueryString
false : Operator
When you need a column value to be false
-- foo=is.false
[ P.param "foo" P.false ]
|> toQueryString
null : Operator
Query, specifying that a value should be null.
param "age" <| null
value : Value -> Operator
When you don't want to use a specific type after the equals sign in the query, you
can use value
to set anything you want.
offset : Basics.Int -> Param
Specify the offset in the query.
offset 10
ilike : String -> Operator
ILIKE operator (use * in place of %)
param "text" <| ilike "foo*bar"
like : String -> Operator
LIKE operator (use * in place of %)
param "text" <| like "foo*bar"
contains : List Value -> Operator
Use the cs
operator.
param "tag" <| contains <| List.map string [ "Chico", "Harpo", "Groucho" ]
-- tag=cs.(\"Chico\",\"Harpo\",\"Groucho\")"
containedIn : List Value -> Operator
Use the cd
operator.
param "tag" <| containedIn <| List.map string [ "Chico", "Harpo", "Groucho" ]
-- tag=cd.(\"Chico\",\"Harpo\",\"Groucho\")"
string : String -> Value
Normalize a string into a postgrest value.
int : Basics.Int -> Value
Normalize an int into a postgrest value.
list : List Value -> Value
This is available if you need it, but more likely you'll want to use
inList
.
order : List ColumnOrder -> Param
A constructor for the limit parameter.
order (asc "name")
order (desc "name")
asc : String -> ColumnOrder
Used in combination with order
to sort results ascending.
P.order [ P.asc "name" ]
desc : String -> ColumnOrder
Used in combination with order
to sort results descending.
P.order [ P.desc "name" ]
nullsfirst : ColumnOrder -> ColumnOrder
Sort so that nulls will come first.
order [ asc "age" |> nullsfirst ]
nullslast : ColumnOrder -> ColumnOrder
Sort so that nulls will come last.
order [ asc "age" |> nullslast ]
plfts : Maybe Postgrest.Internal.Params.Language -> String -> Operator
Full-Text Search using plainto_tsquery
phfts : Maybe Postgrest.Internal.Params.Language -> String -> Operator
Full-Text Search using phraseto_tsquery
fts : Maybe Postgrest.Internal.Params.Language -> String -> Operator
Full-Text Search using to_tsquery
[ param "my_tsv" <| fts (Just "french") "amusant" ]
|> toQueryString
"my_tsv=fts(french).amusant"