We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Post 76
Ash Framework 05 - API's
Published on: 2025-09-27
Tags:
elixir, Blog, Side Project, LiveView, Ecto, Html/CSS, Phoenix, Ash, Framework
Chapter 4: Generating APIs Without Writing Code (85)
We now want to make an API so that other users can access our data in thier own apps.
Model Your Domain, Derive the Results
In this chapter we will build a REST and GraphQL API for other users to access our data.
Building A JSON REST interface
We will use some built in ASH package to generate the API that will accept requests over HTTP and the send the data in a JSON format
Setup
Lets add some features to the app. This will be done with and igniter install, here is what it will do:
ash_json_api Hex package
Code formatting and configuration to support a new application/vnd.api+json
New TunezWeb.AshJsonApiRouter
A new scope for the Phoenix router.
mix igniter.install ash_json_api
Adding Artists to the API
We will try and implement the CRUD for the API and as such we will need to add each resource and then make them public. We will use the extend Mix task to do so. A couple of things that this will do:
AshJsonApi.Resource will be added to make use of the new access point
A default API type will be added to the resource.
Since it is the first resource for Music it will connect the pieces of the domain
AshJsonApi.Domain will be added as an extension of the Tunez.Music domain
Tunez.Music will be added to the list of domains in the TunezWeb.AshJsonApiRouter module.
mix ash.extend Tunez.Music.Artist json_api
Now we need to set some of the routes to make the actions on the Artist resource available in the API. We could add the domain or the resource to the API but it makes more sense to add a layer of obfiscation so that a user can only interact with the top layer of the domain. Head to lib/tunez/music.ex
defmodule Tunez.Music do
# ...
json_api do
routes do
base_route "/artists", Tunez.Music.Artist do
get :read
index :search
post :create
patch :update
delete :destroy
end
end
end
end
This will set some default actions for each user for each artist. GET and artist POST will create an artist, and so on. We can check the routes from a the WSL console
mix phx.routes
You can even use the GET route by accessing http://localhost:4000/api/json/artists/?query=and
"What Data Gets Included in API Responses?
So you might notice that some of the data is missing and that is becuase only the attributes that are made public are included. This is a security thing and can be changed. Lets add the biography and the previous_names to the public list, head to lib/tunez/music/artist.ex
attributes do
# ...
attribute :previous_names, {:array, :string} do
default []
public? true
end
attribute :biography, :string do
public? true
end
# ...
end
Calculations and Aggregates are different as they can be calculation expensive so there are a few ways to make them visible to the API:
Set them as default fields at the resource level. This will be done EVERYTIME that the resource is pulled.
Make it so that a user can request the specific calculation or aggreagates that they require. They will need to add the field to the query string parameters. Something like http://localhost:4000/api/json/artists?fields=name,album_count . THis is a default response in the ASH framework.
"Creating Artists Records"
There is quite a few routes that were created and we will just go over the post but with the right body and route you can use the API to create an artist as well.
The PATCH can update and the DELETE can remove an artist.
Adding Albums to the API
We can use the same extension to add albums to the API as well. Afterwards we will need to add in the routes and default actions that we want the API route to be able to use.
mix ash.extend Tunez.Music.Album json_api
Now we need to head to lib/tunez/music.ex to add in the routes as we did before
json_api do
routes do
# ...
base_route "/albums", Tunez.Music.Album do
post :create
patch :update
delete :destroy
end
end
We now have the way to work with the albums but we don't have a nice way of dealing with the artist_id that is usually pre-hidden in the HTTP request. We will need to provide a valid ID for that and we will need to head into the album.ex to make those values public. Head to lib/tunez/music/album.ex to make those changes.
attributes do
uuid_primary_key :id
attribute :name, :string do
allow_nil? false
public? true
end
attribute :year_released, :integer do
allow_nil? false
public? true
end
attribute :cover_image_url, :string do
public? true
end
# ...
"Showing Albums for a Given Artist"
There are to ways to include related resources for a given resource. Both will require that the relationship is public. Head to lib/tunez/music/artist.ex to set that now.
relationships do
has_many :albums, Tunez.Music.Album do
sort year_released: :desc
public? true
end
end
"Including Related Records"
The easist way and it mirrors the way we have done things like this before and that is to include it in the json_api parent resource. Like this http://localhost:4000/api/json/artists?query=cove&include=albums
json_api do
type "artist"
includes [:albums]
end
"Linking to a List of Related Records"
You can add a link the the records that the user might want wit the GET call to the API. This will require heading into lib/tunez/music.ex and adding in a related relationship to the API response.
base_route "/artists", Tunez.Music.Artist do
# ...
related(:albums, :read, primary?: true)
end
Generating API Documentation with OpenApiSpex
The installation of AshJsonApi also incluced a way to build some Documentation with ease. We just need to add in the route. Head to lib/tunez_web/ash_json_api_router.ex to see what it already did. http://localhost:4000/api/json/open_api
defmodule TunezWeb.AshJsonApiRouter do
use AshJsonApi.Router,
domains: [Tunez.Music],
open_api: "/open_api"
end
Now this is just a json result with all the information but not a clear way of using it you can use the built in funcionality of swaggerui to format it much better. Head to lib/tunez_web/router.ex this will made the route api/json/swaggerui available
scope "/api/json" do
pipe_through [:api]
forward "/swaggerui", OpenApiSpex.Plug.SwaggerUI,
path: "/api/json/open_api",
default_model_expand_depth: 4
forward "/", TunezWeb.AshJsonApiRouter
end
Customizing the Generated API
Lets tackle some low hanging fruit and make it look better and work better.
"Adding Informative Descriptions"
Ash allows us to set some descriptors for all the resources that we have in order to make any Documentation or calls be better understood.
A description for a resource as a whole. or
A description for an action or an argument for an action
Take some time and add in a few of those here are some to get you started
defmodule Tunez.Music.Artist do
use Ash.Resource, ...
resource do
description "A person or group of people that makes and releases music."
end
read :search do
description "List Artists, optionally filtering by name."
argument :query, :ci_string do
description "Return only artists with names including the given value."
# ...
"Updating the API Title and Version"
Head to lib/tunez_web/ash_json_api_router.ex and then add some lines so that when you are using the route there is a title and version in the heading.
defmodule TunezWeb.AshJsonApiRouter do
use AshJsonApi.Router,
domains: [Tunez.Music],
open_api: "/open_api",
open_api_title: "Tunez API Documentation",
open_api_version: to_string(Application.spec(:tunez, :vsn))
end
"Removing Unwanted Extras"
If you took some time to look through the API docs you see that there is some filtering that we are not using as we have our own setup. You can disable the built in versions if you want and it is with the derive_filter? boolean flag. Head to tunez/music/artist.ex and add this line.
json_api do
type("artist")
includes([:albums])
derive_filter?(false)
end
Building a GraphQL interface
This is an other way to do the same things but this will do a few different things. We will go over those in the next section.
Setup
Using a similar igniter install we will be doing some different things:
Code formatting and configuration for AshGraphql and Absinthe
New graphql pipeline and scope for your Phoenix router, taking /gql and /gqp/playground
New TunezWeb.GraphqlSchema
New TunezWe.GraphqlSocket
You can head to http://localhost:4000/gql/playground after we get it all setup.
mix igniter.install ash_graphql
This next bit will go fast as there is a lot that we have done that will look very similar
Adding Artists to the API
mix ash.extend Tunez.Music.Artist graphql
We will now need to add in the queries and actions for the new API route. Head to lib/tunez/music.ex and add this new block.
graphql do
queries do
get Tunez.Music.Artist, :get_artist_by_id, :read
list Tunez.Music.Artist, :search_artists, :search
end
end
This will create queries named getArtistById and searchArtistById that connects to the read action. We can now now add some non read options that will all be mutations, same file
graphql do
# ...
mutations do
create Tunez.Music.Artist, :create_artist, :create
update Tunez.Music.Artist, :update_artist, :update
delete Tunez.Music.Artist, :destroy_artist, :destroy
end
end
"What Data Gets Included in API Responses?"
We already did this in the previous section so this will not change anything
"Creating Artist Records"
Same as before this will not change.
Adding Albums to the API
We will use the same ash.extend but for graphql and albums then we will need to set the mutations for the graphql
mix ash.extend Tunez.Music.Album graphql
Then head to lib/tunez/music.ex
graphql do
mutations do
# ...
create Tunez.Music.Album, :create_album, :create
update Tunez.Music.Album, :update_album, :update
destroy Tunez.Music.Album, :destroy_album, :destroy
end
end
"Showing Albums for a Given Artist"
Same as we did for the RESTful
Customizing the Generated API
Same as we did before we want to remove the unwanted fields it will be very similar but I will go over the needs for the graphql part
"Removing Unwanted Extras"
Here is where we will set the fields that will allows us to filter results, head to lib/tunez/music/artist.ex make these changes.
graphql do
type :artist
filterable_fields [:album_count, :cover_image_url, :inserted_at, :latest_album_year_released, :updated_at]
end
That is it for this section hope you learned something.