No Time For a REST

Phew, it’s been another red hot, football-filled week here in the UK. I’m sure for some of you, 29°C is positively chilly. But for me, it’s been baking. Not helped by an evil selection of naughty wasps making camp outside my window, forcing me to work without fresh air. Lovely.

Anyway, I’m sure you don’t tune in for weather updates, or for my views on the Sports Ball. Let’s get back to the good stuff.

Do You GraphQL?

One of the most interesting bits of tech about at the moment, in my opinion, is GraphQL.

GraphQL, briefly, is an alternative to REST. The main difference between ‘REST’ and GraphQL being that we – as consumers – can specify exactly which bits of data we want / need, and the server returns just the things we care about, and nothing more.

Also, you only need one endpoint with GraphQL, as often opposed to many with ‘REST’.

Now, I put REST in inverted commas as the typical RESTful API’s that I both create, and work with, are not truly RESTful. Pragmatism, and all that. That’s why I’ve more typically started referring to these types of API’s as JSON APIs.

But I digress.

Like every new piece of tech, there are often a bunch of advocates shouting loudly, particularly on sites like Twitter, about how if you’re not using GraphQL then you’re basically a dinosaur.

how-to-graphqlThen recently – I think maybe a few weeks ago – I came across a really interesting website about GraphQL. It’s called How To GraphQL.

Why I like the site is that there are some generic introduction tutorials, and then some more language specific tutorials. There’s React for the front end, along with Node and Elixir on the back end. No PHP, mind. More on that in a second. I chose the Elixir tutorial, and enjoyed playing around with it.

After playing around with the Elixir back end implementation I’d created, I wondered how this might translate into PHP. I was already aware that the API Platform can support GraphQL so used that as a starting point. I kept my implementation identical (well, as close as) to the setup in the How To GraphQL Elixir tutorial. The idea being that I could switch out the two, and the front end shouldn’t care.

It turned out to be a really interesting exercise. I’d be happy to share my code if anyone has any interest? There’s nothing fancy there, but it was a fun learning experience for me.

What I did find most interesting was in that Dunglas, the creator of the API Platform (and many other cool things – very clever guy, well worth following), isn’t quite the GraphQL advocate I expected. These two tweet threads are interesting reading:

and:

I’d be really interested to hear about your experiences with GraphQL.

Video Update

This week saw three new videos added to the site:

GET’ting Multiple Resources [API Platform]

I mentioned last week that in many ways I’ve been doing the API Platform a disservice.

In taking as many videos as I have to show a single end point API I may have made things look more difficult than they really are.

We’ve used this setup as an excuse to cover some interesting, and useful / commonly needed things such as customisation of your route paths, and defining custom operations (the Health check).

If you don’t need to customise anything, getting an API up and running using the API Platform is really remarkably rapid.

All that said, I stand by this approach. We’ve played around with some cool features. This is all stuff that will help you in the real world.

PUT to Update Existing Data [API Platform]

The API Platform takes an interesting approach to the process of updating existing data.

There’s no implementation of PATCH , the most controversial / complex HTTP Verb. And that’s fine. Less controversy is always a good thing, imo. Besides which, the more I work with the front end, the less I find any use for PATCH  anyway. Typically I will have the full resource, so making full updates to that resource is easy enough.

We cover a little potential gotcha in the way that API Platform differs from the Symfony 4 JSON API, and Symfony 4 with FOSRESTBundle approaches. This is in the status code returned by the API Platform, and why they may choose to do this.

DELETE to Remove Data [API Platform]

Adding an implementation for DELETE  is probably the easiest of the whole lot. This is partly because we’ve done all the hard work already. But also because deleting stuff just works. There’s very little to it.

Now, in the real world you’d probably want to restrict who can and can’t delete, and things get a little more complex. But the underlying operation itself is very straightforward.

There’s just one thing left to do with our API Platform setup, and that’s handle the error paths. We’ll get on to that in the final video in this part of the series.

Live Stream Update

I’ve recorded the first “live stream”. It’s on my laptop, waiting for a touch of editing. I need to mask a few bits of config due to security reasons.

I also hit on a proper issue. The domain I was planning to launch under has expired. And worse, because I let it expire and didn’t renew it, it’s gone into grace period. And now Namecheap want $108 to reactivate it. Silly me.

Ok, so this may delay the launch of the thing in the real world. It’s not going to stop me writing the code. I wanted to get this video out this week. It will slip into next week. Fortunately (depending on how you look at it), I have a couple of long train journeys on Monday and Tuesday evening next week. The perfect time to edit videos – even if it does draw a few funny looks.

As a reminder, the live stream stuff will not be getting a write up. These will be video only, but you’re more than welcome to raise questions, or ask to see more detail etc. I’ll share all that via the forum.

Ok, that about wraps it up from me this. As ever, have a great weekend, and happy coding.

Chris

What is MFA in Elixir?

I’m posting this to force myself to remember what it means.

M : Module

F : Function

A : Arguments

Module, Function, Arguments.

pid = spawn(MyModule, :myfunc, [])

E.g.

iex(1)> pid = spawn(Explorate, :loop, [])
#PID<0.123.0>

Can then send that pid a message.

defmodule Explorate do

  def loop do
    receive do
      { :hello } -> IO.puts "ahoy, shipmate!"
      _ -> IO.puts "Yous no ma shipmayte"
    end

    loop
  end

end
Interactive Elixir (1.5.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> pid = spawn(Explorate, :loop, [])
#PID<0.123.0>
iex(2)> send(pid, "ddd")
Yous no ma shipmayte
"ddd"
iex(3)> send(pid, {:hello})
ahoy, shipmate!
{:hello}

Useful link

Note also to call loop  again to recurse, otherwise you only get to call the pid  once.

Programming Phoenix – Video to Category Relationship

I’m currently working my way through the Programming Phoenix book, which I have to say, I am thoroughly enjoying.

That’s not to say it’s all plain sailing. If only I were that good.

I got to chapter 7, and got myself quite stuck.

I’m aware the book was written for an earlier version of Phoenix, and in an attempt to force myself to “learn the hard way”, I decided that whenever I hit on a deprecated function, I would use the given replacement.

Ok, so in the book on page 99 (Chapter 6: Building Relationships), there is the following code:

  @required_fields ~w(url title description)
  @optional_fields ~w(category_id)

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end

When using this code I got a deprecation warning:

warning: `Ecto.Changeset.cast/4` is deprecated, please use `cast/3` + `validate_required/3` instead
    (rumbl) web/models/video.ex:34: Rumbl.Video.changeset/2
    (rumbl) web/controllers/video_controller.ex:33: Rumbl.VideoController.create/3
    (rumbl) web/controllers/video_controller.ex:1: Rumbl.VideoController.action/2
    (rumbl) web/controllers/video_controller.ex:1: Rumbl.VideoController.phoenix_controller_pipeline/2

Seeing as I made the commitment to using the newer functions, this seemed like as good a time as any to do so.

The suggested replacement: cast/3 (docs) is where my confusion started. Here’s the signature:

cast(data, params, allowed)

And here’s the signature for cast/4 :

cast(data, params, required, optional)

You may then be wondering why in the original code from the book then that we only have three arguments being passed in?

Good question.

Really there are four, though one is being passed in as the first argument by way of Elixir’s pipe operator – which passes the outcome of the previous statement into the first argument of the next:

  model
 |> cast(params, @required_fields, @optional_fields)

As seen in the code from the Programming Phoenix book, cast/3 expects some params, then a list of required_fields, and another list of optional_fields.

One nice thing happening in the original code is the use of Attributes (@ symbol) as constants.

Using the ~w sigil is a way to create a list from the given string. Ok, so lots of things happening here in a very little amount of code. This is one of the benefits, and drawbacks (when learning) of Elixir, in my experience.

With PHPStorm I’m so used to having the code-completion and method signature look ups (cmd+p on mac) that learning and remembering all this stuff is really the hardest part. Maybe ‘intellisense’ has made me overly lazy.

Anyway, all of this is nice to know but it’s not directly addressing ‘problem’ I faced (and I use the inverted commas there as this isn’t a problem, it’s my lack of understanding).

We’ve gone from having required and optional fields, to simply just allowed fields.

My replacement code started off like this:

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:url, :title, :description])
    |> assoc_constraint(:category)
    |> validate_required([:url, :title, :description])

Everything looked right to me.

I had the required fields, but now as a list, rather than the ~w sigil approach.

I had specified my three required fields by way of validate_required.

And as best I could tell, I was associating with the :category.

But no matter what I did, my Video was never associated with a Category on form submission.

I could see the Category was being submitted from my form:

%{"category_id" => "3", "description" => "Quality techno", "title" => "Richie Hawtin @ ENTER Ibiza Closing Party 2014, Space Ibiza",
  "url" => "https://www.youtube.com/watch?v=-5t2gH0l99w"}

But the generated insert statement was missing my category data:

INSERT INTO "videos" ("description","title","url","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5,$6) ...

Anyway, it turns out I was over thinking this.

All I needed to do was add in :category to the list passed in as the allowed argument and everything worked swimmingly:

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:url, :title, :description, :category_id])
    |> assoc_constraint(:category)
    |> validate_required([:url, :title, :description])
  end

With the outcome of the next form submission:

INSERT INTO "videos" ("category_id","description","title","url","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5,$6,$7) ...
[debug] QUERY OK db=1.1ms

Easy, when you know how.

This took me an embarrassingly long time to figure out. I share with you for two reasons:

  1. Learning the hard way makes you learn a whole bunch more than simply copy / pasting, or typing out what you see on the book pages.
  2. I couldn’t find anything about this after a lot of Googling, so if some other poor unfortunate soul is saved a lot of headscratching from this in the future then it was all worthwhile writing.

That said, the new edition of Programming Phoenix is due for release very soon (so I believe), so likely this will have been addressed already.