Building a JSON API in Elixir with Phoenix 1.5+

Hello all!

For those wanting to try your hands at Elixir / Phoenix, I wrote a comprehensive tutorial on doing a simple JSON API with said technologies.

Touches on everything: From language installation, app creation, schemas and endpoints, CORS configuration, simple authentication, to sessions (with cookies) and a bit of testing.

I updated it to latest Phoenix version:
Building a JSON API in Elixir with Phoenix 1.5+

Any feedback is appreciated!

60 Likes

Thanks! It’s a really nice tutorial.

2 Likes

You are welcome! :slight_smile:

I’ve also bookmarked it :003: (will go thro it when I’ve finished reading Programming Phoenix)

Thanks - we could definitely do with more tuts like this :023:

1 Like

I’m thinking about writing one about building a basic GraphQL API with Phoenix and Absinthe in Elixir.
Could also touch on writing a frontend for it using Vue.js + Apollo.

Let me know if this sounds interesting to you people.

19 Likes

I’d be interested an updated GraphQL tutorial.

2 Likes

I’d be interested in one for…

  • LiveView (when it’s out :lol:)
  • Drab
  • Texas

(can you tell I’d like to minimise JS!)

And then perhaps:

  • Vue.js
  • and maybe Bucklescript or Elm

You could start a poll btw “What kind of tutorial would you like to see?” and see what kind of response you get :003:

1 Like

Vue.js + apollo would be great.

3 Likes

Great tutorial! I really appreciate your style of tutorial (I can tell you have thought it through from the introduction)! I’m glad that you went through a bit of testing, which is often neglected these days. Even including things like custom 500 error pages is really nice because these are the things people just glaze over in their tutorials.

I am actually about to start a project where I need to setup Phoenix for a JSON API and I have been wondering about how to do authentication (JWT, API keys, etc.). I’m really glad I came across your tutorial since the session based method that you use seems the most simple and straight forward to me, and it’s in line with much of the authentication I’ve done in the past for Phoenix apps with interfaces.

Thanks again, great work and I really like your blog as well :slight_smile:

4 Likes

Ok, I’ve thinking about the poll idea, and here is the stuff I’ve come up with:

  • GraphQL API (Phoenix + Absinthe)
  • Static site generator (markdown + frontmatter => HTML)
  • Simple game server (Phoenix) + Game client (Vue.js)

0 voters

Those are the 3 things I’ve been wanting to dabble in, and plan to get a tutorial out of them.

For the GraphQL API one, it’d be similar to the JSON API one, but you’ll have GraphQL endpoints ready for use with something like vue-apollo.

For the static site generator, I’m currently working on one, the idea is to use it for my own blog to replace Hugo , since I can’t really stand its templating language :smile:

And as for the game server, probably will go for a multiplayer Battleship game using Phoenix and WebSockets.

Let me know what y’all think. Thx.

2 Likes

Just FYI, there is a nice book about writing Battleship game using Elixir and Phoenix Channels. The game in the book is called Islands, but it’s the same thing. You would better pick a unique game to attract more readers.

4 Likes

Thanks for the heads up!
All right, maybe multiplayer Tetris or something? :smile:

1 Like

Looks like a solid advantage on the GraphQL one.
I’ll close the poll after the weekend.

Thanks everyone for your participation!

So, I’ll be working on a GraphQL API tutorial over the coming weeks on my spare time.
Once it’s done I’ll post a link on this forum to get some feedback.

1 Like

Congrats!

1 Like

Looking forward to seeing GraphQL + Absinthe!

3 Likes

Hello!

Long time no see.

I have a small update for this tutorial.
I added a Simple VPS deployment section to it.

Also updated the code, and removed a couple of warnings that were in there.

I’d appreciate any feedback you might have.

Cheers!

2 Likes

Hey @zenw0lf

short question. After reading couple of tutorials and blogposts about phoenix and APIs I come across the data nesting of the json responses.
like

%{
    data: %{
        user: %{
          id: user.id,
          email: user.email
        }
    }
}

All the guide doe that and looks to me highly unusual since from the js side I always have to access the payload via response.data.data;. Even in your tests you have to access it via = json_response(conn, 200)["data"]. Wouldn’t it make more sense to have it nested based on the object type like %{ user: %{ ...

best regards
denym_

1 Like

Iirc there are/were security problems with browsers, which could be circumvented by always sending data as part of a top level object.

1 Like

It’s just good practice to not return top level arrays as the result for a request. At some point it was a vulnerability like explained here: https://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/

But, nowadays, I just use it that way because sometimes I add extra properties to the response like this:

{
  data: [ ... ],
  metadata: { current_page:2, total_pages: 10 }
}

So it’s good to keep separate your data from other attributes you might want in the response.

And regarding the res.data.data access, I prefer to not have a customized name for the data attribute. Although it sounds good in principle, in the long run is better to have a uniform way to access what you need from the response.

In any case you should be able to instruct your HTTP client to unwrap what you want for you on every request. For example, I have this setup for Axios for interacting with a Phoenix JSON API:

import axios from 'axios'


const baseDataService = axios.create({
  baseURL: 'http://localhost:4000/api/',
  headers: {
    'Accept': 'application/json'/* ,
    'Content-Type': 'application/json;charset=UTF-8' */
    // Seems like that's the type Axios sends by default
  },
  withCredentials: true
})

baseDataService.interceptors.response.use(
  function (response) {
    // Unwrap and return the data property
    return response.data

    // This is desired unless you have a special need for any of the
    // other root properties:
    // config, headers, request, status, statusText
  },

  function (error) {
    if (error.response) {
      return Promise.reject(error.response)
    } else {
      return Promise.reject(error)
    }
  }
)


export default baseDataService

So, on my requests, assuming I’m returning data and metadata (like in the example above) I do something like this:

userService
  .all()
  .then(res => console.log(res.data, res.metadata))
1 Like